Skip to content

Python: Add OpenFeature provider#156

Open
msiebert wants to merge 16 commits intomasterfrom
msiebert-openfeature-provider
Open

Python: Add OpenFeature provider#156
msiebert wants to merge 16 commits intomasterfrom
msiebert-openfeature-provider

Conversation

@msiebert
Copy link
Copy Markdown

Summary

  • Adds openfeature-provider/ package implementing OpenFeature provider
  • Wraps local or remote flags provider, identity check for flag detection
  • 29 tests, all passing

Design

  • targetingKey not special, STATIC reason, three error codes, shutdown no-op

🤖 Generated with Claude Code

Implement an OpenFeature provider that wraps the Mixpanel Python SDK's
local or remote feature flags provider, enabling standardized feature
flag evaluation via the OpenFeature API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@msiebert msiebert self-assigned this Mar 19, 2026
msiebert and others added 3 commits March 20, 2026 11:05
Adds variant key passthrough, SDK exception handling (try/except),
null variant key tests, and context forwarding to evaluation calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stop mapping targeting_key to distinct_id and nesting attributes under
custom_properties. All context attributes are now passed through flat,
matching the Java and Go SDKs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- shutdown() now delegates to underlying flags provider
- Add shutdown() to LocalFeatureFlagsProvider and RemoteFeatureFlagsProvider
- Explicitly pass report_exposure=True to get_variant()
- Rename reportExposure to report_exposure in RemoteFeatureFlagsProvider
- Add context value unwrapping with whole-number float→int conversion
- Update test mock signatures for report_exposure parameter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@msiebert msiebert changed the title Add OpenFeature provider Python: Add OpenFeature provider Mar 31, 2026
msiebert and others added 6 commits April 1, 2026 12:49
Six call sites in remote_feature_flags.py used logging.X() (root
logger) instead of logger.X() (module logger), breaking independent
log configuration. Consolidate lowercase_keys_and_values and
lowercase_only_leaf_nodes into single _casefold_recursive method.
Fix incorrect return type annotation and remove unnecessary f-string
prefixes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Python's bool is a subclass of int, so isinstance(True, int) returns
True. Add early guard to reject bool values when resolving integer or
float types, returning TYPE_MISMATCH instead of silently coercing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve conflicts in local_feature_flags.py and remote_feature_flags.py:
- Keep consolidated _casefold_recursive method from branch
- Adopt master's %-formatting for logger calls and dict[] type hints
- Keep branch's snake_case report_exposure parameter naming

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restore master's lowercase_keys_and_values/lowercase_only_leaf_nodes
methods instead of consolidated _casefold_recursive, and restore
master's reportExposure camelCase naming on remote provider.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The openfeature-provider sub-project has its own pyproject.toml and
dependencies. Root pytest was collecting its tests (failing on missing
openfeature SDK), and root ruff was linting it without proper config.

- Add --ignore=openfeature-provider to pytest addopts
- Add extend-exclude for openfeature-provider in ruff config
- Auto-format provider.py and test_provider.py with ruff

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

❌ Patch coverage is 97.60638% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.87%. Comparing base (67424eb) to head (b6719df).

Files with missing lines Patch % Lines
...ture-provider/src/mixpanel_openfeature/provider.py 92.20% 3 Missing and 3 partials ⚠️
mixpanel/flags/local_feature_flags.py 33.33% 2 Missing ⚠️
mixpanel/flags/remote_feature_flags.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #156      +/-   ##
==========================================
+ Coverage   94.20%   94.87%   +0.66%     
==========================================
  Files           9       12       +3     
  Lines        1554     1930     +376     
  Branches      100      116      +16     
==========================================
+ Hits         1464     1831     +367     
- Misses         56       62       +6     
- Partials       34       37       +3     
Flag Coverage Δ
openfeature-provider 53.78% <97.60%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…ant call

Let the underlying flags provider use its default behavior rather than
explicitly passing report_exposure=True.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@msiebert msiebert marked this pull request as ready for review April 2, 2026 21:57
@msiebert msiebert requested review from a team and rahul-mixpanel April 2, 2026 21:57
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@efahk efahk self-requested a review April 2, 2026 23:50
msiebert and others added 2 commits April 2, 2026 19:55
Codecov patch/project checks were failing because the openfeature-provider
tests weren't generating or uploading coverage data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The CI coverage upload needs pytest-cov installed to use --cov flags.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
class MixpanelProvider(AbstractProvider):
"""An OpenFeature provider backed by a Mixpanel feature flags provider."""

def __init__(self, flags_provider: typing.Any) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since the primary purpose of the openfeatureprovider is to not have to worry about handling the internals of the providers behind the scene, I think the init should just take in properties we need to initialize the mixpanel library and the requested flags provider

provider = MixpanelProvider(token: str, evaluation_mode: Local|Remote, ..., ..., ... ) 

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is a really good point, and for a lot of other OpenFeature providers, it looks like this is how things are handled. I have a couple concerns though (kind of more in a general sense, and not specifically with Python):

  1. For the client side SDKs, we need to be able to call mixpanel.identify to get all the distinctId stuff in
  2. If we create a Mixpanel client within the OpenFeature provider, mixpanel.track calls on the instance outside the provider are no longer passing through the instance that the OpenFeature provider is using, which breaks runtime events' ability to detect those triggers. This will break client SDKs, and will preclude us from implementing the feature on server side SDKs, and incur the wrath of Neha.

I personally don't think it's too painful to pass in the mixpanel client, since it's just one more line, but I do understand that this deviates from the usual pattern. What do you think @efahk ?

msiebert and others added 2 commits April 3, 2026 19:09
- Add PyPy 3.9 and 3.11 to OpenFeature provider CI test matrix
- Add error_message to TYPE_MISMATCH flag resolution details
- Constrain mixpanel dependency to >=2,<3
- Add ruff linting and formatting config mirroring main project
- Fix lint violations (imports, formatting)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dependency was pinned to mixpanel>=2,<3 but the current SDK version
is 5.1.0, causing CI to fail when pip couldn't resolve the constraint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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