zenmanage 1.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.
Files changed (55) hide show
  1. zenmanage-1.0.0/.github/workflows/ci.yml +39 -0
  2. zenmanage-1.0.0/.github/workflows/publish.yml +39 -0
  3. zenmanage-1.0.0/.gitignore +14 -0
  4. zenmanage-1.0.0/CHANGELOG.md +11 -0
  5. zenmanage-1.0.0/LICENSE +21 -0
  6. zenmanage-1.0.0/PKG-INFO +282 -0
  7. zenmanage-1.0.0/README.md +246 -0
  8. zenmanage-1.0.0/docs/FRAMEWORK_INTEGRATIONS.md +99 -0
  9. zenmanage-1.0.0/examples/README.md +20 -0
  10. zenmanage-1.0.0/examples/ab_testing.py +25 -0
  11. zenmanage-1.0.0/examples/caching.py +17 -0
  12. zenmanage-1.0.0/examples/context_based_flags.py +18 -0
  13. zenmanage-1.0.0/examples/defaults.py +19 -0
  14. zenmanage-1.0.0/examples/django_integration.py +23 -0
  15. zenmanage-1.0.0/examples/fastapi_async.py +30 -0
  16. zenmanage-1.0.0/examples/flask_integration.py +22 -0
  17. zenmanage-1.0.0/examples/percentage_rollouts.py +15 -0
  18. zenmanage-1.0.0/examples/simple_flags.py +8 -0
  19. zenmanage-1.0.0/pyproject.toml +80 -0
  20. zenmanage-1.0.0/scripts/validate_release.py +99 -0
  21. zenmanage-1.0.0/src/zenmanage/__init__.py +46 -0
  22. zenmanage-1.0.0/src/zenmanage/api_client.py +163 -0
  23. zenmanage-1.0.0/src/zenmanage/async_api_client.py +165 -0
  24. zenmanage-1.0.0/src/zenmanage/async_flag_manager.py +163 -0
  25. zenmanage-1.0.0/src/zenmanage/async_zenmanage.py +52 -0
  26. zenmanage-1.0.0/src/zenmanage/cache/__init__.py +8 -0
  27. zenmanage-1.0.0/src/zenmanage/cache/base.py +17 -0
  28. zenmanage-1.0.0/src/zenmanage/cache/filesystem.py +65 -0
  29. zenmanage-1.0.0/src/zenmanage/cache/in_memory.py +35 -0
  30. zenmanage-1.0.0/src/zenmanage/cache/null.py +22 -0
  31. zenmanage-1.0.0/src/zenmanage/config.py +133 -0
  32. zenmanage-1.0.0/src/zenmanage/context.py +89 -0
  33. zenmanage-1.0.0/src/zenmanage/defaults_collection.py +43 -0
  34. zenmanage-1.0.0/src/zenmanage/errors.py +27 -0
  35. zenmanage-1.0.0/src/zenmanage/flag.py +108 -0
  36. zenmanage-1.0.0/src/zenmanage/flag_manager.py +163 -0
  37. zenmanage-1.0.0/src/zenmanage/py.typed +0 -0
  38. zenmanage-1.0.0/src/zenmanage/rollout.py +23 -0
  39. zenmanage-1.0.0/src/zenmanage/rule_engine.py +178 -0
  40. zenmanage-1.0.0/src/zenmanage/types.py +104 -0
  41. zenmanage-1.0.0/src/zenmanage/zenmanage.py +48 -0
  42. zenmanage-1.0.0/tests/conftest.py +7 -0
  43. zenmanage-1.0.0/tests/test_api_client.py +174 -0
  44. zenmanage-1.0.0/tests/test_async_api_client.py +182 -0
  45. zenmanage-1.0.0/tests/test_async_flag_manager.py +163 -0
  46. zenmanage-1.0.0/tests/test_async_zenmanage.py +60 -0
  47. zenmanage-1.0.0/tests/test_cache.py +69 -0
  48. zenmanage-1.0.0/tests/test_config.py +65 -0
  49. zenmanage-1.0.0/tests/test_context.py +24 -0
  50. zenmanage-1.0.0/tests/test_defaults_collection.py +16 -0
  51. zenmanage-1.0.0/tests/test_flag.py +170 -0
  52. zenmanage-1.0.0/tests/test_flag_manager.py +189 -0
  53. zenmanage-1.0.0/tests/test_rollout.py +26 -0
  54. zenmanage-1.0.0/tests/test_rule_engine.py +176 -0
  55. zenmanage-1.0.0/tests/test_zenmanage.py +62 -0
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.11", "3.12"]
14
+
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
18
+
19
+ - name: Setup Python
20
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]"
28
+
29
+ - name: Lint
30
+ run: ruff check .
31
+
32
+ - name: Type check
33
+ run: mypy src
34
+
35
+ - name: Validate release metadata
36
+ run: python scripts/validate_release.py
37
+
38
+ - name: Test
39
+ run: pytest
@@ -0,0 +1,39 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
15
+
16
+ - name: Setup Python
17
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ - name: Install dependencies
22
+ run: |
23
+ python -m pip install --upgrade pip
24
+ pip install -e ".[dev]"
25
+
26
+ - name: Validate tag, version, and changelog
27
+ run: python scripts/validate_release.py --tag "${{ github.ref_name }}"
28
+
29
+ - name: Build
30
+ run: python -m build
31
+
32
+ - name: Verify package
33
+ run: python -m twine check dist/*
34
+
35
+ - name: Publish to PyPI
36
+ env:
37
+ TWINE_USERNAME: __token__
38
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
39
+ run: python -m twine upload dist/*
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.so
4
+ .venv/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .coverage
9
+ coverage.xml
10
+ dist/
11
+ build/
12
+ *.egg-info/
13
+ .cache/
14
+ .vscode/
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ ## 1.0.0 - 2026-05-31
6
+
7
+ - Initial Python SDK implementation.
8
+ - Added ConfigBuilder, flag evaluation, context targeting, and deterministic percentage rollouts.
9
+ - Added cache backends: in-memory, filesystem, and null.
10
+ - Added comprehensive test suite with 90%+ coverage target.
11
+ - Added runnable examples and publishing guide.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zenmanage
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: zenmanage
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for Zenmanage feature flags with local evaluation
5
+ Project-URL: Homepage, https://github.com/zenmanage/zenmanage-python
6
+ Project-URL: Repository, https://github.com/zenmanage/zenmanage-python
7
+ Project-URL: Documentation, https://github.com/zenmanage/zenmanage-python#readme
8
+ Project-URL: Issues, https://github.com/zenmanage/zenmanage-python/issues
9
+ Author-email: Zenmanage <hello@zenmanage.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: ab-testing,feature-flags,feature-toggles,remote-config,zenmanage
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: requests>=2.31.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: build>=1.2.1; extra == 'dev'
27
+ Requires-Dist: mypy>=1.11.1; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.23.7; extra == 'dev'
29
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
30
+ Requires-Dist: pytest>=8.2.0; extra == 'dev'
31
+ Requires-Dist: requests-mock>=1.12.1; extra == 'dev'
32
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
33
+ Requires-Dist: twine>=5.1.1; extra == 'dev'
34
+ Requires-Dist: types-requests>=2.32.0.20250602; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # Zenmanage Python SDK
38
+
39
+ [![PyPI version](https://badge.fury.io/py/zenmanage.svg)](https://pypi.org/project/zenmanage/)
40
+ [![CI](https://github.com/zenmanage/zenmanage-python/actions/workflows/ci.yml/badge.svg)](https://github.com/zenmanage/zenmanage-python/actions/workflows/ci.yml)
41
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/66861c5e33c04e208f9a779efedfce14)](https://app.codacy.com/gh/zenmanage/zenmanage-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
42
+ [![Coverage](https://img.shields.io/badge/coverage-90%25%2B-brightgreen)](#development)
43
+
44
+ Add feature flags to your Python application in minutes. Control feature rollouts, A/B test, and manage configurations without deploying code.
45
+
46
+ ## Why Zenmanage?
47
+
48
+ - Fast: rules cached locally for low-latency evaluation
49
+ - Targeted: roll out by user, organization, or custom attributes
50
+ - Safe: graceful defaults and typed accessors
51
+ - Insightful: optional usage reporting
52
+ - Testable: deterministic rollout logic and isolated rule engine
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install zenmanage
58
+ ```
59
+
60
+ Requirements: Python 3.9+
61
+
62
+ ## Key Compatibility
63
+
64
+ - Supported in Python SDK: case-sensitive server keys prefixed with `srv_`
65
+ - Not supported in Python SDK: client keys (`cli_`) and mobile keys (`mob_`) (initialization fails fast)
66
+
67
+ ## Get Started in 60 Seconds
68
+
69
+ 1. Get your server key (`srv_...`) from [zenmanage.com](https://zenmanage.com)
70
+ 2. Initialize the SDK:
71
+
72
+ ```python
73
+ from zenmanage import ConfigBuilder, Zenmanage
74
+
75
+ zenmanage = Zenmanage(
76
+ ConfigBuilder.create()
77
+ .with_environment_token("srv_your_server_key_here")
78
+ .build()
79
+ )
80
+ ```
81
+
82
+ 3. Check a feature flag:
83
+
84
+ ```python
85
+ flag = zenmanage.flags().single("new-dashboard", False)
86
+
87
+ if flag.is_enabled():
88
+ show_new_dashboard()
89
+ else:
90
+ show_old_dashboard()
91
+ ```
92
+
93
+ ## Common Use Cases
94
+
95
+ ### Async Frameworks (FastAPI, Starlette, etc.)
96
+
97
+ ```python
98
+ from zenmanage import AsyncZenmanage, ConfigBuilder, Context
99
+
100
+ zenmanage = AsyncZenmanage(
101
+ ConfigBuilder.from_environment().build()
102
+ )
103
+
104
+ async def is_enabled_for_user(user_id: str) -> bool:
105
+ context = Context.single("user", user_id)
106
+ flag = await zenmanage.flags().with_context(context).single("new-dashboard", False)
107
+ return flag.is_enabled()
108
+ ```
109
+
110
+ Remember to close the async client on shutdown:
111
+
112
+ ```python
113
+ await zenmanage.aclose()
114
+ ```
115
+
116
+ ### Roll Out a New Feature Gradually
117
+
118
+ ```python
119
+ from zenmanage import Context
120
+
121
+ context = Context.single("user", user_id, user_name)
122
+
123
+ beta_access = (
124
+ zenmanage.flags()
125
+ .with_context(context)
126
+ .single("beta-program", False)
127
+ .is_enabled()
128
+ )
129
+
130
+ if beta_access:
131
+ enable_beta_features()
132
+ ```
133
+
134
+ ### A/B Testing
135
+
136
+ ```python
137
+ from zenmanage import Attribute, Context
138
+
139
+ context = Context.single("user", user.id, user.name)
140
+ context.add_attribute(Attribute.from_strings("country", [user.country]))
141
+ context.add_attribute(Attribute.from_strings("plan", [user.subscription_plan]))
142
+
143
+ variant = (
144
+ zenmanage.flags()
145
+ .with_context(context)
146
+ .single("checkout-flow", "multi-page")
147
+ .as_string()
148
+ )
149
+
150
+ if variant == "one-page":
151
+ render_one_page_checkout()
152
+ else:
153
+ render_multi_page_checkout()
154
+ ```
155
+
156
+ ### Percentage Rollouts
157
+
158
+ ```python
159
+ from zenmanage import Context
160
+
161
+ context = Context.single("user", user_id)
162
+
163
+ flag = (
164
+ zenmanage.flags()
165
+ .with_context(context)
166
+ .single("new-checkout-flow", False)
167
+ )
168
+
169
+ if flag.is_enabled():
170
+ render_new_checkout()
171
+ else:
172
+ render_classic_checkout()
173
+ ```
174
+
175
+ How it works:
176
+
177
+ - Configure rollout percentage (0-100) and salt in Zenmanage
178
+ - SDK computes CRC32B bucket from `salt:contextIdentifier`
179
+ - Same user always lands in same bucket
180
+ - Increasing percentage only adds users, never removes included users
181
+
182
+ ### Use Defaults Across Many Flags
183
+
184
+ ```python
185
+ from zenmanage import DefaultsCollection
186
+
187
+ defaults = DefaultsCollection.from_dict(
188
+ {
189
+ "new-ui": True,
190
+ "api-version": "v2",
191
+ "max-items": 100,
192
+ }
193
+ )
194
+
195
+ flag_manager = zenmanage.flags().with_defaults(defaults)
196
+ new_ui = flag_manager.single("new-ui").as_bool()
197
+ ```
198
+
199
+ ### Fetch All Flags
200
+
201
+ ```python
202
+ for flag in zenmanage.flags().all():
203
+ print(flag.key, flag.get_value())
204
+ ```
205
+
206
+ ## Configuration
207
+
208
+ ```python
209
+ import logging
210
+
211
+ config = (
212
+ ConfigBuilder.create()
213
+ .with_environment_token("srv_your_server_key_here")
214
+ .with_cache_ttl(3600)
215
+ .with_cache_backend("memory") # memory | filesystem | null
216
+ .with_cache_directory(".cache/zenmanage") # required for filesystem
217
+ .with_usage_reporting(True)
218
+ .with_api_endpoint("https://api.zenmanage.com")
219
+ .with_logger(logging.getLogger("my-app"))
220
+ .build()
221
+ )
222
+ ```
223
+
224
+ You can also load from environment variables:
225
+
226
+ - `ZENMANAGE_ENVIRONMENT_TOKEN`
227
+ - `ZENMANAGE_CACHE_TTL`
228
+ - `ZENMANAGE_CACHE_BACKEND`
229
+ - `ZENMANAGE_CACHE_DIR`
230
+ - `ZENMANAGE_ENABLE_USAGE_REPORTING`
231
+ - `ZENMANAGE_API_ENDPOINT`
232
+
233
+ ```python
234
+ config = ConfigBuilder.from_environment().build()
235
+ ```
236
+
237
+ ## Cache Backends
238
+
239
+ - `memory`: default, fastest, process-local
240
+ - `filesystem`: durable between process restarts
241
+ - `null`: disables caching
242
+
243
+ Custom cache objects are supported with `with_cache(...)` as long as they implement `get`, `set`, `has`, `delete`, and `clear`.
244
+
245
+ ## Examples
246
+
247
+ See [examples/README.md](examples/README.md) for runnable examples:
248
+
249
+ - simple-flags
250
+ - ab-testing
251
+ - caching
252
+ - context-based-flags
253
+ - defaults
254
+ - percentage-rollouts
255
+ - django-integration
256
+ - flask-integration
257
+ - fastapi-async
258
+
259
+ Framework integrations: [docs/FRAMEWORK_INTEGRATIONS.md](docs/FRAMEWORK_INTEGRATIONS.md)
260
+
261
+ ## Development
262
+
263
+ ```bash
264
+ python -m venv .venv
265
+ source .venv/bin/activate
266
+ pip install -e .[dev]
267
+ pytest
268
+ ruff check .
269
+ mypy src
270
+ ```
271
+
272
+ ## Publishing
273
+
274
+ Validate tag/version/changelog consistency locally:
275
+
276
+ ```bash
277
+ python scripts/validate_release.py --tag v1.0.0
278
+ ```
279
+
280
+ ## License
281
+
282
+ MIT
@@ -0,0 +1,246 @@
1
+ # Zenmanage Python SDK
2
+
3
+ [![PyPI version](https://badge.fury.io/py/zenmanage.svg)](https://pypi.org/project/zenmanage/)
4
+ [![CI](https://github.com/zenmanage/zenmanage-python/actions/workflows/ci.yml/badge.svg)](https://github.com/zenmanage/zenmanage-python/actions/workflows/ci.yml)
5
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/66861c5e33c04e208f9a779efedfce14)](https://app.codacy.com/gh/zenmanage/zenmanage-python/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
6
+ [![Coverage](https://img.shields.io/badge/coverage-90%25%2B-brightgreen)](#development)
7
+
8
+ Add feature flags to your Python application in minutes. Control feature rollouts, A/B test, and manage configurations without deploying code.
9
+
10
+ ## Why Zenmanage?
11
+
12
+ - Fast: rules cached locally for low-latency evaluation
13
+ - Targeted: roll out by user, organization, or custom attributes
14
+ - Safe: graceful defaults and typed accessors
15
+ - Insightful: optional usage reporting
16
+ - Testable: deterministic rollout logic and isolated rule engine
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install zenmanage
22
+ ```
23
+
24
+ Requirements: Python 3.9+
25
+
26
+ ## Key Compatibility
27
+
28
+ - Supported in Python SDK: case-sensitive server keys prefixed with `srv_`
29
+ - Not supported in Python SDK: client keys (`cli_`) and mobile keys (`mob_`) (initialization fails fast)
30
+
31
+ ## Get Started in 60 Seconds
32
+
33
+ 1. Get your server key (`srv_...`) from [zenmanage.com](https://zenmanage.com)
34
+ 2. Initialize the SDK:
35
+
36
+ ```python
37
+ from zenmanage import ConfigBuilder, Zenmanage
38
+
39
+ zenmanage = Zenmanage(
40
+ ConfigBuilder.create()
41
+ .with_environment_token("srv_your_server_key_here")
42
+ .build()
43
+ )
44
+ ```
45
+
46
+ 3. Check a feature flag:
47
+
48
+ ```python
49
+ flag = zenmanage.flags().single("new-dashboard", False)
50
+
51
+ if flag.is_enabled():
52
+ show_new_dashboard()
53
+ else:
54
+ show_old_dashboard()
55
+ ```
56
+
57
+ ## Common Use Cases
58
+
59
+ ### Async Frameworks (FastAPI, Starlette, etc.)
60
+
61
+ ```python
62
+ from zenmanage import AsyncZenmanage, ConfigBuilder, Context
63
+
64
+ zenmanage = AsyncZenmanage(
65
+ ConfigBuilder.from_environment().build()
66
+ )
67
+
68
+ async def is_enabled_for_user(user_id: str) -> bool:
69
+ context = Context.single("user", user_id)
70
+ flag = await zenmanage.flags().with_context(context).single("new-dashboard", False)
71
+ return flag.is_enabled()
72
+ ```
73
+
74
+ Remember to close the async client on shutdown:
75
+
76
+ ```python
77
+ await zenmanage.aclose()
78
+ ```
79
+
80
+ ### Roll Out a New Feature Gradually
81
+
82
+ ```python
83
+ from zenmanage import Context
84
+
85
+ context = Context.single("user", user_id, user_name)
86
+
87
+ beta_access = (
88
+ zenmanage.flags()
89
+ .with_context(context)
90
+ .single("beta-program", False)
91
+ .is_enabled()
92
+ )
93
+
94
+ if beta_access:
95
+ enable_beta_features()
96
+ ```
97
+
98
+ ### A/B Testing
99
+
100
+ ```python
101
+ from zenmanage import Attribute, Context
102
+
103
+ context = Context.single("user", user.id, user.name)
104
+ context.add_attribute(Attribute.from_strings("country", [user.country]))
105
+ context.add_attribute(Attribute.from_strings("plan", [user.subscription_plan]))
106
+
107
+ variant = (
108
+ zenmanage.flags()
109
+ .with_context(context)
110
+ .single("checkout-flow", "multi-page")
111
+ .as_string()
112
+ )
113
+
114
+ if variant == "one-page":
115
+ render_one_page_checkout()
116
+ else:
117
+ render_multi_page_checkout()
118
+ ```
119
+
120
+ ### Percentage Rollouts
121
+
122
+ ```python
123
+ from zenmanage import Context
124
+
125
+ context = Context.single("user", user_id)
126
+
127
+ flag = (
128
+ zenmanage.flags()
129
+ .with_context(context)
130
+ .single("new-checkout-flow", False)
131
+ )
132
+
133
+ if flag.is_enabled():
134
+ render_new_checkout()
135
+ else:
136
+ render_classic_checkout()
137
+ ```
138
+
139
+ How it works:
140
+
141
+ - Configure rollout percentage (0-100) and salt in Zenmanage
142
+ - SDK computes CRC32B bucket from `salt:contextIdentifier`
143
+ - Same user always lands in same bucket
144
+ - Increasing percentage only adds users, never removes included users
145
+
146
+ ### Use Defaults Across Many Flags
147
+
148
+ ```python
149
+ from zenmanage import DefaultsCollection
150
+
151
+ defaults = DefaultsCollection.from_dict(
152
+ {
153
+ "new-ui": True,
154
+ "api-version": "v2",
155
+ "max-items": 100,
156
+ }
157
+ )
158
+
159
+ flag_manager = zenmanage.flags().with_defaults(defaults)
160
+ new_ui = flag_manager.single("new-ui").as_bool()
161
+ ```
162
+
163
+ ### Fetch All Flags
164
+
165
+ ```python
166
+ for flag in zenmanage.flags().all():
167
+ print(flag.key, flag.get_value())
168
+ ```
169
+
170
+ ## Configuration
171
+
172
+ ```python
173
+ import logging
174
+
175
+ config = (
176
+ ConfigBuilder.create()
177
+ .with_environment_token("srv_your_server_key_here")
178
+ .with_cache_ttl(3600)
179
+ .with_cache_backend("memory") # memory | filesystem | null
180
+ .with_cache_directory(".cache/zenmanage") # required for filesystem
181
+ .with_usage_reporting(True)
182
+ .with_api_endpoint("https://api.zenmanage.com")
183
+ .with_logger(logging.getLogger("my-app"))
184
+ .build()
185
+ )
186
+ ```
187
+
188
+ You can also load from environment variables:
189
+
190
+ - `ZENMANAGE_ENVIRONMENT_TOKEN`
191
+ - `ZENMANAGE_CACHE_TTL`
192
+ - `ZENMANAGE_CACHE_BACKEND`
193
+ - `ZENMANAGE_CACHE_DIR`
194
+ - `ZENMANAGE_ENABLE_USAGE_REPORTING`
195
+ - `ZENMANAGE_API_ENDPOINT`
196
+
197
+ ```python
198
+ config = ConfigBuilder.from_environment().build()
199
+ ```
200
+
201
+ ## Cache Backends
202
+
203
+ - `memory`: default, fastest, process-local
204
+ - `filesystem`: durable between process restarts
205
+ - `null`: disables caching
206
+
207
+ Custom cache objects are supported with `with_cache(...)` as long as they implement `get`, `set`, `has`, `delete`, and `clear`.
208
+
209
+ ## Examples
210
+
211
+ See [examples/README.md](examples/README.md) for runnable examples:
212
+
213
+ - simple-flags
214
+ - ab-testing
215
+ - caching
216
+ - context-based-flags
217
+ - defaults
218
+ - percentage-rollouts
219
+ - django-integration
220
+ - flask-integration
221
+ - fastapi-async
222
+
223
+ Framework integrations: [docs/FRAMEWORK_INTEGRATIONS.md](docs/FRAMEWORK_INTEGRATIONS.md)
224
+
225
+ ## Development
226
+
227
+ ```bash
228
+ python -m venv .venv
229
+ source .venv/bin/activate
230
+ pip install -e .[dev]
231
+ pytest
232
+ ruff check .
233
+ mypy src
234
+ ```
235
+
236
+ ## Publishing
237
+
238
+ Validate tag/version/changelog consistency locally:
239
+
240
+ ```bash
241
+ python scripts/validate_release.py --tag v1.0.0
242
+ ```
243
+
244
+ ## License
245
+
246
+ MIT