-
-
Notifications
You must be signed in to change notification settings - Fork 204
feat(python)!: New python provider with inprocess support #4861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
f61df4a
feat(python): New python provider with inprocess support
thomaspoignant 6c2d65e
move ci to uv
thomaspoignant 0c5b53c
use read and write wasmtime functions
thomaspoignant 4693892
Merge branch 'main' into python-wasm-provider
thomaspoignant 5e5c718
Merge branch 'main' into python-wasm-provider
thomaspoignant 1b10f1e
fix(python-provider): run async flag resolution in thread via asyncio…
thomaspoignant 6efadbe
chore(python-provider): remove unused deps (pylru, websocket-client, …
thomaspoignant 64079ba
fix(python-provider): default unknown flags to trackable in inprocess…
thomaspoignant cc40e02
feat(python-provider): add WASM store pool for concurrent in-process …
thomaspoignant 5857636
chore(python-provider): default wasm pool size to 10
thomaspoignant 32fe79c
HEAD
thomaspoignant 739531c
fix(python-provider): make event_publisher tests deterministic for CI
thomaspoignant 0891714
docs: update AGENTS.md to reflect current codebase structure
thomaspoignant 0ed46d3
Merge branch 'main' into python-wasm-provider
thomaspoignant bd06063
test(python-provider): add in-process evaluation tests
thomaspoignant c5da61d
move mock files
thomaspoignant b4b5e43
Merge branch 'main' into python-wasm-provider
thomaspoignant ea0d224
manage all errors
thomaspoignant 65598ec
set as prerelease
thomaspoignant 6936e5e
Merge branch 'main' into python-wasm-provider
thomaspoignant 82fe44e
fix: remove @staticmethod from _raise_for_error_code to fix missing '…
thomaspoignant File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,7 +80,8 @@ | |
| "extra-files": [ | ||
| "pyproject.toml", | ||
| "README.md" | ||
| ] | ||
| ], | ||
| "prerelease": true | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [submodule "openfeature/providers/python-provider/wasm-releases"] | ||
| path = openfeature/providers/python-provider/wasm-releases | ||
| url = https://github.com/go-feature-flag/wasm-releases.git |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| # GO Feature Flag Python Provider | ||
|
|
||
| OpenFeature Python provider for [GO Feature Flag](https://gofeatureflag.org). | ||
|
|
||
| ## Project Overview | ||
|
|
||
| This is a Python package that implements the OpenFeature provider interface to connect to a GO Feature Flag relay proxy. It enables Python applications to evaluate feature flags using the OpenFeature SDK. | ||
|
|
||
| ## Architecture | ||
|
|
||
| ``` | ||
| gofeatureflag_python_provider/ | ||
| ├── __init__.py # Package exports | ||
| ├── provider.py # Main GoFeatureFlagProvider class (AbstractProvider implementation) | ||
| ├── options.py # GoFeatureFlagOptions configuration class | ||
| ├── hooks/ # OpenFeature hooks | ||
| │ ├── __init__.py | ||
| │ ├── data_collector.py # Hook for collecting flag evaluation usage data | ||
| │ └── enrich_evaluation_context.py # Hook that adds gofeatureflag metadata to context before evaluation | ||
| ├── metadata.py # Provider metadata | ||
| ├── request_data_collector.py # Data models for usage collection | ||
| ├── request_flag_evaluation.py # Request models for flag evaluation API calls | ||
| └── response_flag_evaluation.py # Response models for flag evaluation API calls | ||
|
|
||
| tests/ | ||
| ├── test_gofeatureflag_python_provider.py # Main provider tests | ||
| ├── test_enrich_evaluation_context_hook.py # EnrichEvaluationContextHook tests | ||
| ├── test_provider_graceful_exit.py # Shutdown/cleanup tests | ||
| ├── test_websocket_cache_invalidation.py # WebSocket cache invalidation tests | ||
| ├── mock_responses/ # JSON mock responses for testing | ||
| ├── config.goff.yaml # Test flag configuration | ||
| └── docker-compose.yml # Test infrastructure | ||
| ``` | ||
|
|
||
| ## Key Components | ||
|
|
||
| ### GoFeatureFlagProvider (`provider.py`) | ||
| - Extends `AbstractProvider` from OpenFeature SDK | ||
| - Implements all resolve methods: `resolve_boolean_details`, `resolve_string_details`, `resolve_integer_details`, `resolve_float_details`, `resolve_object_details` | ||
| - Uses `generic_go_feature_flag_resolver` for all flag types | ||
| - Features: | ||
| - LRU cache for flag evaluations (`pylru`) | ||
| - WebSocket connection for cache invalidation | ||
| - Data collection for usage analytics | ||
|
|
||
| ### GoFeatureFlagOptions (`options.py`) | ||
| Configuration options: | ||
| - `endpoint` (required): URL of the GO Feature Flag relay proxy | ||
| - `cache_size`: Max cached flags (default: 10000) | ||
| - `data_flush_interval`: Interval to flush usage data in ms (default: 60000) | ||
| - `disable_data_collection`: Turn off usage tracking (default: false) | ||
| - `reconnect_interval`: WebSocket reconnect interval in seconds (default: 60) | ||
| - `disable_cache_invalidation`: Disable WebSocket cache invalidation (default: false) | ||
| - `api_key`: API key for authenticated requests | ||
| - `exporter_metadata`: Custom metadata for evaluation events | ||
| - `debug`: Enable debug logging (default: false) | ||
| - `urllib3_pool_manager`: Custom HTTP client | ||
|
|
||
| ### DataCollectorHook (`hooks/data_collector.py`) | ||
| - OpenFeature Hook implementation for collecting usage data | ||
| - Tracks flag evaluations via `after()` and `error()` hooks | ||
| - Flushes data to `/v1/data/collector` endpoint periodically | ||
|
|
||
| ### EnrichEvaluationContextHook (`hooks/enrich_evaluation_context.py`) | ||
| - Enriches the evaluation context with a `gofeatureflag` attribute (from `exporter_metadata`) before flag resolution | ||
| - Used by the relay proxy for analytics or filtering; registered automatically by the provider | ||
|
|
||
| ## Development | ||
|
|
||
| ### Prerequisites | ||
| - Python 3.9+ | ||
| - uv (package manager) | ||
|
|
||
| ### Setup | ||
| ```bash | ||
| # Install dependencies | ||
| uv sync | ||
|
|
||
| # Run a command in the virtual environment | ||
| uv run <command> | ||
| ``` | ||
|
|
||
| ### Running Tests | ||
| ```bash | ||
| # Run all tests | ||
| uv run pytest | ||
|
|
||
| # Run specific test file | ||
| uv run pytest tests/test_gofeatureflag_python_provider.py | ||
|
|
||
| # Run with verbose output | ||
| uv run pytest -v | ||
| ``` | ||
|
|
||
| ### Code Style | ||
| ```bash | ||
| # Format code with black | ||
| uv run black gofeatureflag_python_provider tests | ||
| ``` | ||
|
|
||
| ## Key Patterns | ||
|
|
||
| ### Pydantic Models | ||
| - All data classes extend Pydantic `BaseModel` for validation | ||
| - Request/response models use `model_dump_json()` for serialization | ||
| - Use `model_validate_json()` for deserialization | ||
|
|
||
| ### HTTP Communication | ||
| - Uses `urllib3.PoolManager` for HTTP requests | ||
| - POST to `/v1/feature/{flag_key}/eval` for flag evaluation | ||
| - POST to `/v1/data/collector` for usage data | ||
| - WebSocket at `/ws/v1/flag/change` for cache invalidation | ||
|
|
||
| ### Caching Strategy | ||
| - LRU cache keyed by `{flag_key}:{evaluation_context_hash()}` | ||
| - Cache cleared on WebSocket message (flag config changed) | ||
| - Set `cacheable` field in response determines if result is cached | ||
|
|
||
| ### Error Handling | ||
| - `FlagNotFoundError`: Flag doesn't exist (404) | ||
| - `InvalidContextError`: Invalid evaluation context (400) | ||
| - `TypeMismatchError`: Response type doesn't match expected type | ||
| - `GeneralError`: Other errors (500+) | ||
|
|
||
| ## API Reference | ||
|
|
||
| The provider communicates with the GO Feature Flag relay proxy: | ||
|
|
||
| | Endpoint | Method | Purpose | | ||
| |----------|--------|---------| | ||
| | `/v1/feature/{flag}/eval` | POST | Evaluate a flag | | ||
| | `/v1/data/collector` | POST | Send usage data | | ||
| | `/ws/v1/flag/change` | WebSocket | Cache invalidation notifications | | ||
|
|
||
| ## Dependencies | ||
|
|
||
| Core: | ||
| - `openfeature-sdk`: OpenFeature Python SDK | ||
| - `pydantic`: Data validation | ||
| - `urllib3`: HTTP client | ||
| - `pylru`: LRU cache implementation | ||
| - `websocket-client`: WebSocket support | ||
| - `rel`: WebSocket reconnection handling | ||
|
|
||
| Dev: | ||
| - `pytest`: Testing framework | ||
| - `black`: Code formatter | ||
| - `pytest-docker`: Docker-based integration tests | ||
|
thomaspoignant marked this conversation as resolved.
thomaspoignant marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| @AGENTS.md |
Empty file.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.