rollgate 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.
- rollgate-1.0.0/.gitignore +1 -0
- rollgate-1.0.0/PKG-INFO +288 -0
- rollgate-1.0.0/README.md +254 -0
- rollgate-1.0.0/pyproject.toml +68 -0
- rollgate-1.0.0/rollgate/__init__.py +135 -0
- rollgate-1.0.0/rollgate/cache.py +260 -0
- rollgate-1.0.0/rollgate/circuit_breaker.py +240 -0
- rollgate-1.0.0/rollgate/client.py +562 -0
- rollgate-1.0.0/rollgate/dedup.py +172 -0
- rollgate-1.0.0/rollgate/errors.py +162 -0
- rollgate-1.0.0/rollgate/evaluate.py +345 -0
- rollgate-1.0.0/rollgate/metrics.py +567 -0
- rollgate-1.0.0/rollgate/reasons.py +115 -0
- rollgate-1.0.0/rollgate/retry.py +177 -0
- rollgate-1.0.0/rollgate/tracing.py +434 -0
- rollgate-1.0.0/src/rollgate/__init__.py +32 -0
- rollgate-1.0.0/src/rollgate/config.py +57 -0
- rollgate-1.0.0/test_service/main.py +353 -0
- rollgate-1.0.0/test_service/requirements.txt +4 -0
- rollgate-1.0.0/tests/__init__.py +1 -0
- rollgate-1.0.0/tests/test_cache.py +228 -0
- rollgate-1.0.0/tests/test_circuit_breaker.py +199 -0
- rollgate-1.0.0/tests/test_client.py +305 -0
- rollgate-1.0.0/tests/test_dedup.py +218 -0
- rollgate-1.0.0/tests/test_evaluate.py +304 -0
- rollgate-1.0.0/tests/test_metrics.py +237 -0
- rollgate-1.0.0/tests/test_retry.py +177 -0
- rollgate-1.0.0/tests/test_tracing.py +296 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__pycache__/
|
rollgate-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rollgate
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for Rollgate feature flags
|
|
5
|
+
Project-URL: Homepage, https://rollgate.io
|
|
6
|
+
Project-URL: Documentation, https://rollgate.io/docs/sdk/python
|
|
7
|
+
Project-URL: Repository, https://github.com/thejord-it/rollgate
|
|
8
|
+
Author-email: Rollgate <hello@rollgate.io>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: feature-flags,feature-toggles,rollgate,sdk
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: httpx-sse>=0.4.0
|
|
25
|
+
Requires-Dist: httpx>=0.25.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: respx>=0.20.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Rollgate Python SDK
|
|
36
|
+
|
|
37
|
+
[](https://github.com/rollgate/sdks/actions/workflows/ci.yml)
|
|
38
|
+
[](https://pypi.org/project/rollgate/)
|
|
39
|
+
[](https://opensource.org/licenses/MIT)
|
|
40
|
+
|
|
41
|
+
Official Python SDK for [Rollgate](https://rollgate.io) - Feature flags made simple.
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- Python 3.9+
|
|
46
|
+
- httpx >= 0.25.0
|
|
47
|
+
- httpx-sse >= 0.4.0
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install rollgate
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import asyncio
|
|
59
|
+
from rollgate import RollgateClient, RollgateConfig, UserContext
|
|
60
|
+
|
|
61
|
+
async def main():
|
|
62
|
+
# Initialize client
|
|
63
|
+
config = RollgateConfig(api_key="your-api-key")
|
|
64
|
+
client = RollgateClient(config)
|
|
65
|
+
|
|
66
|
+
# Initialize and fetch flags
|
|
67
|
+
await client.init()
|
|
68
|
+
|
|
69
|
+
# Check if feature is enabled
|
|
70
|
+
if client.is_enabled("new-feature"):
|
|
71
|
+
print("New feature is enabled!")
|
|
72
|
+
|
|
73
|
+
# With user targeting
|
|
74
|
+
await client.identify(UserContext(
|
|
75
|
+
id="user-123",
|
|
76
|
+
email="user@example.com",
|
|
77
|
+
attributes={"plan": "pro", "country": "IT"}
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
if client.is_enabled("premium-feature"):
|
|
81
|
+
print("Premium feature is enabled for this user!")
|
|
82
|
+
|
|
83
|
+
# Cleanup
|
|
84
|
+
await client.close()
|
|
85
|
+
|
|
86
|
+
asyncio.run(main())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Context Manager
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
async with RollgateClient(RollgateConfig(api_key="your-api-key")) as client:
|
|
93
|
+
if client.is_enabled("my-feature"):
|
|
94
|
+
# Feature is enabled
|
|
95
|
+
pass
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Configuration
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from rollgate import (
|
|
102
|
+
RollgateConfig,
|
|
103
|
+
RetryConfig,
|
|
104
|
+
CircuitBreakerConfig,
|
|
105
|
+
CacheConfig,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
config = RollgateConfig(
|
|
109
|
+
api_key="your-api-key",
|
|
110
|
+
base_url="https://api.rollgate.io", # Custom API URL
|
|
111
|
+
refresh_interval_ms=30000, # Polling interval (30s default)
|
|
112
|
+
enable_streaming=False, # Use SSE for real-time updates
|
|
113
|
+
timeout_ms=5000, # Request timeout
|
|
114
|
+
|
|
115
|
+
# Retry configuration
|
|
116
|
+
retry=RetryConfig(
|
|
117
|
+
max_retries=3,
|
|
118
|
+
base_delay_ms=100,
|
|
119
|
+
max_delay_ms=10000,
|
|
120
|
+
jitter_factor=0.1,
|
|
121
|
+
),
|
|
122
|
+
|
|
123
|
+
# Circuit breaker configuration
|
|
124
|
+
circuit_breaker=CircuitBreakerConfig(
|
|
125
|
+
failure_threshold=5,
|
|
126
|
+
recovery_timeout_ms=30000,
|
|
127
|
+
monitoring_window_ms=60000,
|
|
128
|
+
success_threshold=3,
|
|
129
|
+
),
|
|
130
|
+
|
|
131
|
+
# Cache configuration
|
|
132
|
+
cache=CacheConfig(
|
|
133
|
+
ttl_ms=300000, # 5 minutes
|
|
134
|
+
stale_ttl_ms=3600000, # 1 hour
|
|
135
|
+
persist_path="/tmp/rollgate-cache.json", # Optional persistence
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Events
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
client = RollgateClient(config)
|
|
144
|
+
|
|
145
|
+
# Register event callbacks
|
|
146
|
+
client.on("ready", lambda: print("Client ready"))
|
|
147
|
+
client.on("flags_updated", lambda flags: print(f"Flags updated: {flags}"))
|
|
148
|
+
client.on("flag_changed", lambda key, new, old: print(f"{key}: {old} -> {new}"))
|
|
149
|
+
client.on("error", lambda err: print(f"Error: {err}"))
|
|
150
|
+
client.on("circuit_open", lambda *args: print("Circuit breaker opened"))
|
|
151
|
+
client.on("circuit_closed", lambda: print("Circuit breaker closed"))
|
|
152
|
+
|
|
153
|
+
await client.init()
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Features
|
|
157
|
+
|
|
158
|
+
### Polling (Default)
|
|
159
|
+
|
|
160
|
+
By default, the SDK polls for flag updates every 30 seconds.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
config = RollgateConfig(
|
|
164
|
+
api_key="your-api-key",
|
|
165
|
+
refresh_interval_ms=30000, # Poll every 30s
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### SSE Streaming
|
|
170
|
+
|
|
171
|
+
Enable Server-Sent Events for real-time flag updates:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
config = RollgateConfig(
|
|
175
|
+
api_key="your-api-key",
|
|
176
|
+
enable_streaming=True,
|
|
177
|
+
)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Circuit Breaker
|
|
181
|
+
|
|
182
|
+
The SDK includes a circuit breaker to prevent cascading failures:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
# Check circuit state
|
|
186
|
+
state = client.circuit_state # CircuitState.CLOSED, OPEN, or HALF_OPEN
|
|
187
|
+
|
|
188
|
+
# Get statistics
|
|
189
|
+
stats = client.get_circuit_stats()
|
|
190
|
+
|
|
191
|
+
# Force reset
|
|
192
|
+
client.reset_circuit()
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Caching
|
|
196
|
+
|
|
197
|
+
Flags are cached locally with stale-while-revalidate support:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
# Get cache statistics
|
|
201
|
+
stats = client.get_cache_stats()
|
|
202
|
+
hit_rate = client.get_cache_hit_rate()
|
|
203
|
+
|
|
204
|
+
# Clear cache
|
|
205
|
+
client.clear_cache()
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Error Handling
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from rollgate import (
|
|
212
|
+
RollgateError,
|
|
213
|
+
AuthenticationError,
|
|
214
|
+
NetworkError,
|
|
215
|
+
RateLimitError,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
await client.init()
|
|
220
|
+
except AuthenticationError as e:
|
|
221
|
+
print(f"Invalid API key: {e}")
|
|
222
|
+
except NetworkError as e:
|
|
223
|
+
print(f"Network error: {e}")
|
|
224
|
+
except RateLimitError as e:
|
|
225
|
+
print(f"Rate limited, retry after: {e.retry_after}s")
|
|
226
|
+
except RollgateError as e:
|
|
227
|
+
print(f"Rollgate error: {e}")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## API Reference
|
|
231
|
+
|
|
232
|
+
### RollgateClient
|
|
233
|
+
|
|
234
|
+
| Method | Description |
|
|
235
|
+
| --------------------------------------- | ---------------------------------- |
|
|
236
|
+
| `init(user?)` | Initialize client and fetch flags |
|
|
237
|
+
| `is_enabled(flag_key, default?)` | Check if flag is enabled |
|
|
238
|
+
| `is_enabled_detail(flag_key, default?)` | Check flag with evaluation reason |
|
|
239
|
+
| `get_all_flags()` | Get all flags as dictionary |
|
|
240
|
+
| `identify(user)` | Set user context and refresh flags |
|
|
241
|
+
| `reset()` | Clear user context |
|
|
242
|
+
| `refresh()` | Force refresh flags |
|
|
243
|
+
| `close()` | Cleanup resources |
|
|
244
|
+
|
|
245
|
+
### Evaluation Reasons
|
|
246
|
+
|
|
247
|
+
Get detailed information about why a flag evaluated to a particular value:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
detail = client.is_enabled_detail("my-flag", False)
|
|
251
|
+
print(detail.value) # bool
|
|
252
|
+
print(detail.reason.kind) # "OFF", "TARGET_MATCH", "RULE_MATCH", "FALLTHROUGH", "ERROR", "UNKNOWN"
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Reason kinds:
|
|
256
|
+
|
|
257
|
+
| Kind | Description |
|
|
258
|
+
| -------------- | ---------------------------------- |
|
|
259
|
+
| `OFF` | Flag is disabled |
|
|
260
|
+
| `TARGET_MATCH` | User is in the flag's target list |
|
|
261
|
+
| `RULE_MATCH` | User matched a targeting rule |
|
|
262
|
+
| `FALLTHROUGH` | Default rollout (no rules matched) |
|
|
263
|
+
| `ERROR` | Error during evaluation |
|
|
264
|
+
| `UNKNOWN` | Flag not found |
|
|
265
|
+
|
|
266
|
+
### UserContext
|
|
267
|
+
|
|
268
|
+
| Field | Type | Description |
|
|
269
|
+
| ------------ | ------- | ------------------------------- |
|
|
270
|
+
| `id` | `str` | User identifier (required) |
|
|
271
|
+
| `email` | `str?` | User email |
|
|
272
|
+
| `attributes` | `dict?` | Custom attributes for targeting |
|
|
273
|
+
|
|
274
|
+
## Documentation
|
|
275
|
+
|
|
276
|
+
- [Getting Started](../../docs/GETTING-STARTED.md)
|
|
277
|
+
- [Architecture](../../docs/ARCHITECTURE.md)
|
|
278
|
+
- [Production Setup](../../docs/PRODUCTION-SETUP.md)
|
|
279
|
+
|
|
280
|
+
Full documentation: [docs.rollgate.io](https://rollgate.io/docs)
|
|
281
|
+
|
|
282
|
+
## About Rollgate
|
|
283
|
+
|
|
284
|
+
[Rollgate](https://rollgate.io) is a feature management platform that helps teams release features safely with gradual rollouts, user targeting, and instant kill switches.
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT
|
rollgate-1.0.0/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Rollgate Python SDK
|
|
2
|
+
|
|
3
|
+
[](https://github.com/rollgate/sdks/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/rollgate/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Official Python SDK for [Rollgate](https://rollgate.io) - Feature flags made simple.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Python 3.9+
|
|
12
|
+
- httpx >= 0.25.0
|
|
13
|
+
- httpx-sse >= 0.4.0
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install rollgate
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import asyncio
|
|
25
|
+
from rollgate import RollgateClient, RollgateConfig, UserContext
|
|
26
|
+
|
|
27
|
+
async def main():
|
|
28
|
+
# Initialize client
|
|
29
|
+
config = RollgateConfig(api_key="your-api-key")
|
|
30
|
+
client = RollgateClient(config)
|
|
31
|
+
|
|
32
|
+
# Initialize and fetch flags
|
|
33
|
+
await client.init()
|
|
34
|
+
|
|
35
|
+
# Check if feature is enabled
|
|
36
|
+
if client.is_enabled("new-feature"):
|
|
37
|
+
print("New feature is enabled!")
|
|
38
|
+
|
|
39
|
+
# With user targeting
|
|
40
|
+
await client.identify(UserContext(
|
|
41
|
+
id="user-123",
|
|
42
|
+
email="user@example.com",
|
|
43
|
+
attributes={"plan": "pro", "country": "IT"}
|
|
44
|
+
))
|
|
45
|
+
|
|
46
|
+
if client.is_enabled("premium-feature"):
|
|
47
|
+
print("Premium feature is enabled for this user!")
|
|
48
|
+
|
|
49
|
+
# Cleanup
|
|
50
|
+
await client.close()
|
|
51
|
+
|
|
52
|
+
asyncio.run(main())
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Context Manager
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
async with RollgateClient(RollgateConfig(api_key="your-api-key")) as client:
|
|
59
|
+
if client.is_enabled("my-feature"):
|
|
60
|
+
# Feature is enabled
|
|
61
|
+
pass
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from rollgate import (
|
|
68
|
+
RollgateConfig,
|
|
69
|
+
RetryConfig,
|
|
70
|
+
CircuitBreakerConfig,
|
|
71
|
+
CacheConfig,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
config = RollgateConfig(
|
|
75
|
+
api_key="your-api-key",
|
|
76
|
+
base_url="https://api.rollgate.io", # Custom API URL
|
|
77
|
+
refresh_interval_ms=30000, # Polling interval (30s default)
|
|
78
|
+
enable_streaming=False, # Use SSE for real-time updates
|
|
79
|
+
timeout_ms=5000, # Request timeout
|
|
80
|
+
|
|
81
|
+
# Retry configuration
|
|
82
|
+
retry=RetryConfig(
|
|
83
|
+
max_retries=3,
|
|
84
|
+
base_delay_ms=100,
|
|
85
|
+
max_delay_ms=10000,
|
|
86
|
+
jitter_factor=0.1,
|
|
87
|
+
),
|
|
88
|
+
|
|
89
|
+
# Circuit breaker configuration
|
|
90
|
+
circuit_breaker=CircuitBreakerConfig(
|
|
91
|
+
failure_threshold=5,
|
|
92
|
+
recovery_timeout_ms=30000,
|
|
93
|
+
monitoring_window_ms=60000,
|
|
94
|
+
success_threshold=3,
|
|
95
|
+
),
|
|
96
|
+
|
|
97
|
+
# Cache configuration
|
|
98
|
+
cache=CacheConfig(
|
|
99
|
+
ttl_ms=300000, # 5 minutes
|
|
100
|
+
stale_ttl_ms=3600000, # 1 hour
|
|
101
|
+
persist_path="/tmp/rollgate-cache.json", # Optional persistence
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Events
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
client = RollgateClient(config)
|
|
110
|
+
|
|
111
|
+
# Register event callbacks
|
|
112
|
+
client.on("ready", lambda: print("Client ready"))
|
|
113
|
+
client.on("flags_updated", lambda flags: print(f"Flags updated: {flags}"))
|
|
114
|
+
client.on("flag_changed", lambda key, new, old: print(f"{key}: {old} -> {new}"))
|
|
115
|
+
client.on("error", lambda err: print(f"Error: {err}"))
|
|
116
|
+
client.on("circuit_open", lambda *args: print("Circuit breaker opened"))
|
|
117
|
+
client.on("circuit_closed", lambda: print("Circuit breaker closed"))
|
|
118
|
+
|
|
119
|
+
await client.init()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Features
|
|
123
|
+
|
|
124
|
+
### Polling (Default)
|
|
125
|
+
|
|
126
|
+
By default, the SDK polls for flag updates every 30 seconds.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
config = RollgateConfig(
|
|
130
|
+
api_key="your-api-key",
|
|
131
|
+
refresh_interval_ms=30000, # Poll every 30s
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### SSE Streaming
|
|
136
|
+
|
|
137
|
+
Enable Server-Sent Events for real-time flag updates:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
config = RollgateConfig(
|
|
141
|
+
api_key="your-api-key",
|
|
142
|
+
enable_streaming=True,
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Circuit Breaker
|
|
147
|
+
|
|
148
|
+
The SDK includes a circuit breaker to prevent cascading failures:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# Check circuit state
|
|
152
|
+
state = client.circuit_state # CircuitState.CLOSED, OPEN, or HALF_OPEN
|
|
153
|
+
|
|
154
|
+
# Get statistics
|
|
155
|
+
stats = client.get_circuit_stats()
|
|
156
|
+
|
|
157
|
+
# Force reset
|
|
158
|
+
client.reset_circuit()
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Caching
|
|
162
|
+
|
|
163
|
+
Flags are cached locally with stale-while-revalidate support:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# Get cache statistics
|
|
167
|
+
stats = client.get_cache_stats()
|
|
168
|
+
hit_rate = client.get_cache_hit_rate()
|
|
169
|
+
|
|
170
|
+
# Clear cache
|
|
171
|
+
client.clear_cache()
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Error Handling
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from rollgate import (
|
|
178
|
+
RollgateError,
|
|
179
|
+
AuthenticationError,
|
|
180
|
+
NetworkError,
|
|
181
|
+
RateLimitError,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
await client.init()
|
|
186
|
+
except AuthenticationError as e:
|
|
187
|
+
print(f"Invalid API key: {e}")
|
|
188
|
+
except NetworkError as e:
|
|
189
|
+
print(f"Network error: {e}")
|
|
190
|
+
except RateLimitError as e:
|
|
191
|
+
print(f"Rate limited, retry after: {e.retry_after}s")
|
|
192
|
+
except RollgateError as e:
|
|
193
|
+
print(f"Rollgate error: {e}")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## API Reference
|
|
197
|
+
|
|
198
|
+
### RollgateClient
|
|
199
|
+
|
|
200
|
+
| Method | Description |
|
|
201
|
+
| --------------------------------------- | ---------------------------------- |
|
|
202
|
+
| `init(user?)` | Initialize client and fetch flags |
|
|
203
|
+
| `is_enabled(flag_key, default?)` | Check if flag is enabled |
|
|
204
|
+
| `is_enabled_detail(flag_key, default?)` | Check flag with evaluation reason |
|
|
205
|
+
| `get_all_flags()` | Get all flags as dictionary |
|
|
206
|
+
| `identify(user)` | Set user context and refresh flags |
|
|
207
|
+
| `reset()` | Clear user context |
|
|
208
|
+
| `refresh()` | Force refresh flags |
|
|
209
|
+
| `close()` | Cleanup resources |
|
|
210
|
+
|
|
211
|
+
### Evaluation Reasons
|
|
212
|
+
|
|
213
|
+
Get detailed information about why a flag evaluated to a particular value:
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
detail = client.is_enabled_detail("my-flag", False)
|
|
217
|
+
print(detail.value) # bool
|
|
218
|
+
print(detail.reason.kind) # "OFF", "TARGET_MATCH", "RULE_MATCH", "FALLTHROUGH", "ERROR", "UNKNOWN"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Reason kinds:
|
|
222
|
+
|
|
223
|
+
| Kind | Description |
|
|
224
|
+
| -------------- | ---------------------------------- |
|
|
225
|
+
| `OFF` | Flag is disabled |
|
|
226
|
+
| `TARGET_MATCH` | User is in the flag's target list |
|
|
227
|
+
| `RULE_MATCH` | User matched a targeting rule |
|
|
228
|
+
| `FALLTHROUGH` | Default rollout (no rules matched) |
|
|
229
|
+
| `ERROR` | Error during evaluation |
|
|
230
|
+
| `UNKNOWN` | Flag not found |
|
|
231
|
+
|
|
232
|
+
### UserContext
|
|
233
|
+
|
|
234
|
+
| Field | Type | Description |
|
|
235
|
+
| ------------ | ------- | ------------------------------- |
|
|
236
|
+
| `id` | `str` | User identifier (required) |
|
|
237
|
+
| `email` | `str?` | User email |
|
|
238
|
+
| `attributes` | `dict?` | Custom attributes for targeting |
|
|
239
|
+
|
|
240
|
+
## Documentation
|
|
241
|
+
|
|
242
|
+
- [Getting Started](../../docs/GETTING-STARTED.md)
|
|
243
|
+
- [Architecture](../../docs/ARCHITECTURE.md)
|
|
244
|
+
- [Production Setup](../../docs/PRODUCTION-SETUP.md)
|
|
245
|
+
|
|
246
|
+
Full documentation: [docs.rollgate.io](https://rollgate.io/docs)
|
|
247
|
+
|
|
248
|
+
## About Rollgate
|
|
249
|
+
|
|
250
|
+
[Rollgate](https://rollgate.io) is a feature management platform that helps teams release features safely with gradual rollouts, user targeting, and instant kill switches.
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rollgate"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python SDK for Rollgate feature flags"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Rollgate", email = "hello@rollgate.io" }
|
|
13
|
+
]
|
|
14
|
+
keywords = ["feature-flags", "feature-toggles", "rollgate", "sdk"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
requires-python = ">=3.9"
|
|
30
|
+
dependencies = [
|
|
31
|
+
"httpx>=0.25.0",
|
|
32
|
+
"httpx-sse>=0.4.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=7.0.0",
|
|
38
|
+
"pytest-asyncio>=0.21.0",
|
|
39
|
+
"pytest-cov>=4.0.0",
|
|
40
|
+
"respx>=0.20.0",
|
|
41
|
+
"mypy>=1.0.0",
|
|
42
|
+
"ruff>=0.1.0",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://rollgate.io"
|
|
47
|
+
Documentation = "https://rollgate.io/docs/sdk/python"
|
|
48
|
+
Repository = "https://github.com/thejord-it/rollgate"
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.wheel]
|
|
51
|
+
packages = ["rollgate"]
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
asyncio_mode = "auto"
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
|
|
57
|
+
[tool.mypy]
|
|
58
|
+
python_version = "3.9"
|
|
59
|
+
strict = true
|
|
60
|
+
warn_return_any = true
|
|
61
|
+
warn_unused_configs = true
|
|
62
|
+
|
|
63
|
+
[tool.ruff]
|
|
64
|
+
target-version = "py39"
|
|
65
|
+
line-length = 100
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint]
|
|
68
|
+
select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"]
|