switchbox-flags 0.4.0__tar.gz → 0.5.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.
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/PKG-INFO +1 -1
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/pyproject.toml +1 -1
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/switchbox/__init__.py +1 -1
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/switchbox/evaluator.py +8 -2
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/switchbox/models.py +5 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/tests/parity_vectors.json +7 -1
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/uv.lock +1 -1
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/.coverage +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/.github/workflows/publish.yml +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/.github/workflows/test.yml +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/.gitignore +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/LICENSE +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/README.md +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/switchbox/cache.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/switchbox/client.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/switchbox/exceptions.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/switchbox/sync.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/tests/__init__.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/tests/test_cache.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/tests/test_client.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/tests/test_evaluator.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/tests/test_models.py +0 -0
- {switchbox_flags-0.4.0 → switchbox_flags-0.5.0}/tests/test_parity_vectors.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: switchbox-flags
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Feature flag SDK with zero dependencies
|
|
5
5
|
Project-URL: Homepage, https://github.com/ignat14/switchbox-sdk-python
|
|
6
6
|
Project-URL: Repository, https://github.com/ignat14/switchbox-sdk-python
|
|
@@ -88,10 +88,16 @@ def evaluate(flag: Flag, user_context: dict | None = None) -> bool | str | int |
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def _enabled_value(flag: Flag) -> bool | str | int | Any:
|
|
91
|
-
"""
|
|
91
|
+
"""The value served to matched / in-rollout users.
|
|
92
|
+
|
|
93
|
+
Boolean flags serve ``True``. Non-boolean flags serve ``enabled_value``
|
|
94
|
+
(variations, ADR-017), falling back to ``default_value`` when it's unset —
|
|
95
|
+
so a config without ``enabled_value`` behaves exactly as before. Fallback is
|
|
96
|
+
at evaluation time (not parse time) so directly-constructed Flags work too,
|
|
97
|
+
matching the JS SDK's ``?? default_value``."""
|
|
92
98
|
if flag.flag_type == "boolean":
|
|
93
99
|
return True
|
|
94
|
-
return flag.default_value
|
|
100
|
+
return flag.enabled_value if flag.enabled_value is not None else flag.default_value
|
|
95
101
|
|
|
96
102
|
|
|
97
103
|
def _to_groups(rules: list) -> list[RuleGroup]:
|
|
@@ -29,6 +29,10 @@ class Flag:
|
|
|
29
29
|
rollout_pct: int
|
|
30
30
|
flag_type: str # boolean | string | number | json
|
|
31
31
|
default_value: Any
|
|
32
|
+
# The "on"/matched value for non-boolean flags (variations, ADR-017). None
|
|
33
|
+
# means "unset" — the evaluator falls back to default_value, so configs
|
|
34
|
+
# without this key behave exactly as before. Boolean flags ignore it.
|
|
35
|
+
enabled_value: Any = None
|
|
32
36
|
rules: list[RuleGroup] = field(default_factory=list)
|
|
33
37
|
|
|
34
38
|
|
|
@@ -62,6 +66,7 @@ class FlagConfig:
|
|
|
62
66
|
rollout_pct=flag_data.get("rollout_pct", 0),
|
|
63
67
|
flag_type=flag_data.get("flag_type", "boolean"),
|
|
64
68
|
default_value=flag_data.get("default_value"),
|
|
69
|
+
enabled_value=flag_data.get("enabled_value"),
|
|
65
70
|
rules=rules,
|
|
66
71
|
)
|
|
67
72
|
return cls(version=data.get("version", ""), flags=flags)
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
{"name": "DNF: second group matches (OR across groups) -> enabled", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "boolean", "default_value": false, "rules": [{"conditions": [{"attribute": "country", "operator": "equals", "value": "US"}]}, {"conditions": [{"attribute": "plan", "operator": "equals", "value": "ent"}]}]}, "user": {"plan": "ent"}, "expected": true},
|
|
35
35
|
{"name": "DNF: no group matches (OR across groups) -> default", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "boolean", "default_value": false, "rules": [{"conditions": [{"attribute": "country", "operator": "equals", "value": "US"}]}, {"conditions": [{"attribute": "plan", "operator": "equals", "value": "ent"}]}]}, "user": {"plan": "free", "country": "EU"}, "expected": false},
|
|
36
36
|
{"name": "DNF: empty-conditions group never matches -> default", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "boolean", "default_value": false, "rules": [{"conditions": []}]}, "user": {"any": "thing"}, "expected": false},
|
|
37
|
-
{"name": "DNF back-compat: legacy flat rule still matches -> enabled", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "boolean", "default_value": false, "rules": [{"attribute": "plan", "operator": "equals", "value": "pro"}]}, "user": {"plan": "pro"}, "expected": true}
|
|
37
|
+
{"name": "DNF back-compat: legacy flat rule still matches -> enabled", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "boolean", "default_value": false, "rules": [{"attribute": "plan", "operator": "equals", "value": "pro"}]}, "user": {"plan": "pro"}, "expected": true},
|
|
38
|
+
{"name": "variation: string flag rule match serves enabled_value", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "string", "default_value": "Shop", "enabled_value": "Buy now", "rules": [{"conditions": [{"attribute": "plan", "operator": "equals", "value": "pro"}]}]}, "user": {"plan": "pro", "user_id": "u1"}, "expected": "Buy now"},
|
|
39
|
+
{"name": "variation: string flag no match serves default_value", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "string", "default_value": "Shop", "enabled_value": "Buy now", "rules": [{"conditions": [{"attribute": "plan", "operator": "equals", "value": "pro"}]}]}, "user": {"plan": "free", "user_id": "u1"}, "expected": "Shop"},
|
|
40
|
+
{"name": "variation: absent enabled_value falls back to default on match", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 0, "flag_type": "string", "default_value": "Shop", "rules": [{"conditions": [{"attribute": "plan", "operator": "equals", "value": "pro"}]}]}, "user": {"plan": "pro", "user_id": "u1"}, "expected": "Shop"},
|
|
41
|
+
{"name": "variation: number flag rollout 100 no context serves enabled_value", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 100, "flag_type": "number", "default_value": 0, "enabled_value": 42, "rules": []}, "user": null, "expected": 42},
|
|
42
|
+
{"name": "variation: disabled non-boolean serves default_value", "flag_key": "f", "flag": {"enabled": false, "rollout_pct": 100, "flag_type": "string", "default_value": "Shop", "enabled_value": "Buy now", "rules": []}, "user": {"user_id": "u1"}, "expected": "Shop"},
|
|
43
|
+
{"name": "variation: boolean flag ignores enabled_value (serves true)", "flag_key": "f", "flag": {"enabled": true, "rollout_pct": 100, "flag_type": "boolean", "default_value": false, "enabled_value": "ignored", "rules": []}, "user": null, "expected": true}
|
|
38
44
|
]
|
|
39
45
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|