growthbook 1.4.5__py2.py3-none-any.whl → 1.4.6__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- growthbook/__init__.py +1 -1
- growthbook/common_types.py +1 -1
- growthbook/growthbook.py +29 -15
- growthbook/growthbook_client.py +4 -4
- {growthbook-1.4.5.dist-info → growthbook-1.4.6.dist-info}/METADATA +1 -2
- growthbook-1.4.6.dist-info/RECORD +15 -0
- growthbook-1.4.5.dist-info/RECORD +0 -15
- {growthbook-1.4.5.dist-info → growthbook-1.4.6.dist-info}/WHEEL +0 -0
- {growthbook-1.4.5.dist-info → growthbook-1.4.6.dist-info}/licenses/LICENSE +0 -0
- {growthbook-1.4.5.dist-info → growthbook-1.4.6.dist-info}/top_level.txt +0 -0
growthbook/__init__.py
CHANGED
growthbook/common_types.py
CHANGED
|
@@ -426,7 +426,7 @@ class Options:
|
|
|
426
426
|
sticky_bucket_service: Optional[AbstractStickyBucketService] = None
|
|
427
427
|
sticky_bucket_identifier_attributes: Optional[List[str]] = None
|
|
428
428
|
on_experiment_viewed: Optional[Callable[[Experiment, Result, Optional[UserContext]], None]] = None
|
|
429
|
-
on_feature_usage: Optional[Callable[[str, 'FeatureResult'], None]] = None
|
|
429
|
+
on_feature_usage: Optional[Callable[[str, 'FeatureResult', UserContext], None]] = None
|
|
430
430
|
tracking_plugins: Optional[List[Any]] = None
|
|
431
431
|
|
|
432
432
|
|
growthbook/growthbook.py
CHANGED
|
@@ -503,6 +503,10 @@ class FeatureRepository(object):
|
|
|
503
503
|
|
|
504
504
|
def start_background_refresh(self, api_host: str, client_key: str, decryption_key: str, ttl: int = 600, refresh_interval: int = 300) -> None:
|
|
505
505
|
"""Start periodic background refresh task"""
|
|
506
|
+
|
|
507
|
+
if not client_key:
|
|
508
|
+
raise ValueError("Must specify `client_key` to refresh features")
|
|
509
|
+
|
|
506
510
|
with self._refresh_lock:
|
|
507
511
|
if self._refresh_thread is not None:
|
|
508
512
|
return # Already running
|
|
@@ -620,6 +624,7 @@ class GrowthBook(object):
|
|
|
620
624
|
self._tracked: Dict[str, Any] = {}
|
|
621
625
|
self._assigned: Dict[str, Any] = {}
|
|
622
626
|
self._subscriptions: Set[Any] = set()
|
|
627
|
+
self._is_updating_features = False
|
|
623
628
|
|
|
624
629
|
# support plugins
|
|
625
630
|
self._plugins: List = plugins or []
|
|
@@ -662,7 +667,7 @@ class GrowthBook(object):
|
|
|
662
667
|
if self._streaming:
|
|
663
668
|
self.load_features()
|
|
664
669
|
self.startAutoRefresh()
|
|
665
|
-
elif self._stale_while_revalidate
|
|
670
|
+
elif self._stale_while_revalidate:
|
|
666
671
|
# Start background refresh task for stale-while-revalidate
|
|
667
672
|
self.load_features() # Initial load
|
|
668
673
|
feature_repo.start_background_refresh(
|
|
@@ -752,19 +757,24 @@ class GrowthBook(object):
|
|
|
752
757
|
return self.set_features(features)
|
|
753
758
|
|
|
754
759
|
def set_features(self, features: dict) -> None:
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
760
|
+
# Prevent infinite recursion during feature updates
|
|
761
|
+
self._is_updating_features = True
|
|
762
|
+
try:
|
|
763
|
+
self._features = {}
|
|
764
|
+
for key, feature in features.items():
|
|
765
|
+
if isinstance(feature, Feature):
|
|
766
|
+
self._features[key] = feature
|
|
767
|
+
else:
|
|
768
|
+
self._features[key] = Feature(
|
|
769
|
+
rules=feature.get("rules", []),
|
|
770
|
+
defaultValue=feature.get("defaultValue", None),
|
|
771
|
+
)
|
|
772
|
+
# Update the global context with the new features and saved groups
|
|
773
|
+
self._global_ctx.features = self._features
|
|
774
|
+
self._global_ctx.saved_groups = self._saved_groups
|
|
775
|
+
self.refresh_sticky_buckets()
|
|
776
|
+
finally:
|
|
777
|
+
self._is_updating_features = False
|
|
768
778
|
|
|
769
779
|
# @deprecated, use get_features
|
|
770
780
|
def getFeatures(self) -> Dict[str, Feature]:
|
|
@@ -864,6 +874,10 @@ class GrowthBook(object):
|
|
|
864
874
|
def _ensure_fresh_features(self) -> None:
|
|
865
875
|
"""Lazy refresh: Check cache expiry and refresh if needed, but only if client_key is provided"""
|
|
866
876
|
|
|
877
|
+
# Prevent infinite recursion when updating features (e.g., during sticky bucket refresh)
|
|
878
|
+
if self._is_updating_features:
|
|
879
|
+
return
|
|
880
|
+
|
|
867
881
|
if self._streaming or self._stale_while_revalidate or not self._client_key:
|
|
868
882
|
return # Skip cache checks - SSE or background refresh handles freshness
|
|
869
883
|
|
|
@@ -897,7 +911,7 @@ class GrowthBook(object):
|
|
|
897
911
|
# Call feature usage callback if provided
|
|
898
912
|
if self._featureUsageCallback:
|
|
899
913
|
try:
|
|
900
|
-
self._featureUsageCallback(key, result)
|
|
914
|
+
self._featureUsageCallback(key, result, self._user_ctx)
|
|
901
915
|
except Exception:
|
|
902
916
|
pass
|
|
903
917
|
return result
|
growthbook/growthbook_client.py
CHANGED
|
@@ -503,7 +503,7 @@ class GrowthBookClient:
|
|
|
503
503
|
# Call feature usage callback if provided
|
|
504
504
|
if self.options.on_feature_usage:
|
|
505
505
|
try:
|
|
506
|
-
self.options.on_feature_usage(key, result)
|
|
506
|
+
self.options.on_feature_usage(key, result, user_context)
|
|
507
507
|
except Exception:
|
|
508
508
|
logger.exception("Error in feature usage callback")
|
|
509
509
|
return result
|
|
@@ -516,7 +516,7 @@ class GrowthBookClient:
|
|
|
516
516
|
# Call feature usage callback if provided
|
|
517
517
|
if self.options.on_feature_usage:
|
|
518
518
|
try:
|
|
519
|
-
self.options.on_feature_usage(key, result)
|
|
519
|
+
self.options.on_feature_usage(key, result, user_context)
|
|
520
520
|
except Exception:
|
|
521
521
|
logger.exception("Error in feature usage callback")
|
|
522
522
|
return result.on
|
|
@@ -529,7 +529,7 @@ class GrowthBookClient:
|
|
|
529
529
|
# Call feature usage callback if provided
|
|
530
530
|
if self.options.on_feature_usage:
|
|
531
531
|
try:
|
|
532
|
-
self.options.on_feature_usage(key, result)
|
|
532
|
+
self.options.on_feature_usage(key, result, user_context)
|
|
533
533
|
except Exception:
|
|
534
534
|
logger.exception("Error in feature usage callback")
|
|
535
535
|
return result.off
|
|
@@ -541,7 +541,7 @@ class GrowthBookClient:
|
|
|
541
541
|
# Call feature usage callback if provided
|
|
542
542
|
if self.options.on_feature_usage:
|
|
543
543
|
try:
|
|
544
|
-
self.options.on_feature_usage(key, result)
|
|
544
|
+
self.options.on_feature_usage(key, result, user_context)
|
|
545
545
|
except Exception:
|
|
546
546
|
logger.exception("Error in feature usage callback")
|
|
547
547
|
return result.value if result.value is not None else fallback
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: growthbook
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.6
|
|
4
4
|
Summary: Powerful Feature flagging and A/B testing for Python apps
|
|
5
5
|
Home-page: https://github.com/growthbook/growthbook-python
|
|
6
6
|
Author: GrowthBook
|
|
@@ -499,7 +499,6 @@ class MyStickyBucketService(AbstractStickyBucketService):
|
|
|
499
499
|
})
|
|
500
500
|
|
|
501
501
|
# Pass in an instance of this service to your GrowthBook constructor
|
|
502
|
-
|
|
503
502
|
gb = GrowthBook(
|
|
504
503
|
sticky_bucket_service = MyStickyBucketService()
|
|
505
504
|
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
growthbook/__init__.py,sha256=1B2U7uOmsNvvEQ3DQ2hLuvALy6pqnJAduUUIXFbFbso,444
|
|
2
|
+
growthbook/common_types.py,sha256=OMfssahxLjvCGuGsWa75G6JMmu5xH1hVrK0pCL1ArMU,14972
|
|
3
|
+
growthbook/core.py,sha256=n9nwna26iZTY48LIvQqu5N_RrE35X0wlRBhq0-Qdb-s,35241
|
|
4
|
+
growthbook/growthbook.py,sha256=8U6UmxHAdLDCsUi3CBFTvK4nBchs9-ukXwd_bZAiXsY,40333
|
|
5
|
+
growthbook/growthbook_client.py,sha256=igD6T9MP9rfYzS1Dk0wBJ3fgfNzJxDQZ_bm5SS8XO7I,24632
|
|
6
|
+
growthbook/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
growthbook/plugins/__init__.py,sha256=y2eAV1sA041XWcftBVTDH0t-ggy9r2C5oKRYRF6XR6s,602
|
|
8
|
+
growthbook/plugins/base.py,sha256=PWBXUBj62hi25Y5Eif9WmEWagWdkwGXHi2dMtn44bo8,3637
|
|
9
|
+
growthbook/plugins/growthbook_tracking.py,sha256=FvPFOuKF_xKjmTX8x_hzMlHrrL-68Y2ZPw1Hfl2_ilQ,11333
|
|
10
|
+
growthbook/plugins/request_context.py,sha256=O5FJDrjJR5u0rx3ENGO9cOsKMHd9e0l0Nvdb1PHfmm8,12951
|
|
11
|
+
growthbook-1.4.6.dist-info/licenses/LICENSE,sha256=D-TcBckB0dTPUlNJ8jBiTIJIj1ekHLB1CY7HJtJKhMY,1069
|
|
12
|
+
growthbook-1.4.6.dist-info/METADATA,sha256=q04J95e-Kgf9yssVQCfG_JJZHGHWsTDndDwZm1c74eg,22073
|
|
13
|
+
growthbook-1.4.6.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
|
|
14
|
+
growthbook-1.4.6.dist-info/top_level.txt,sha256=dzfRQFGYejCIUstRSrrRVTMlxf7pBqASTI5S8gGRlXw,11
|
|
15
|
+
growthbook-1.4.6.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
growthbook/__init__.py,sha256=JLdue4592q3iH1ck1qG9Fbv-p8i07SJo-tUJhGCf60s,444
|
|
2
|
-
growthbook/common_types.py,sha256=KYA9rmWRMde2JnsUjygsiaJ1q-KZakDdzPAtUOnrKyY,14959
|
|
3
|
-
growthbook/core.py,sha256=n9nwna26iZTY48LIvQqu5N_RrE35X0wlRBhq0-Qdb-s,35241
|
|
4
|
-
growthbook/growthbook.py,sha256=xjuX-q8oPsLqP0kWBZqy7LpoTkrgjMZCOWjtpUsQR1o,39793
|
|
5
|
-
growthbook/growthbook_client.py,sha256=kfdc2NGhdmsXxaVIm0CBlNCMkwicfPpbDZ10nZSPd7w,24576
|
|
6
|
-
growthbook/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
growthbook/plugins/__init__.py,sha256=y2eAV1sA041XWcftBVTDH0t-ggy9r2C5oKRYRF6XR6s,602
|
|
8
|
-
growthbook/plugins/base.py,sha256=PWBXUBj62hi25Y5Eif9WmEWagWdkwGXHi2dMtn44bo8,3637
|
|
9
|
-
growthbook/plugins/growthbook_tracking.py,sha256=FvPFOuKF_xKjmTX8x_hzMlHrrL-68Y2ZPw1Hfl2_ilQ,11333
|
|
10
|
-
growthbook/plugins/request_context.py,sha256=O5FJDrjJR5u0rx3ENGO9cOsKMHd9e0l0Nvdb1PHfmm8,12951
|
|
11
|
-
growthbook-1.4.5.dist-info/licenses/LICENSE,sha256=D-TcBckB0dTPUlNJ8jBiTIJIj1ekHLB1CY7HJtJKhMY,1069
|
|
12
|
-
growthbook-1.4.5.dist-info/METADATA,sha256=8KBWZ908qYYku0GqZCeZ1KNS5YLPxs7FXZVl6wesLWM,22074
|
|
13
|
-
growthbook-1.4.5.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
|
|
14
|
-
growthbook-1.4.5.dist-info/top_level.txt,sha256=dzfRQFGYejCIUstRSrrRVTMlxf7pBqASTI5S8gGRlXw,11
|
|
15
|
-
growthbook-1.4.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|