featurevisor 2.0.0__tar.gz → 3.0.0__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.
- {featurevisor-2.0.0/src/featurevisor.egg-info → featurevisor-3.0.0}/PKG-INFO +77 -26
- {featurevisor-2.0.0 → featurevisor-3.0.0}/README.md +76 -25
- featurevisor-3.0.0/conformance/sdk-v3.json +876 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/pyproject.toml +1 -1
- featurevisor-3.0.0/src/featurevisor/child.py +151 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/cli.py +2 -2
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/evaluate.py +63 -28
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/evaluation_data_provider.py +13 -1
- featurevisor-3.0.0/src/featurevisor/events.py +150 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/instance.py +163 -38
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/modules.py +26 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/openfeature.py +21 -5
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/tester.py +68 -12
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/types.py +30 -1
- {featurevisor-2.0.0 → featurevisor-3.0.0/src/featurevisor.egg-info}/PKG-INFO +77 -26
- featurevisor-3.0.0/tests/test_conformance.py +210 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/tests/test_instance_parity.py +4 -4
- {featurevisor-2.0.0 → featurevisor-3.0.0}/tests/test_sdk.py +11 -11
- {featurevisor-2.0.0 → featurevisor-3.0.0}/tests_openfeature/test_provider.py +35 -0
- featurevisor-2.0.0/conformance/sdk-v3.json +0 -218
- featurevisor-2.0.0/src/featurevisor/child.py +0 -119
- featurevisor-2.0.0/src/featurevisor/events.py +0 -40
- featurevisor-2.0.0/tests/test_conformance.py +0 -87
- {featurevisor-2.0.0 → featurevisor-3.0.0}/LICENSE +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/MANIFEST.in +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/setup.cfg +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/__init__.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/__main__.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/bucketer.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/compare_versions.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/conditions.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/diagnostics.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/emitter.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/helpers.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/murmurhash.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/project.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor/py.typed +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor.egg-info/SOURCES.txt +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor.egg-info/dependency_links.txt +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor.egg-info/entry_points.txt +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor.egg-info/requires.txt +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/src/featurevisor.egg-info/top_level.txt +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/tests/test_cli.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/tests/test_conditions_parity.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/tests/test_evaluation_data_provider.py +0 -0
- {featurevisor-2.0.0 → featurevisor-3.0.0}/tests/test_helpers_parity.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: featurevisor
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Featurevisor Python SDK
|
|
5
5
|
Author: Fahad Heylaal
|
|
6
6
|
License-Expression: MIT
|
|
@@ -50,10 +50,11 @@ This SDK is compatible with Featurevisor v3 projects and v2 datafiles.
|
|
|
50
50
|
- [Manually passing context](#manually-passing-context)
|
|
51
51
|
- [Check if enabled](#check-if-enabled)
|
|
52
52
|
- [Getting variation](#getting-variation)
|
|
53
|
-
- [Getting variables](#getting-variables)
|
|
54
|
-
|
|
55
|
-
- [
|
|
56
|
-
- [
|
|
53
|
+
- [Getting feature variables](#getting-feature-variables)
|
|
54
|
+
- [Getting global variables](#getting-global-variables)
|
|
55
|
+
- [Type specific methods](#type-specific-methods)
|
|
56
|
+
- [Getting evaluation snapshots](#getting-evaluation-snapshots)
|
|
57
|
+
- [Sticky features and variables](#sticky-features-and-variables)
|
|
57
58
|
- [Initialize with sticky](#initialize-with-sticky)
|
|
58
59
|
- [Set sticky afterwards](#set-sticky-afterwards)
|
|
59
60
|
- [Setting datafile](#setting-datafile)
|
|
@@ -68,7 +69,7 @@ This SDK is compatible with Featurevisor v3 projects and v2 datafiles.
|
|
|
68
69
|
- [Events](#events)
|
|
69
70
|
- [`datafile_set`](#datafile_set)
|
|
70
71
|
- [`context_set`](#context_set)
|
|
71
|
-
- [`
|
|
72
|
+
- [`sticky_features_set` and `sticky_variables_set`](#sticky_features_set-and-sticky_variables_set)
|
|
72
73
|
- [`error`](#error)
|
|
73
74
|
- [Modules](#modules)
|
|
74
75
|
- [Defining a module](#defining-a-module)
|
|
@@ -113,7 +114,7 @@ f: Featurevisor = create_featurevisor({
|
|
|
113
114
|
|
|
114
115
|
Most applications only need `create_featurevisor` and the `Featurevisor` instance type. Public extension and observability APIs include `FeaturevisorModule`, diagnostics, events, and the datafile dictionaries accepted by the factory.
|
|
115
116
|
|
|
116
|
-
Concurrent evaluations are safe after an instance is configured. Do not mutate or close the same instance concurrently with evaluations. Serialize calls to `set_datafile`, `set_context`, `
|
|
117
|
+
Concurrent evaluations are safe after an instance is configured. Do not mutate or close the same instance concurrently with evaluations. Serialize calls to `set_datafile`, `set_context`, `set_sticky_features`, `set_sticky_variables`, `add_module`, `remove_module`, and `close`. Module, event, and diagnostic callbacks must synchronize mutable state that they capture.
|
|
117
118
|
|
|
118
119
|
## Initialization
|
|
119
120
|
|
|
@@ -137,11 +138,12 @@ f = create_featurevisor({
|
|
|
137
138
|
|
|
138
139
|
## Evaluation types
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
Featurevisor evaluates flags, variations, feature variables, and global variables:
|
|
141
142
|
|
|
142
143
|
- [**Flag**](#check-if-enabled) (`bool`): whether the feature is enabled or not
|
|
143
144
|
- [**Variation**](#getting-variation) (`string`): the variation of the feature (if any)
|
|
144
|
-
- [**
|
|
145
|
+
- [**Feature variables**](#getting-feature-variables): variable values owned by a feature
|
|
146
|
+
- [**Global variables**](#getting-global-variables): independently evaluated project values
|
|
145
147
|
|
|
146
148
|
## Context
|
|
147
149
|
|
|
@@ -200,7 +202,8 @@ You can also pass additional per-evaluation context:
|
|
|
200
202
|
```python
|
|
201
203
|
is_enabled = f.is_enabled("my_feature", {"country": "nl"})
|
|
202
204
|
variation = f.get_variation("my_feature", {"country": "nl"})
|
|
203
|
-
|
|
205
|
+
feature_variable = f.get_variable("my_feature", "my_variable", {"country": "nl"})
|
|
206
|
+
global_variable = f.get_variable("supportEmail", {"country": "nl"})
|
|
204
207
|
```
|
|
205
208
|
|
|
206
209
|
## Check if enabled
|
|
@@ -219,13 +222,24 @@ if variation == "treatment":
|
|
|
219
222
|
pass
|
|
220
223
|
```
|
|
221
224
|
|
|
222
|
-
## Getting variables
|
|
225
|
+
## Getting feature variables
|
|
223
226
|
|
|
224
227
|
```python
|
|
225
228
|
bg_color = f.get_variable("my_feature", "bgColor")
|
|
226
229
|
```
|
|
227
230
|
|
|
228
|
-
|
|
231
|
+
## Getting global variables
|
|
232
|
+
|
|
233
|
+
Global variables use the same methods without a feature key:
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
support_email = f.get_variable("supportEmail", {"country": "nl"})
|
|
237
|
+
evaluation = f.evaluate_variable("supportEmail", {"country": "nl"})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Global variables honour sticky values, required features, and the first matching override. When an override contains both conditions and segments, both must match.
|
|
241
|
+
|
|
242
|
+
## Type specific methods
|
|
229
243
|
|
|
230
244
|
Typed convenience methods are also available:
|
|
231
245
|
|
|
@@ -239,33 +253,44 @@ f.get_variable_object(feature_key, variable_key, context={})
|
|
|
239
253
|
f.get_variable_json(feature_key, variable_key, context={})
|
|
240
254
|
```
|
|
241
255
|
|
|
256
|
+
The same helpers evaluate global variables when the feature key is omitted:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
f.get_variable_string("supportEmail", context={})
|
|
260
|
+
f.get_variable_object("checkoutSettings", context={})
|
|
261
|
+
```
|
|
262
|
+
|
|
242
263
|
Type specific methods do not coerce values. `get_variable_integer()` returns `None` for the string `"1"`, and boolean getters return `None` for non-boolean values.
|
|
243
264
|
|
|
244
|
-
## Getting
|
|
265
|
+
## Getting evaluation snapshots
|
|
245
266
|
|
|
246
267
|
```python
|
|
247
|
-
|
|
268
|
+
features = f.get_feature_evaluations()
|
|
269
|
+
variables = f.get_variable_evaluations()
|
|
248
270
|
```
|
|
249
271
|
|
|
250
|
-
## Sticky
|
|
272
|
+
## Sticky features and variables
|
|
251
273
|
|
|
252
274
|
### Initialize with sticky
|
|
253
275
|
|
|
254
276
|
You can pin feature evaluations with sticky values:
|
|
255
277
|
|
|
256
|
-
Sticky values belong to an SDK or child instance. Evaluation options do not accept sticky overrides
|
|
278
|
+
Sticky values belong to an SDK or child instance. Evaluation options do not accept sticky overrides. Pass `stickyFeatures` or `stickyVariables` when a child needs its own state.
|
|
257
279
|
|
|
258
280
|
```python
|
|
259
281
|
f = create_featurevisor({
|
|
260
|
-
"
|
|
282
|
+
"stickyFeatures": {
|
|
261
283
|
"myFeatureKey": {
|
|
262
284
|
"enabled": True,
|
|
263
285
|
"variation": "treatment",
|
|
264
286
|
"variables": {
|
|
265
287
|
"myVariableKey": "myVariableValue",
|
|
266
288
|
},
|
|
267
|
-
}
|
|
268
|
-
}
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
"stickyVariables": {
|
|
292
|
+
"supportEmail": "sticky@example.com",
|
|
293
|
+
},
|
|
269
294
|
})
|
|
270
295
|
```
|
|
271
296
|
|
|
@@ -274,11 +299,13 @@ f = create_featurevisor({
|
|
|
274
299
|
Or update them later:
|
|
275
300
|
|
|
276
301
|
```python
|
|
277
|
-
f.
|
|
302
|
+
f.set_sticky_features({
|
|
278
303
|
"myFeatureKey": {
|
|
279
304
|
"enabled": False,
|
|
280
305
|
}
|
|
281
306
|
})
|
|
307
|
+
|
|
308
|
+
f.set_sticky_variables({"supportEmail": "new@example.com"})
|
|
282
309
|
```
|
|
283
310
|
|
|
284
311
|
## Setting datafile
|
|
@@ -420,10 +447,13 @@ unsubscribe = f.on("context_set", lambda event: print(event["context"]))
|
|
|
420
447
|
unsubscribe()
|
|
421
448
|
```
|
|
422
449
|
|
|
423
|
-
### `
|
|
450
|
+
### `sticky_features_set` and `sticky_variables_set`
|
|
424
451
|
|
|
425
452
|
```python
|
|
426
|
-
unsubscribe = f.on("
|
|
453
|
+
unsubscribe = f.on("sticky_features_set", lambda event: print(event["features"]))
|
|
454
|
+
unsubscribe()
|
|
455
|
+
|
|
456
|
+
unsubscribe = f.on("sticky_variables_set", lambda event: print(event["variables"]))
|
|
427
457
|
unsubscribe()
|
|
428
458
|
```
|
|
429
459
|
|
|
@@ -468,6 +498,8 @@ And optionally these properties depending on whether you are evaluating a featur
|
|
|
468
498
|
- `variableValue`: the variable value
|
|
469
499
|
- `variableSchema`: the variable schema
|
|
470
500
|
- `variableOverrideIndex`: index of matched variable override when applicable
|
|
501
|
+
- `variableOverrideKey`: key of the matched variable override when available
|
|
502
|
+
- `variableOverridePath`: nested authoring path of the matched global variable override
|
|
471
503
|
|
|
472
504
|
## Modules
|
|
473
505
|
|
|
@@ -478,6 +510,8 @@ Modules can intercept evaluation and participate in SDK lifecycle:
|
|
|
478
510
|
- `bucketKey`
|
|
479
511
|
- `bucketValue`
|
|
480
512
|
- `after`
|
|
513
|
+
- `beforeEvaluation`
|
|
514
|
+
- `afterEvaluation`
|
|
481
515
|
- `close`
|
|
482
516
|
|
|
483
517
|
### Defining a module
|
|
@@ -487,13 +521,17 @@ my_module = {
|
|
|
487
521
|
"name": "my-module",
|
|
488
522
|
"setup": lambda api: api["onDiagnostic"](lambda diagnostic: print(diagnostic)),
|
|
489
523
|
"before": lambda options: {**options, "context": {**options["context"], "country": "nl"}},
|
|
524
|
+
"beforeEvaluation": lambda options: options,
|
|
490
525
|
"bucketKey": lambda options: options["bucketKey"],
|
|
491
526
|
"bucketValue": lambda options: options["bucketValue"],
|
|
492
527
|
"after": lambda evaluation, options: evaluation,
|
|
528
|
+
"afterEvaluation": lambda evaluation, options: evaluation,
|
|
493
529
|
"close": lambda: None,
|
|
494
530
|
}
|
|
495
531
|
```
|
|
496
532
|
|
|
533
|
+
For feature evaluations, all `before` callbacks run in registration order, followed by all `beforeEvaluation` callbacks. After evaluation and caller defaults, all `afterEvaluation` callbacks run, followed by all `after` callbacks. Global variable evaluations use only `beforeEvaluation` and `afterEvaluation`. Required feature checks run through the complete module pipeline, and transformed defaults are preserved.
|
|
534
|
+
|
|
497
535
|
The module API passed to `setup` exposes `getRevision`, `onDiagnostic`, and `reportDiagnostic`.
|
|
498
536
|
|
|
499
537
|
If `setup` raises an exception, the module is not registered. Featurevisor removes subscriptions created during setup, reports `module_setup_error`, and calls `close` when present.
|
|
@@ -524,6 +562,7 @@ child.is_enabled("my_feature")
|
|
|
524
562
|
child.evaluate_flag("my_feature")
|
|
525
563
|
child.evaluate_variation("my_feature")
|
|
526
564
|
child.evaluate_variable("my_feature", "my_variable")
|
|
565
|
+
child.evaluate_variable("supportEmail")
|
|
527
566
|
```
|
|
528
567
|
|
|
529
568
|
## Close
|
|
@@ -602,6 +641,16 @@ python -m featurevisor benchmark \
|
|
|
602
641
|
--context='{"userId":"123"}'
|
|
603
642
|
```
|
|
604
643
|
|
|
644
|
+
For a global variable, omit `--feature`:
|
|
645
|
+
|
|
646
|
+
```bash
|
|
647
|
+
python -m featurevisor benchmark \
|
|
648
|
+
--projectDirectoryPath=/path/to/featurevisor-project \
|
|
649
|
+
--environment=production \
|
|
650
|
+
--variable=supportEmail \
|
|
651
|
+
--context='{"country":"nl"}'
|
|
652
|
+
```
|
|
653
|
+
|
|
605
654
|
### Assess distribution
|
|
606
655
|
|
|
607
656
|
Inspect enabled/disabled and variation distribution over repeated evaluations:
|
|
@@ -665,6 +714,7 @@ Call `api.shutdown()` during application shutdown. This closes a Featurevisor in
|
|
|
665
714
|
| `checkout` | Boolean flag for `checkout` |
|
|
666
715
|
| `checkout:variation` | Variation value for `checkout` |
|
|
667
716
|
| `checkout:title` | Variable `title` for `checkout` |
|
|
717
|
+
| `variable:supportEmail` | Global variable `supportEmail` |
|
|
668
718
|
|
|
669
719
|
Boolean variables use the boolean resolver. Integer and double variables use their matching numeric resolvers. Arrays, objects, and JSON variables use the object resolver.
|
|
670
720
|
|
|
@@ -675,10 +725,11 @@ provider = FeaturevisorOpenFeatureProvider(
|
|
|
675
725
|
{"datafile": datafile_content},
|
|
676
726
|
key_separator="/",
|
|
677
727
|
variation_key="$variation",
|
|
728
|
+
global_variable_prefix="$variable",
|
|
678
729
|
)
|
|
679
730
|
```
|
|
680
731
|
|
|
681
|
-
This makes `checkout/$variation` the variation key
|
|
732
|
+
This makes `checkout/$variation` the variation key, `checkout/title` a feature variable key, and `$variable/supportEmail` a global variable key. The global prefix defaults to `variable` and cannot contain the separator.
|
|
682
733
|
|
|
683
734
|
### Context mapping
|
|
684
735
|
|
|
@@ -699,9 +750,9 @@ The provider maps Featurevisor evaluation results to OpenFeature details:
|
|
|
699
750
|
|
|
700
751
|
| Featurevisor result | OpenFeature result |
|
|
701
752
|
| --- | --- |
|
|
702
|
-
| Required, forced, sticky, or rule match | `TARGETING_MATCH` |
|
|
753
|
+
| Required feature rule, forced, sticky, or rule match | `TARGETING_MATCH` |
|
|
703
754
|
| Traffic allocation | `SPLIT` |
|
|
704
|
-
|
|
|
755
|
+
| Unmet global variable requirements, disabled variation, or disabled variable | `DISABLED` |
|
|
705
756
|
| No match or variable default | `DEFAULT` |
|
|
706
757
|
| Missing feature, variable, or variations | `ERROR` with `FLAG_NOT_FOUND` |
|
|
707
758
|
| Wrong resolver type | `ERROR` with `TYPE_MISMATCH` |
|
|
@@ -710,7 +761,7 @@ The provider maps Featurevisor evaluation results to OpenFeature details:
|
|
|
710
761
|
|
|
711
762
|
Errors return the default value supplied to OpenFeature. A malformed datafile uses the stable message `Could not parse datafile`. A later successful `set_datafile` call clears the parse error.
|
|
712
763
|
|
|
713
|
-
Resolution metadata can include `featureKey`, `variableKey`, `featurevisorReason`, `revision`, `schemaVersion`, `ruleKey`, `bucketKey`, `bucketValue`, `forceIndex`, and `
|
|
764
|
+
Resolution metadata can include `featureKey`, `variableKey`, `featurevisorReason`, `revision`, `schemaVersion`, `ruleKey`, `bucketKey`, `bucketValue`, `forceIndex`, `variableOverrideIndex`, and `variableOverrideKey`. The selected variation is exposed as the OpenFeature variant when available.
|
|
714
765
|
|
|
715
766
|
### Tracking
|
|
716
767
|
|
|
@@ -19,10 +19,11 @@ This SDK is compatible with Featurevisor v3 projects and v2 datafiles.
|
|
|
19
19
|
- [Manually passing context](#manually-passing-context)
|
|
20
20
|
- [Check if enabled](#check-if-enabled)
|
|
21
21
|
- [Getting variation](#getting-variation)
|
|
22
|
-
- [Getting variables](#getting-variables)
|
|
23
|
-
|
|
24
|
-
- [
|
|
25
|
-
- [
|
|
22
|
+
- [Getting feature variables](#getting-feature-variables)
|
|
23
|
+
- [Getting global variables](#getting-global-variables)
|
|
24
|
+
- [Type specific methods](#type-specific-methods)
|
|
25
|
+
- [Getting evaluation snapshots](#getting-evaluation-snapshots)
|
|
26
|
+
- [Sticky features and variables](#sticky-features-and-variables)
|
|
26
27
|
- [Initialize with sticky](#initialize-with-sticky)
|
|
27
28
|
- [Set sticky afterwards](#set-sticky-afterwards)
|
|
28
29
|
- [Setting datafile](#setting-datafile)
|
|
@@ -37,7 +38,7 @@ This SDK is compatible with Featurevisor v3 projects and v2 datafiles.
|
|
|
37
38
|
- [Events](#events)
|
|
38
39
|
- [`datafile_set`](#datafile_set)
|
|
39
40
|
- [`context_set`](#context_set)
|
|
40
|
-
- [`
|
|
41
|
+
- [`sticky_features_set` and `sticky_variables_set`](#sticky_features_set-and-sticky_variables_set)
|
|
41
42
|
- [`error`](#error)
|
|
42
43
|
- [Modules](#modules)
|
|
43
44
|
- [Defining a module](#defining-a-module)
|
|
@@ -82,7 +83,7 @@ f: Featurevisor = create_featurevisor({
|
|
|
82
83
|
|
|
83
84
|
Most applications only need `create_featurevisor` and the `Featurevisor` instance type. Public extension and observability APIs include `FeaturevisorModule`, diagnostics, events, and the datafile dictionaries accepted by the factory.
|
|
84
85
|
|
|
85
|
-
Concurrent evaluations are safe after an instance is configured. Do not mutate or close the same instance concurrently with evaluations. Serialize calls to `set_datafile`, `set_context`, `
|
|
86
|
+
Concurrent evaluations are safe after an instance is configured. Do not mutate or close the same instance concurrently with evaluations. Serialize calls to `set_datafile`, `set_context`, `set_sticky_features`, `set_sticky_variables`, `add_module`, `remove_module`, and `close`. Module, event, and diagnostic callbacks must synchronize mutable state that they capture.
|
|
86
87
|
|
|
87
88
|
## Initialization
|
|
88
89
|
|
|
@@ -106,11 +107,12 @@ f = create_featurevisor({
|
|
|
106
107
|
|
|
107
108
|
## Evaluation types
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
Featurevisor evaluates flags, variations, feature variables, and global variables:
|
|
110
111
|
|
|
111
112
|
- [**Flag**](#check-if-enabled) (`bool`): whether the feature is enabled or not
|
|
112
113
|
- [**Variation**](#getting-variation) (`string`): the variation of the feature (if any)
|
|
113
|
-
- [**
|
|
114
|
+
- [**Feature variables**](#getting-feature-variables): variable values owned by a feature
|
|
115
|
+
- [**Global variables**](#getting-global-variables): independently evaluated project values
|
|
114
116
|
|
|
115
117
|
## Context
|
|
116
118
|
|
|
@@ -169,7 +171,8 @@ You can also pass additional per-evaluation context:
|
|
|
169
171
|
```python
|
|
170
172
|
is_enabled = f.is_enabled("my_feature", {"country": "nl"})
|
|
171
173
|
variation = f.get_variation("my_feature", {"country": "nl"})
|
|
172
|
-
|
|
174
|
+
feature_variable = f.get_variable("my_feature", "my_variable", {"country": "nl"})
|
|
175
|
+
global_variable = f.get_variable("supportEmail", {"country": "nl"})
|
|
173
176
|
```
|
|
174
177
|
|
|
175
178
|
## Check if enabled
|
|
@@ -188,13 +191,24 @@ if variation == "treatment":
|
|
|
188
191
|
pass
|
|
189
192
|
```
|
|
190
193
|
|
|
191
|
-
## Getting variables
|
|
194
|
+
## Getting feature variables
|
|
192
195
|
|
|
193
196
|
```python
|
|
194
197
|
bg_color = f.get_variable("my_feature", "bgColor")
|
|
195
198
|
```
|
|
196
199
|
|
|
197
|
-
|
|
200
|
+
## Getting global variables
|
|
201
|
+
|
|
202
|
+
Global variables use the same methods without a feature key:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
support_email = f.get_variable("supportEmail", {"country": "nl"})
|
|
206
|
+
evaluation = f.evaluate_variable("supportEmail", {"country": "nl"})
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Global variables honour sticky values, required features, and the first matching override. When an override contains both conditions and segments, both must match.
|
|
210
|
+
|
|
211
|
+
## Type specific methods
|
|
198
212
|
|
|
199
213
|
Typed convenience methods are also available:
|
|
200
214
|
|
|
@@ -208,33 +222,44 @@ f.get_variable_object(feature_key, variable_key, context={})
|
|
|
208
222
|
f.get_variable_json(feature_key, variable_key, context={})
|
|
209
223
|
```
|
|
210
224
|
|
|
225
|
+
The same helpers evaluate global variables when the feature key is omitted:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
f.get_variable_string("supportEmail", context={})
|
|
229
|
+
f.get_variable_object("checkoutSettings", context={})
|
|
230
|
+
```
|
|
231
|
+
|
|
211
232
|
Type specific methods do not coerce values. `get_variable_integer()` returns `None` for the string `"1"`, and boolean getters return `None` for non-boolean values.
|
|
212
233
|
|
|
213
|
-
## Getting
|
|
234
|
+
## Getting evaluation snapshots
|
|
214
235
|
|
|
215
236
|
```python
|
|
216
|
-
|
|
237
|
+
features = f.get_feature_evaluations()
|
|
238
|
+
variables = f.get_variable_evaluations()
|
|
217
239
|
```
|
|
218
240
|
|
|
219
|
-
## Sticky
|
|
241
|
+
## Sticky features and variables
|
|
220
242
|
|
|
221
243
|
### Initialize with sticky
|
|
222
244
|
|
|
223
245
|
You can pin feature evaluations with sticky values:
|
|
224
246
|
|
|
225
|
-
Sticky values belong to an SDK or child instance. Evaluation options do not accept sticky overrides
|
|
247
|
+
Sticky values belong to an SDK or child instance. Evaluation options do not accept sticky overrides. Pass `stickyFeatures` or `stickyVariables` when a child needs its own state.
|
|
226
248
|
|
|
227
249
|
```python
|
|
228
250
|
f = create_featurevisor({
|
|
229
|
-
"
|
|
251
|
+
"stickyFeatures": {
|
|
230
252
|
"myFeatureKey": {
|
|
231
253
|
"enabled": True,
|
|
232
254
|
"variation": "treatment",
|
|
233
255
|
"variables": {
|
|
234
256
|
"myVariableKey": "myVariableValue",
|
|
235
257
|
},
|
|
236
|
-
}
|
|
237
|
-
}
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
"stickyVariables": {
|
|
261
|
+
"supportEmail": "sticky@example.com",
|
|
262
|
+
},
|
|
238
263
|
})
|
|
239
264
|
```
|
|
240
265
|
|
|
@@ -243,11 +268,13 @@ f = create_featurevisor({
|
|
|
243
268
|
Or update them later:
|
|
244
269
|
|
|
245
270
|
```python
|
|
246
|
-
f.
|
|
271
|
+
f.set_sticky_features({
|
|
247
272
|
"myFeatureKey": {
|
|
248
273
|
"enabled": False,
|
|
249
274
|
}
|
|
250
275
|
})
|
|
276
|
+
|
|
277
|
+
f.set_sticky_variables({"supportEmail": "new@example.com"})
|
|
251
278
|
```
|
|
252
279
|
|
|
253
280
|
## Setting datafile
|
|
@@ -389,10 +416,13 @@ unsubscribe = f.on("context_set", lambda event: print(event["context"]))
|
|
|
389
416
|
unsubscribe()
|
|
390
417
|
```
|
|
391
418
|
|
|
392
|
-
### `
|
|
419
|
+
### `sticky_features_set` and `sticky_variables_set`
|
|
393
420
|
|
|
394
421
|
```python
|
|
395
|
-
unsubscribe = f.on("
|
|
422
|
+
unsubscribe = f.on("sticky_features_set", lambda event: print(event["features"]))
|
|
423
|
+
unsubscribe()
|
|
424
|
+
|
|
425
|
+
unsubscribe = f.on("sticky_variables_set", lambda event: print(event["variables"]))
|
|
396
426
|
unsubscribe()
|
|
397
427
|
```
|
|
398
428
|
|
|
@@ -437,6 +467,8 @@ And optionally these properties depending on whether you are evaluating a featur
|
|
|
437
467
|
- `variableValue`: the variable value
|
|
438
468
|
- `variableSchema`: the variable schema
|
|
439
469
|
- `variableOverrideIndex`: index of matched variable override when applicable
|
|
470
|
+
- `variableOverrideKey`: key of the matched variable override when available
|
|
471
|
+
- `variableOverridePath`: nested authoring path of the matched global variable override
|
|
440
472
|
|
|
441
473
|
## Modules
|
|
442
474
|
|
|
@@ -447,6 +479,8 @@ Modules can intercept evaluation and participate in SDK lifecycle:
|
|
|
447
479
|
- `bucketKey`
|
|
448
480
|
- `bucketValue`
|
|
449
481
|
- `after`
|
|
482
|
+
- `beforeEvaluation`
|
|
483
|
+
- `afterEvaluation`
|
|
450
484
|
- `close`
|
|
451
485
|
|
|
452
486
|
### Defining a module
|
|
@@ -456,13 +490,17 @@ my_module = {
|
|
|
456
490
|
"name": "my-module",
|
|
457
491
|
"setup": lambda api: api["onDiagnostic"](lambda diagnostic: print(diagnostic)),
|
|
458
492
|
"before": lambda options: {**options, "context": {**options["context"], "country": "nl"}},
|
|
493
|
+
"beforeEvaluation": lambda options: options,
|
|
459
494
|
"bucketKey": lambda options: options["bucketKey"],
|
|
460
495
|
"bucketValue": lambda options: options["bucketValue"],
|
|
461
496
|
"after": lambda evaluation, options: evaluation,
|
|
497
|
+
"afterEvaluation": lambda evaluation, options: evaluation,
|
|
462
498
|
"close": lambda: None,
|
|
463
499
|
}
|
|
464
500
|
```
|
|
465
501
|
|
|
502
|
+
For feature evaluations, all `before` callbacks run in registration order, followed by all `beforeEvaluation` callbacks. After evaluation and caller defaults, all `afterEvaluation` callbacks run, followed by all `after` callbacks. Global variable evaluations use only `beforeEvaluation` and `afterEvaluation`. Required feature checks run through the complete module pipeline, and transformed defaults are preserved.
|
|
503
|
+
|
|
466
504
|
The module API passed to `setup` exposes `getRevision`, `onDiagnostic`, and `reportDiagnostic`.
|
|
467
505
|
|
|
468
506
|
If `setup` raises an exception, the module is not registered. Featurevisor removes subscriptions created during setup, reports `module_setup_error`, and calls `close` when present.
|
|
@@ -493,6 +531,7 @@ child.is_enabled("my_feature")
|
|
|
493
531
|
child.evaluate_flag("my_feature")
|
|
494
532
|
child.evaluate_variation("my_feature")
|
|
495
533
|
child.evaluate_variable("my_feature", "my_variable")
|
|
534
|
+
child.evaluate_variable("supportEmail")
|
|
496
535
|
```
|
|
497
536
|
|
|
498
537
|
## Close
|
|
@@ -571,6 +610,16 @@ python -m featurevisor benchmark \
|
|
|
571
610
|
--context='{"userId":"123"}'
|
|
572
611
|
```
|
|
573
612
|
|
|
613
|
+
For a global variable, omit `--feature`:
|
|
614
|
+
|
|
615
|
+
```bash
|
|
616
|
+
python -m featurevisor benchmark \
|
|
617
|
+
--projectDirectoryPath=/path/to/featurevisor-project \
|
|
618
|
+
--environment=production \
|
|
619
|
+
--variable=supportEmail \
|
|
620
|
+
--context='{"country":"nl"}'
|
|
621
|
+
```
|
|
622
|
+
|
|
574
623
|
### Assess distribution
|
|
575
624
|
|
|
576
625
|
Inspect enabled/disabled and variation distribution over repeated evaluations:
|
|
@@ -634,6 +683,7 @@ Call `api.shutdown()` during application shutdown. This closes a Featurevisor in
|
|
|
634
683
|
| `checkout` | Boolean flag for `checkout` |
|
|
635
684
|
| `checkout:variation` | Variation value for `checkout` |
|
|
636
685
|
| `checkout:title` | Variable `title` for `checkout` |
|
|
686
|
+
| `variable:supportEmail` | Global variable `supportEmail` |
|
|
637
687
|
|
|
638
688
|
Boolean variables use the boolean resolver. Integer and double variables use their matching numeric resolvers. Arrays, objects, and JSON variables use the object resolver.
|
|
639
689
|
|
|
@@ -644,10 +694,11 @@ provider = FeaturevisorOpenFeatureProvider(
|
|
|
644
694
|
{"datafile": datafile_content},
|
|
645
695
|
key_separator="/",
|
|
646
696
|
variation_key="$variation",
|
|
697
|
+
global_variable_prefix="$variable",
|
|
647
698
|
)
|
|
648
699
|
```
|
|
649
700
|
|
|
650
|
-
This makes `checkout/$variation` the variation key
|
|
701
|
+
This makes `checkout/$variation` the variation key, `checkout/title` a feature variable key, and `$variable/supportEmail` a global variable key. The global prefix defaults to `variable` and cannot contain the separator.
|
|
651
702
|
|
|
652
703
|
### Context mapping
|
|
653
704
|
|
|
@@ -668,9 +719,9 @@ The provider maps Featurevisor evaluation results to OpenFeature details:
|
|
|
668
719
|
|
|
669
720
|
| Featurevisor result | OpenFeature result |
|
|
670
721
|
| --- | --- |
|
|
671
|
-
| Required, forced, sticky, or rule match | `TARGETING_MATCH` |
|
|
722
|
+
| Required feature rule, forced, sticky, or rule match | `TARGETING_MATCH` |
|
|
672
723
|
| Traffic allocation | `SPLIT` |
|
|
673
|
-
|
|
|
724
|
+
| Unmet global variable requirements, disabled variation, or disabled variable | `DISABLED` |
|
|
674
725
|
| No match or variable default | `DEFAULT` |
|
|
675
726
|
| Missing feature, variable, or variations | `ERROR` with `FLAG_NOT_FOUND` |
|
|
676
727
|
| Wrong resolver type | `ERROR` with `TYPE_MISMATCH` |
|
|
@@ -679,7 +730,7 @@ The provider maps Featurevisor evaluation results to OpenFeature details:
|
|
|
679
730
|
|
|
680
731
|
Errors return the default value supplied to OpenFeature. A malformed datafile uses the stable message `Could not parse datafile`. A later successful `set_datafile` call clears the parse error.
|
|
681
732
|
|
|
682
|
-
Resolution metadata can include `featureKey`, `variableKey`, `featurevisorReason`, `revision`, `schemaVersion`, `ruleKey`, `bucketKey`, `bucketValue`, `forceIndex`, and `
|
|
733
|
+
Resolution metadata can include `featureKey`, `variableKey`, `featurevisorReason`, `revision`, `schemaVersion`, `ruleKey`, `bucketKey`, `bucketValue`, `forceIndex`, `variableOverrideIndex`, and `variableOverrideKey`. The selected variation is exposed as the OpenFeature variant when available.
|
|
683
734
|
|
|
684
735
|
### Tracking
|
|
685
736
|
|