gofeatureflag-python-provider 0.1.0__tar.gz → 0.1.2__tar.gz
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.
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/PKG-INFO +8 -8
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/README.md +6 -6
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/data_collector_hook.py +10 -6
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/provider.py +30 -25
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/pyproject.toml +5 -5
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/__init__.py +0 -0
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/metadata.py +0 -0
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/options.py +0 -0
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/provider_status.py +0 -0
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/request_data_collector.py +0 -0
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/request_flag_evaluation.py +0 -0
- {gofeatureflag_python_provider-0.1.0 → gofeatureflag_python_provider-0.1.2}/gofeatureflag_python_provider/response_flag_evaluation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: gofeatureflag-python-provider
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: GO Feature Flag provider for OpenFeature
|
|
5
5
|
Home-page: https://gofeatureflag.org
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
18
|
Requires-Dist: asyncio (>=3.4.3,<4.0.0)
|
|
19
|
-
Requires-Dist: openfeature-sdk (>=0.
|
|
19
|
+
Requires-Dist: openfeature-sdk (>=0.5.0,<0.6.0)
|
|
20
20
|
Requires-Dist: pydantic (>=2.5.2,<3.0.0)
|
|
21
21
|
Requires-Dist: pylru (>=1.2.1,<2.0.0)
|
|
22
22
|
Requires-Dist: rel (>=0.4.9,<0.5.0)
|
|
@@ -58,27 +58,27 @@ from gofeatureflag_python_provider.options import GoFeatureFlagOptions
|
|
|
58
58
|
from openfeature import api
|
|
59
59
|
from openfeature.evaluation_context import EvaluationContext
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
# ...
|
|
62
62
|
|
|
63
63
|
goff_provider = GoFeatureFlagProvider(
|
|
64
64
|
options=GoFeatureFlagOptions(endpoint="https://gofeatureflag.org/")
|
|
65
65
|
)
|
|
66
66
|
api.set_provider(goff_provider)
|
|
67
|
-
client = api.get_client(
|
|
67
|
+
client = api.get_client(domain="test-client")
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
## Evaluate your flag
|
|
71
71
|
|
|
72
|
-
This code block
|
|
72
|
+
This code block explains how you can create an `EvaluationContext` and use it to evaluate your flag.
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
> In this example we are evaluating a `boolean` flag, but other types are available.
|
|
76
76
|
>
|
|
77
77
|
> **Refer to the [Open Feature documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api#basic-evaluation) to know more about it.**
|
|
78
78
|
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
```python
|
|
80
|
+
# Context of your flag evaluation.
|
|
81
|
+
# With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user.
|
|
82
82
|
evaluation_ctx = EvaluationContext(
|
|
83
83
|
targeting_key="d45e303a-38c2-11ed-a261-0242ac120002",
|
|
84
84
|
attributes={
|
|
@@ -28,27 +28,27 @@ from gofeatureflag_python_provider.options import GoFeatureFlagOptions
|
|
|
28
28
|
from openfeature import api
|
|
29
29
|
from openfeature.evaluation_context import EvaluationContext
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
# ...
|
|
32
32
|
|
|
33
33
|
goff_provider = GoFeatureFlagProvider(
|
|
34
34
|
options=GoFeatureFlagOptions(endpoint="https://gofeatureflag.org/")
|
|
35
35
|
)
|
|
36
36
|
api.set_provider(goff_provider)
|
|
37
|
-
client = api.get_client(
|
|
37
|
+
client = api.get_client(domain="test-client")
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Evaluate your flag
|
|
41
41
|
|
|
42
|
-
This code block
|
|
42
|
+
This code block explains how you can create an `EvaluationContext` and use it to evaluate your flag.
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
> In this example we are evaluating a `boolean` flag, but other types are available.
|
|
46
46
|
>
|
|
47
47
|
> **Refer to the [Open Feature documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api#basic-evaluation) to know more about it.**
|
|
48
48
|
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
```python
|
|
50
|
+
# Context of your flag evaluation.
|
|
51
|
+
# With GO Feature Flag you MUST have a targetingKey that is a unique identifier of the user.
|
|
52
52
|
evaluation_ctx = EvaluationContext(
|
|
53
53
|
targeting_key="d45e303a-38c2-11ed-a261-0242ac120002",
|
|
54
54
|
attributes={
|
|
@@ -47,9 +47,11 @@ class DataCollectorHook(Hook):
|
|
|
47
47
|
# we don't collect data if the data collection is disabled or if the flag is not cached
|
|
48
48
|
return
|
|
49
49
|
feature_event = FeatureEvent(
|
|
50
|
-
contextKind=
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
contextKind=(
|
|
51
|
+
"anonymousUser"
|
|
52
|
+
if hook_context.evaluation_context.attributes["anonymous"]
|
|
53
|
+
else "user"
|
|
54
|
+
),
|
|
53
55
|
creationDate=int(datetime.datetime.now().timestamp()),
|
|
54
56
|
default=False,
|
|
55
57
|
key=hook_context.flag_key,
|
|
@@ -66,9 +68,11 @@ class DataCollectorHook(Hook):
|
|
|
66
68
|
return
|
|
67
69
|
|
|
68
70
|
feature_event = FeatureEvent(
|
|
69
|
-
contextKind=
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
contextKind=(
|
|
72
|
+
"anonymousUser"
|
|
73
|
+
if hook_context.evaluation_context.attributes["anonymous"]
|
|
74
|
+
else "user"
|
|
75
|
+
),
|
|
72
76
|
creationDate=int(datetime.datetime.now().timestamp()),
|
|
73
77
|
default=True,
|
|
74
78
|
key=hook_context.flag_key,
|
|
@@ -8,36 +8,41 @@ import pylru
|
|
|
8
8
|
import urllib3
|
|
9
9
|
import websocket
|
|
10
10
|
from openfeature.evaluation_context import EvaluationContext
|
|
11
|
-
from openfeature.exception import ErrorCode
|
|
12
11
|
from openfeature.exception import (
|
|
12
|
+
ErrorCode,
|
|
13
13
|
FlagNotFoundError,
|
|
14
|
-
TypeMismatchError,
|
|
15
14
|
GeneralError,
|
|
16
15
|
OpenFeatureError,
|
|
16
|
+
TypeMismatchError,
|
|
17
17
|
)
|
|
18
|
-
from openfeature.flag_evaluation import
|
|
19
|
-
from openfeature.flag_evaluation import Reason
|
|
18
|
+
from openfeature.flag_evaluation import FlagResolutionDetails, Reason
|
|
20
19
|
from openfeature.hook import Hook
|
|
21
20
|
from openfeature.provider.metadata import Metadata
|
|
22
21
|
from openfeature.provider.provider import AbstractProvider
|
|
23
|
-
from pydantic import
|
|
22
|
+
from pydantic import PrivateAttr, ValidationError
|
|
24
23
|
|
|
25
24
|
from gofeatureflag_python_provider.data_collector_hook import DataCollectorHook
|
|
26
25
|
from gofeatureflag_python_provider.metadata import GoFeatureFlagMetadata
|
|
27
|
-
from gofeatureflag_python_provider.options import BaseModel
|
|
28
|
-
from gofeatureflag_python_provider.options import GoFeatureFlagOptions
|
|
26
|
+
from gofeatureflag_python_provider.options import BaseModel, GoFeatureFlagOptions
|
|
29
27
|
from gofeatureflag_python_provider.provider_status import ProviderStatus
|
|
30
28
|
from gofeatureflag_python_provider.request_flag_evaluation import (
|
|
31
29
|
RequestFlagEvaluation,
|
|
32
30
|
convert_evaluation_context,
|
|
33
31
|
)
|
|
34
32
|
from gofeatureflag_python_provider.response_flag_evaluation import (
|
|
35
|
-
ResponseFlagEvaluation,
|
|
36
33
|
JsonType,
|
|
34
|
+
ResponseFlagEvaluation,
|
|
37
35
|
)
|
|
38
36
|
|
|
37
|
+
AbstractProviderMetaclass = type(AbstractProvider)
|
|
38
|
+
BaseModelMetaclass = type(BaseModel)
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
class CombinedMetaclass(AbstractProviderMetaclass, BaseModelMetaclass):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class GoFeatureFlagProvider(BaseModel, AbstractProvider, metaclass=CombinedMetaclass):
|
|
41
46
|
options: GoFeatureFlagOptions
|
|
42
47
|
_http_client: urllib3.PoolManager = PrivateAttr()
|
|
43
48
|
_cache: pylru.lrucache = PrivateAttr()
|
|
@@ -124,7 +129,7 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
124
129
|
flag_key: str,
|
|
125
130
|
default_value: bool,
|
|
126
131
|
evaluation_context: Optional[EvaluationContext] = None,
|
|
127
|
-
) ->
|
|
132
|
+
) -> FlagResolutionDetails[bool]:
|
|
128
133
|
return self.generic_go_feature_flag_resolver(
|
|
129
134
|
bool, flag_key, default_value, evaluation_context
|
|
130
135
|
)
|
|
@@ -134,7 +139,7 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
134
139
|
flag_key: str,
|
|
135
140
|
default_value: str,
|
|
136
141
|
evaluation_context: Optional[EvaluationContext] = None,
|
|
137
|
-
) ->
|
|
142
|
+
) -> FlagResolutionDetails[str]:
|
|
138
143
|
return self.generic_go_feature_flag_resolver(
|
|
139
144
|
str, flag_key, default_value, evaluation_context
|
|
140
145
|
)
|
|
@@ -144,7 +149,7 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
144
149
|
flag_key: str,
|
|
145
150
|
default_value: int,
|
|
146
151
|
evaluation_context: Optional[EvaluationContext] = None,
|
|
147
|
-
) ->
|
|
152
|
+
) -> FlagResolutionDetails[int]:
|
|
148
153
|
return self.generic_go_feature_flag_resolver(
|
|
149
154
|
int, flag_key, default_value, evaluation_context
|
|
150
155
|
)
|
|
@@ -154,7 +159,7 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
154
159
|
flag_key: str,
|
|
155
160
|
default_value: float,
|
|
156
161
|
evaluation_context: Optional[EvaluationContext] = None,
|
|
157
|
-
) ->
|
|
162
|
+
) -> FlagResolutionDetails[float]:
|
|
158
163
|
return self.generic_go_feature_flag_resolver(
|
|
159
164
|
float, flag_key, default_value, evaluation_context
|
|
160
165
|
)
|
|
@@ -164,7 +169,7 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
164
169
|
flag_key: str,
|
|
165
170
|
default_value: dict,
|
|
166
171
|
evaluation_context: Optional[EvaluationContext] = None,
|
|
167
|
-
) ->
|
|
172
|
+
) -> FlagResolutionDetails[Union[list, dict]]:
|
|
168
173
|
return self.generic_go_feature_flag_resolver(
|
|
169
174
|
Union[dict, list], flag_key, default_value, evaluation_context
|
|
170
175
|
)
|
|
@@ -175,7 +180,7 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
175
180
|
flag_key: str,
|
|
176
181
|
default_value: JsonType,
|
|
177
182
|
evaluation_context: Optional[EvaluationContext] = None,
|
|
178
|
-
) ->
|
|
183
|
+
) -> FlagResolutionDetails[JsonType]:
|
|
179
184
|
"""
|
|
180
185
|
generic_go_feature_flag_resolver is a generic evaluations of your flag with GO Feature Flag relay proxy it works
|
|
181
186
|
with all types.
|
|
@@ -184,12 +189,11 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
184
189
|
:param flag_key: name of the flag
|
|
185
190
|
:param default_value: default value of the flag
|
|
186
191
|
:param evaluation_context: context to evaluate the flag
|
|
187
|
-
:return: a
|
|
192
|
+
:return: a FlagResolutionDetails object containing the response for the SDK.
|
|
188
193
|
"""
|
|
189
194
|
try:
|
|
190
195
|
if self._status != ProviderStatus.READY:
|
|
191
|
-
return
|
|
192
|
-
flag_key=flag_key,
|
|
196
|
+
return FlagResolutionDetails[original_type](
|
|
193
197
|
value=default_value,
|
|
194
198
|
reason=Reason.ERROR,
|
|
195
199
|
error_code=ErrorCode.PROVIDER_NOT_READY,
|
|
@@ -245,8 +249,7 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
245
249
|
)
|
|
246
250
|
|
|
247
251
|
if response_flag_evaluation.reason == Reason.DISABLED.value:
|
|
248
|
-
return
|
|
249
|
-
flag_key=flag_key,
|
|
252
|
+
return FlagResolutionDetails[original_type](
|
|
250
253
|
value=default_value,
|
|
251
254
|
reason=Reason.DISABLED,
|
|
252
255
|
)
|
|
@@ -256,13 +259,12 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
256
259
|
"flag {} was not found in your configuration".format(flag_key)
|
|
257
260
|
)
|
|
258
261
|
|
|
259
|
-
return
|
|
260
|
-
flag_key=flag_key,
|
|
262
|
+
return FlagResolutionDetails[original_type](
|
|
261
263
|
value=response_flag_evaluation.value,
|
|
262
264
|
variant=response_flag_evaluation.variationType,
|
|
263
|
-
reason=
|
|
264
|
-
|
|
265
|
-
|
|
265
|
+
reason=(
|
|
266
|
+
Reason.CACHED if is_from_cache else response_flag_evaluation.reason
|
|
267
|
+
),
|
|
266
268
|
flag_metadata=response_flag_evaluation.metadata,
|
|
267
269
|
)
|
|
268
270
|
except ValidationError as exc:
|
|
@@ -334,3 +336,6 @@ class GoFeatureFlagProvider(AbstractProvider, BaseModel):
|
|
|
334
336
|
:return: None
|
|
335
337
|
"""
|
|
336
338
|
self._status = ProviderStatus.STALE
|
|
339
|
+
|
|
340
|
+
def __hash__(self):
|
|
341
|
+
return id(self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "gofeatureflag-python-provider"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "GO Feature Flag provider for OpenFeature"
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
authors = [ "Thomas Poignant" ]
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
|
|
29
29
|
[tool.poetry.dependencies]
|
|
30
30
|
python = "^3.9"
|
|
31
|
-
openfeature-sdk = "
|
|
31
|
+
openfeature-sdk = ">=0.5.0,<0.6.0"
|
|
32
32
|
pydantic = "^2.5.2"
|
|
33
33
|
urllib3 = "^2.1.0"
|
|
34
34
|
pylru = "^1.2.1"
|
|
@@ -37,11 +37,11 @@ classifiers = [
|
|
|
37
37
|
asyncio = "^3.4.3"
|
|
38
38
|
|
|
39
39
|
[tool.poetry.group.dev.dependencies]
|
|
40
|
-
pytest = "
|
|
41
|
-
black = "
|
|
40
|
+
pytest = ">=7.4,<9.0"
|
|
41
|
+
black = ">=23.10.1,<25.0.0"
|
|
42
42
|
requests = "^2.31.0"
|
|
43
43
|
pyyaml = "^6.0.1"
|
|
44
|
-
pytest-docker = "
|
|
44
|
+
pytest-docker = ">=2.0.1,<4.0.0"
|
|
45
45
|
|
|
46
46
|
[build-system]
|
|
47
47
|
requires = [ "poetry-core>=1.0.0" ]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|