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.
@@ -0,0 +1 @@
1
+ __pycache__/
@@ -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
+ [![CI](https://github.com/rollgate/sdks/actions/workflows/ci.yml/badge.svg)](https://github.com/rollgate/sdks/actions/workflows/ci.yml)
38
+ [![PyPI version](https://img.shields.io/pypi/v/rollgate.svg)](https://pypi.org/project/rollgate/)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
@@ -0,0 +1,254 @@
1
+ # Rollgate Python SDK
2
+
3
+ [![CI](https://github.com/rollgate/sdks/actions/workflows/ci.yml/badge.svg)](https://github.com/rollgate/sdks/actions/workflows/ci.yml)
4
+ [![PyPI version](https://img.shields.io/pypi/v/rollgate.svg)](https://pypi.org/project/rollgate/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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"]