Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7e267c8
Add OpenFeature provider package
msiebert Mar 19, 2026
5135505
Add missing OpenFeature provider tests and fix provider gaps
msiebert Mar 20, 2026
ece5e75
Fix OpenFeature context passthrough to pass attributes as-is
msiebert Mar 23, 2026
124f6b1
Align OpenFeature provider with server provider spec
msiebert Mar 23, 2026
a34bf6e
Fix root logger misuse and consolidate duplicate recursive methods
msiebert Apr 1, 2026
001dfab
Fix bool subclass bug in numeric type resolution
msiebert Apr 1, 2026
a511391
Merge origin/master into msiebert-openfeature-provider
msiebert Apr 2, 2026
6a32f84
Revert out-of-scope changes from merge conflict resolution
msiebert Apr 2, 2026
571af47
Fix docstrings to match master's reportExposure naming
msiebert Apr 2, 2026
e601641
Fix CI: exclude openfeature-provider from root lint and test collection
msiebert Apr 2, 2026
1c40800
Remove report_exposure parameter from OpenFeature provider's get_vari…
msiebert Apr 2, 2026
baa48db
Add CI job for OpenFeature provider tests
msiebert Apr 2, 2026
247b539
Add coverage upload for OpenFeature provider CI job
msiebert Apr 3, 2026
3c721d5
Add pytest-cov to OpenFeature provider test dependencies
msiebert Apr 3, 2026
6058162
Address PR review feedback for OpenFeature provider
msiebert Apr 4, 2026
b6719df
Fix mixpanel version constraint in OpenFeature provider
msiebert Apr 4, 2026
f8553ca
Add simplified class methods for OpenFeature provider
msiebert Apr 6, 2026
90c87e0
Use TARGETING_MATCH reason for successful evaluations and DEFAULT for…
msiebert Apr 7, 2026
ac74911
Add release documentation for OpenFeature provider
msiebert Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,31 @@ jobs:
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: mixpanel/mixpanel-python
slug: mixpanel/mixpanel-python

test-openfeature-provider:
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', 'pypy3.9', 'pypy3.11']

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install -e ./openfeature-provider[test]
- name: Run OpenFeature provider tests
run: |
pytest --cov --cov-branch --cov-report=xml openfeature-provider/tests/
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: mixpanel/mixpanel-python
flags: openfeature-provider
4 changes: 4 additions & 0 deletions mixpanel/flags/local_feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,10 @@ def _track_exposure(
async def __aenter__(self):
return self

def shutdown(self):
self.stop_polling_for_definitions()
self._sync_client.close()

def __enter__(self):
return self

Expand Down
3 changes: 3 additions & 0 deletions mixpanel/flags/remote_feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ def _lookup_flag_in_response(
)
return fallback_value, True

def shutdown(self):
self._sync_client.close()

def __enter__(self):
return self

Expand Down
45 changes: 45 additions & 0 deletions openfeature-provider/RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Releasing the OpenFeature Provider

The OpenFeature provider (`mixpanel-openfeature`) is published to PyPI independently from the core SDK.

## Prerequisites

- Python 3.9+
- `build` and `twine` packages: `pip install build twine`
- A PyPI API token with permission to upload to the `mixpanel-openfeature` project
- Create one at https://pypi.org/manage/account/token/
- For the first upload, you'll need an account-scoped token (project-scoped tokens can only be created after the project exists on PyPI)

## Releasing

1. Update the version in `pyproject.toml`

2. Build the package:
```bash
cd openfeature-provider
python -m build
```

3. Verify the built artifacts look correct:
```bash
ls dist/
# Should show: mixpanel_openfeature-<version>-py3-none-any.whl
# mixpanel_openfeature-<version>.tar.gz
```

4. Upload to PyPI:
```bash
python -m twine upload dist/*
```
Twine will prompt for credentials. Use `__token__` as the username and your API token as the password. Alternatively, configure `~/.pypirc`:
```ini
[pypi]
username = __token__
password = pypi-<your-token>
```

5. Verify at https://pypi.org/project/mixpanel-openfeature/

## Versioning

The OpenFeature provider is versioned independently from the core SDK. The core SDK dependency version is pinned in `pyproject.toml` (`mixpanel>=5.1.0,<6`) — update it when the provider needs features from a newer core SDK release.
98 changes: 98 additions & 0 deletions openfeature-provider/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mixpanel-openfeature"
version = "0.1.0"
description = "OpenFeature provider for the Mixpanel Python SDK"
license = "Apache-2.0"
authors = [
{name = "Mixpanel, Inc.", email = "dev@mixpanel.com"},
]
requires-python = ">=3.9"
dependencies = [
"mixpanel>=5.1.0,<6",
"openfeature-sdk>=0.7.0",
]

[project.optional-dependencies]
test = [
"pytest>=8.4.1",
"pytest-asyncio>=0.23.0",
"pytest-cov>=6.0",
]

[tool.setuptools.packages.find]
where = ["src"]

Comment thread
msiebert marked this conversation as resolved.
[tool.pytest.ini_options]
asyncio_mode = "auto"

# --- Ruff configuration (mirrors main project) ---

[tool.ruff]
target-version = "py39"
line-length = 88

[tool.ruff.lint]
select = ["ALL"]
ignore = [
# --- Rule conflicts ---
"D203", # conflicts with D211 (no-blank-line-before-class)
"D213", # conflicts with D212 (multi-line-summary-first-line)
"COM812", # conflicts with ruff formatter
"ISC001", # conflicts with ruff formatter

# --- Type annotations (separate effort) ---
"ANN", # all annotation rules

# --- Docstrings (separate effort) ---
"D100", # undocumented-public-module
"D101", # undocumented-public-class
"D102", # undocumented-public-method
"D103", # undocumented-public-function
"D104", # undocumented-public-package
"D105", # undocumented-magic-method
"D107", # undocumented-public-init

# --- Boolean arguments (public API) ---
"FBT", # boolean-type-hint / boolean-default / boolean-positional

# --- TODO/FIXME enforcement ---
"TD002", # missing-todo-author
"TD003", # missing-todo-link
"FIX001", # line-contains-fixme
"FIX002", # line-contains-todo

# --- Exception message style ---
"EM101", # raw-string-in-exception
"EM103", # dot-format-in-exception
"TRY003", # raise-vanilla-args

# --- Other pragmatic exclusions ---
"PLR0913", # too-many-arguments
"PLR0911", # too-many-return-statements (_resolve has many type-check branches)
"E501", # line-too-long (formatter handles code)
"FA100", # future-rewritable-type-annotation
"BLE001", # blind-exception (catching Exception in flag resolution is intentional)
]

[tool.ruff.lint.per-file-ignores]
"tests/*.py" = [
"S101", # assert
"S105", # hardcoded-password-string (test fixtures)
"S106", # hardcoded-password-func-arg
"SLF001", # private-member-access
"PLR2004", # magic-value-comparison
"D", # all docstring rules
"PT018", # pytest-composite-assertion
"INP001", # implicit-namespace-package (no __init__.py in tests)
"ARG", # unused arguments (lambda stubs in mocks)
]

[tool.ruff.lint.isort]
known-first-party = ["mixpanel", "mixpanel_openfeature"]

[tool.ruff.lint.pydocstyle]
convention = "google"
3 changes: 3 additions & 0 deletions openfeature-provider/src/mixpanel_openfeature/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .provider import MixpanelProvider

__all__ = ["MixpanelProvider"]
Loading
Loading