toggletest 0.1.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.
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: toggletest
3
+ Version: 0.1.0
4
+ Summary: ToggleTest SDK for feature flags and A/B testing
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: httpx-sse>=0.4
8
+ Requires-Dist: httpx>=0.25
9
+ Requires-Dist: wasmtime>=27.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest; extra == 'dev'
12
+ Requires-Dist: pytest-asyncio; extra == 'dev'
@@ -0,0 +1,289 @@
1
+ # ToggleTest Python SDK
2
+
3
+ Python SDK for feature flags and A/B testing with local WASM-based evaluation. After initialization, all flag evaluations happen locally with zero network latency.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install toggletest
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from toggletest import ToggleTestClient
15
+
16
+ # Initialize the client
17
+ client = ToggleTestClient(
18
+ api_key="tt_your_api_key",
19
+ base_url="https://api.toggletest.com",
20
+ environment="production" # optional
21
+ )
22
+
23
+ # Start the SDK (fetches WASM evaluator and rules)
24
+ client.start()
25
+
26
+ # Evaluate a feature flag
27
+ if client.is_enabled("dark-mode", user_id="user-123"):
28
+ print("Dark mode is enabled")
29
+
30
+ # Get A/B test variant
31
+ result = client.get_variant("pricing-test", user_id="user-123")
32
+ print(f"User assigned to variant: {result.variant}")
33
+
34
+ # Track an event
35
+ client.track(
36
+ "conversion",
37
+ end_user_id="user-123",
38
+ test_key="pricing-test",
39
+ variant_name=result.variant
40
+ )
41
+
42
+ # Shutdown gracefully
43
+ client.close()
44
+ ```
45
+
46
+ ## API Reference
47
+
48
+ ### Client Initialization
49
+
50
+ ```python
51
+ client = ToggleTestClient(
52
+ api_key="tt_your_api_key",
53
+ base_url="https://api.toggletest.com",
54
+ environment="production", # optional
55
+ on_ready=lambda: print("SDK ready"), # optional callback
56
+ on_error=lambda e: print(f"Error: {e}") # optional error handler
57
+ )
58
+ ```
59
+
60
+ ### Lifecycle Methods
61
+
62
+ #### `start()`
63
+ Initialize the SDK. Fetches the WASM evaluator binary and rules, then starts the SSE connection for real-time updates.
64
+
65
+ ```python
66
+ client.start()
67
+ ```
68
+
69
+ Raises `RuntimeError` if WASM loading fails or `httpx.HTTPError` if rules fetch fails.
70
+
71
+ #### `close()`
72
+ Gracefully shut down the SDK. Disconnects SSE, flushes buffered events, and releases resources.
73
+
74
+ ```python
75
+ client.close()
76
+ ```
77
+
78
+ #### `ready` (property)
79
+ Check if the SDK has completed initialization.
80
+
81
+ ```python
82
+ if client.ready:
83
+ # SDK is ready for evaluations
84
+ ```
85
+
86
+ ### Feature Flag Evaluation
87
+
88
+ #### `is_enabled(flag_key, *, user_id, attributes=None)`
89
+ Returns a boolean indicating if a flag is enabled. Returns `False` if the flag is not found.
90
+
91
+ ```python
92
+ enabled = client.is_enabled(
93
+ "dark-mode",
94
+ user_id="user-123",
95
+ attributes={"plan": "pro", "country": "US"}
96
+ )
97
+ ```
98
+
99
+ #### `evaluate_flag(flag_key, *, user_id, attributes=None)`
100
+ Returns the full flag evaluation result including value, reason, and rule ID.
101
+
102
+ ```python
103
+ from toggletest import FlagResult
104
+
105
+ result = client.evaluate_flag(
106
+ "feature-x",
107
+ user_id="user-123",
108
+ attributes={"beta": True}
109
+ )
110
+
111
+ print(f"Value: {result.value}")
112
+ print(f"Reason: {result.reason}")
113
+ print(f"Rule ID: {result.rule_id}")
114
+ ```
115
+
116
+ **Returns:** `FlagResult` with:
117
+ - `value`: The flag value (bool, str, number, or dict)
118
+ - `reason`: Evaluation reason (e.g., "rule_match", "default", "disabled", "not_found")
119
+ - `rule_id`: ID of the matched rule, if any
120
+
121
+ #### `evaluate_all(*, user_id, attributes=None)`
122
+ Evaluate all flags and tests for the given user. Useful for bootstrapping client-side SDKs.
123
+
124
+ ```python
125
+ from toggletest import EvalResults
126
+
127
+ results = client.evaluate_all(
128
+ user_id="user-123",
129
+ attributes={"plan": "enterprise"}
130
+ )
131
+
132
+ # Access all flags
133
+ for flag_key, flag_result in results.flags.items():
134
+ print(f"{flag_key}: {flag_result.value}")
135
+
136
+ # Access all tests
137
+ for test_key, test_result in results.tests.items():
138
+ print(f"{test_key}: {test_result.variant}")
139
+ ```
140
+
141
+ **Returns:** `EvalResults` with:
142
+ - `flags`: Dict[str, FlagResult] - All flag evaluations
143
+ - `tests`: Dict[str, TestResult] - All test assignments
144
+
145
+ ### A/B Testing
146
+
147
+ #### `get_variant(test_key, *, user_id, attributes=None)`
148
+ Get the variant assignment for an A/B test. Users are deterministically assigned based on a hash of user_id + test_key.
149
+
150
+ ```python
151
+ from toggletest import TestResult
152
+
153
+ result = client.get_variant(
154
+ "pricing-test",
155
+ user_id="user-123",
156
+ attributes={"plan": "pro"}
157
+ )
158
+
159
+ print(f"Variant: {result.variant}") # e.g., "control", "variant_a"
160
+ print(f"Bucket: {result.bucket}") # 0-9999
161
+ ```
162
+
163
+ **Returns:** `TestResult` with:
164
+ - `variant`: Variant name (e.g., "control", "variant_a")
165
+ - `bucket`: Deterministic bucket value (0-9999)
166
+
167
+ ### Event Tracking
168
+
169
+ #### `track(event_type, *, end_user_id, test_key=None, flag_key=None, variant_name=None, evaluation_reason=None, metadata=None)`
170
+ Track an analytics event. Events are buffered and sent in batches.
171
+
172
+ ```python
173
+ # Track a conversion
174
+ client.track(
175
+ "conversion",
176
+ end_user_id="user-123",
177
+ test_key="pricing-test",
178
+ variant_name="variant_a",
179
+ metadata={"revenue": 99.99}
180
+ )
181
+
182
+ # Track a flag evaluation
183
+ client.track(
184
+ "flag_evaluation",
185
+ end_user_id="user-123",
186
+ flag_key="dark-mode",
187
+ evaluation_reason="rule_match"
188
+ )
189
+
190
+ # Track a variant assignment
191
+ client.track(
192
+ "variant_assignment",
193
+ end_user_id="user-123",
194
+ test_key="homepage-redesign",
195
+ variant_name="control"
196
+ )
197
+ ```
198
+
199
+ Common event types:
200
+ - `"conversion"` - User completed a goal
201
+ - `"flag_evaluation"` - A flag was evaluated
202
+ - `"variant_assignment"` - User was assigned to a variant
203
+
204
+ ### Listeners
205
+
206
+ #### `on_rules_update(listener)`
207
+ Register a callback for rules updates. The listener receives the new rules version hash.
208
+
209
+ ```python
210
+ def on_update(version_hash):
211
+ print(f"Rules updated to version: {version_hash}")
212
+
213
+ unsubscribe = client.on_rules_update(on_update)
214
+
215
+ # Later, to unsubscribe:
216
+ unsubscribe()
217
+ ```
218
+
219
+ ## Configuration Options
220
+
221
+ | Parameter | Type | Required | Description |
222
+ |-----------|------|----------|-------------|
223
+ | `api_key` | str | Yes | SDK API key (typically prefixed with "tt_") |
224
+ | `base_url` | str | Yes | Base URL of the ToggleTest API |
225
+ | `environment` | str | No | Environment identifier (e.g., "production", "staging") |
226
+ | `on_ready` | Callable | No | Callback invoked when SDK initialization completes |
227
+ | `on_error` | Callable | No | Callback invoked on non-fatal errors (SSE disconnect, etc.) |
228
+
229
+ ## User Context and Attributes
230
+
231
+ The `attributes` parameter accepts a dictionary of user/request attributes for targeting rules:
232
+
233
+ ```python
234
+ client.is_enabled(
235
+ "premium-feature",
236
+ user_id="user-123",
237
+ attributes={
238
+ "plan": "enterprise",
239
+ "country": "US",
240
+ "beta_tester": True,
241
+ "account_age_days": 45
242
+ }
243
+ )
244
+ ```
245
+
246
+ ## Architecture
247
+
248
+ The SDK uses WASM-based local evaluation for zero-latency flag checks:
249
+
250
+ 1. **Initialization** - Downloads WASM evaluator binary and rules JSON
251
+ 2. **Local Evaluation** - All flag/test evaluations run in-process via WASM
252
+ 3. **Real-time Updates** - SSE connection for instant rules change notifications
253
+ 4. **Event Batching** - Analytics events are buffered and sent periodically
254
+
255
+ After `start()` completes, all evaluation methods are synchronous with no network calls in the hot path.
256
+
257
+ ## Error Handling
258
+
259
+ ```python
260
+ from toggletest import ToggleTestClient
261
+
262
+ client = ToggleTestClient(
263
+ api_key="tt_key",
264
+ base_url="https://api.toggletest.com",
265
+ on_error=lambda e: print(f"SDK Error: {e}")
266
+ )
267
+
268
+ try:
269
+ client.start()
270
+ except RuntimeError as e:
271
+ print(f"Failed to start SDK: {e}")
272
+
273
+ # Evaluation methods raise RuntimeError if SDK is not ready
274
+ if not client.ready:
275
+ print("SDK not ready")
276
+ else:
277
+ result = client.evaluate_flag("feature", user_id="user-123")
278
+ ```
279
+
280
+ ## Requirements
281
+
282
+ - Python 3.9+
283
+ - httpx >= 0.25
284
+ - httpx-sse >= 0.4
285
+ - wasmtime >= 27.0
286
+
287
+ ## License
288
+
289
+ MIT
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "toggletest"
7
+ version = "0.1.0"
8
+ description = "ToggleTest SDK for feature flags and A/B testing"
9
+ requires-python = ">=3.9"
10
+ license = "MIT"
11
+ dependencies = [
12
+ "httpx>=0.25",
13
+ "httpx-sse>=0.4",
14
+ "wasmtime>=27.0",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["pytest", "pytest-asyncio"]
@@ -0,0 +1,54 @@
1
+ """
2
+ toggletest - ToggleTest Python SDK with WASM-based local evaluation
3
+
4
+ This is the package entry point. It re-exports only the types and classes
5
+ that SDK consumers need. Internal implementation details (WasmEngine,
6
+ RulesStore, SseConnection, EventBuffer) are NOT exported.
7
+
8
+ Usage:
9
+ from toggletest import ToggleTestClient
10
+ from toggletest import EvalContext, EvalResults, FlagResult, TestResult
11
+
12
+ client = ToggleTestClient(
13
+ api_key="tt_...",
14
+ base_url="http://localhost:3001",
15
+ environment="production",
16
+ )
17
+ client.start()
18
+
19
+ if client.is_enabled("dark-mode", user_id="user-123"):
20
+ ...
21
+
22
+ client.close()
23
+ """
24
+
25
+ # -- Main client class --
26
+ from .client import ToggleTestClient
27
+
28
+ # -- Configuration and context types --
29
+ from .types import ToggleTestConfig, EvalContext
30
+
31
+ # -- Evaluation result types --
32
+ from .types import FlagResult, TestResult, EvalResults
33
+
34
+ # -- Event tracking types --
35
+ from .types import SdkEvent
36
+
37
+ # -- Listener types --
38
+ from .types import RulesUpdateListener
39
+
40
+ __all__ = [
41
+ # Client
42
+ "ToggleTestClient",
43
+ # Config / context
44
+ "ToggleTestConfig",
45
+ "EvalContext",
46
+ # Evaluation results
47
+ "FlagResult",
48
+ "TestResult",
49
+ "EvalResults",
50
+ # Events
51
+ "SdkEvent",
52
+ # Listeners
53
+ "RulesUpdateListener",
54
+ ]