Skip to content

Conversation

@nicklasl
Copy link
Member

@nicklasl nicklasl commented Jan 26, 2026

Summary

Add Python OpenFeature provider for Confidence feature flags with WASM-based local resolution.

Components Implemented

  • WasmResolver (wasm_resolver.py): Low-level WASM runtime using wasmtime for flag resolution
  • LocalResolver (local_resolver.py): Crash recovery wrapper with automatic WASM reload and state restoration
  • StateFetcher (state_fetcher.py): CDN state fetching with ETag caching and 304 handling
  • FlagLogger (flag_logger.py): Async gRPC flag logging with ThreadPoolExecutor
  • MaterializationStore (materialization.py): Protocol and implementations for sticky assignments and segment inclusion
  • ConfidenceProvider (provider.py): Main OpenFeature provider implementation

Features

  • Local WASM-based flag resolution for low latency
  • Background state polling from CDN (configurable interval)
  • Async flag/assignment logging via gRPC
  • Thread-safe resolver access with Lock
  • Materialization store support (sticky assignments and segment inclusion)
  • Remote materialization store for server-side storage
  • Crash recovery with automatic WASM reload and state restoration
  • Support for nested flag paths (e.g., my-flag.nested.value)
  • Logging via Python standard logging module

Build

  • Docker stages: openfeature-provider-python.{test,test_e2e,lint,build,artifact}
  • WASM built from source (not committed), copied from wasm-rust-guest.artifact in Docker

Usage

from openfeature import api
from confidence_openfeature import ConfidenceProvider

provider = ConfidenceProvider(client_secret="your-client-secret")
api.set_provider(provider)

client = api.get_client()
result = client.get_boolean_value("my-flag.enabled", default_value=False)

Logging

Configure logging to see provider activity:

import logging
logging.getLogger("confidence_openfeature").setLevel(logging.DEBUG)

Test plan

  • 65 unit tests passing (make test)
  • 6 E2E tests passing (make test-e2e)
  • Lint clean (make lint - black, flake8, mypy)
  • Docker build for all stages

🤖 Generated with Claude Code

nicklasl and others added 11 commits January 26, 2026 11:56
Implement Python OpenFeature provider for Confidence feature flags with
WASM-based local resolution.

Components:
- WasmResolver: Low-level WASM runtime using wasmtime
- LocalResolver: Crash recovery wrapper with state restoration
- StateFetcher: CDN state fetching with ETag caching
- FlagLogger: Async gRPC flag logging
- MaterializationStore: Sticky assignment support
- ConfidenceProvider: Main OpenFeature provider implementation

Features:
- Local WASM-based flag resolution for low latency
- Background state polling from CDN
- Async flag/assignment logging via gRPC
- Thread-safe resolver access
- Materialization store support (sticky assignments)
- Crash recovery with automatic WASM reload

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Match Go e2e test pattern with hardcoded constants.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add Docker stages for building, testing, and linting the Python
OpenFeature provider. Updates the Makefile to support Docker builds
with proper WASM file handling.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add timeout=30.0 to all gRPC calls (required by server)
- Add InclusionReadOp and InclusionReadResult for segment targeting
- Update RemoteMaterializationStore to handle both op types
- Fix e2e tests to match Go test patterns (remove tutorial-feature)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- WASM module is now built from wasm/rust-guest source
- Removed committed WASM binary from git tracking
- Added resources/wasm/ to .gitignore

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Replace Optional[Any] with proper types:
- http_client: Optional[httpx.Client]
- grpc_channel: Optional[grpc.Channel]
- state_fetcher: Optional[StateFetcher]
- flag_logger: Optional[FlagLogger]

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…dence

- PyPI name: confidence-local-openfeature-provider
- Import: from confidence import ConfidenceProvider
- Aligns with Go provider's package naming

Co-Authored-By: Claude Opus 4.5 <[email protected]>
DEFAULT_ASSIGN_POLL_INTERVAL = 0.1


def _load_wasm_from_resources() -> bytes:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 3 fallbacks due to Python's evolving resource loading APIs:

  1. importlib.resources.files() (Python 3.9+)
    - The modern, recommended approach
    - Works with installed packages and namespace packages
    - Returns a traversable object
  2. pkg_resources (setuptools legacy)
    - Old approach, being deprecated (see the warning in tests)
    - Still needed for older Python/setuptools combinations
    - Works when importlib.resources doesn't find the file
  3. Development fallback (Path-based)
    - For local development with pip install -e .
    - When WASM is in resources/wasm/ but not yet packaged into the wheel
    - Useful during development before running make build

This complexity exists because:

  • Python's resource loading has changed significantly between versions
  • Editable installs (pip install -e .) behave differently than regular installs
  • The WASM file location differs between development and packaged states

We could simplify this by requiring Python 3.11+ (which has stable importlib.resources), but the current code supports Python 3.9+.

nicklasl and others added 5 commits January 28, 2026 15:56
- Add _status field initialized to NOT_READY
- Add get_status() method
- Set READY after successful initial state load
- Set NOT_READY on shutdown
- Recovery: if initial load fails, retry with 1s interval until success
- Emit PROVIDER_READY event on status transition

Matches Java provider behavior.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- StateFetcher.fetch() now returns (state, account_id, changed)
- changed=False when 304 Not Modified, True otherwise
- _state_poll_loop only updates resolver when state changed

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Assignment flushing is already handled by the background thread.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Reduces code duplication across resolve_*_details methods.
Adds comprehensive type mismatch tests.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants