diff --git a/providers/openfeature-provider-flagd/pyproject.toml b/providers/openfeature-provider-flagd/pyproject.toml index 2c1c1b3a..bb5d4d83 100644 --- a/providers/openfeature-provider-flagd/pyproject.toml +++ b/providers/openfeature-provider-flagd/pyproject.toml @@ -35,6 +35,9 @@ dev = [ "coverage[toml]>=7.10.0,<8.0.0", "grpcio-health-checking>=1.82.1,<2.0.0", "mypy>=1.18.0,<2.0.0", + # The tests call `api.set_provider_and_wait`, which arrived in 0.10.0. The + # runtime floor stays at 0.8.2 because the provider itself needs nothing newer. + "openfeature-sdk>=0.10.0", "poethepoet>=0.37.0", "pytest>=9.0.0,<10.0.0", "pytest-bdd>=8.1.0,<9.0.0", diff --git a/providers/openfeature-provider-flagd/tests/test_errors.py b/providers/openfeature-provider-flagd/tests/test_errors.py index e335fc1c..c35e228e 100644 --- a/providers/openfeature-provider-flagd/tests/test_errors.py +++ b/providers/openfeature-provider-flagd/tests/test_errors.py @@ -1,3 +1,4 @@ +import contextlib import os import time @@ -7,13 +8,19 @@ from openfeature.contrib.provider.flagd import FlagdProvider from openfeature.contrib.provider.flagd.config import ResolverType from openfeature.evaluation_context import EvaluationContext -from openfeature.event import ProviderEvent -from openfeature.exception import ErrorCode +from openfeature.exception import ( + ErrorCode, + OpenFeatureError, + ProviderNotReadyError, +) from openfeature.flag_evaluation import Reason def create_client(provider: FlagdProvider): - api.set_provider(provider) + # These flag files are deliberately broken, so init may fail; what the + # evaluation returns afterwards is the assertion. + with contextlib.suppress(OpenFeatureError): + api.set_provider_and_wait(provider) return api.get_client() @@ -119,23 +126,15 @@ def test_flag_disabled(): @pytest.mark.parametrize("wait", (500, 250)) def test_grpc_sync_fail_deadline(wait: int): - init_failed = False - - def fail(*args, **kwargs): - nonlocal init_failed - init_failed = True - - api.get_client().add_handler(ProviderEvent.PROVIDER_ERROR, fail) - t = time.time() - api.set_provider( - FlagdProvider( - resolver_type=ResolverType.IN_PROCESS, - port=99999, # dead port to test failure - deadline_ms=wait, + with pytest.raises(ProviderNotReadyError): + api.set_provider_and_wait( + FlagdProvider( + resolver_type=ResolverType.IN_PROCESS, + port=99999, # dead port to test failure + deadline_ms=wait, + ) ) - ) elapsed = time.time() - t assert abs(elapsed - wait * 0.001) < 0.17 - assert init_failed diff --git a/providers/openfeature-provider-flagd/tests/test_metadata.py b/providers/openfeature-provider-flagd/tests/test_metadata.py index 939af96f..ef8331c5 100644 --- a/providers/openfeature-provider-flagd/tests/test_metadata.py +++ b/providers/openfeature-provider-flagd/tests/test_metadata.py @@ -1,3 +1,4 @@ +import contextlib import os import time from time import sleep @@ -11,7 +12,7 @@ _validate_metadata, ) from openfeature.event import EventDetails, ProviderEvent -from openfeature.exception import ErrorCode, ParseError +from openfeature.exception import ErrorCode, OpenFeatureError, ParseError def create_client(file_name): @@ -21,7 +22,10 @@ def create_client(file_name): offline_flag_source_path=f"{path}/{file_name}", ) - api.set_provider(provider) + # Some of these flag files are deliberately broken, so init may fail; what + # the evaluation returns afterwards is the assertion. + with contextlib.suppress(OpenFeatureError): + api.set_provider_and_wait(provider) return api.get_client() diff --git a/providers/openfeature-provider-unleash/src/openfeature/contrib/provider/unleash/__init__.py b/providers/openfeature-provider-unleash/src/openfeature/contrib/provider/unleash/__init__.py index 8c0d6fef..bc5985d0 100644 --- a/providers/openfeature-provider-unleash/src/openfeature/contrib/provider/unleash/__init__.py +++ b/providers/openfeature-provider-unleash/src/openfeature/contrib/provider/unleash/__init__.py @@ -136,18 +136,6 @@ def _unleash_event_callback(self, event: BaseEvent) -> None: self._status = ProviderStatus.READY self._event_manager.handle_unleash_event(event) - def track( - self, - event_name: str, - event_details: dict | None = None, - ) -> None: - """No-op tracking method. - - Tracking is not implemented for this provider. Per the OpenFeature spec, - when the provider doesn't support tracking, client.track calls should no-op. - """ - return None - def _build_unleash_context( self, evaluation_context: EvaluationContext | None = None ) -> dict[str, Any] | None: diff --git a/providers/openfeature-provider-unleash/tests/test_provider.py b/providers/openfeature-provider-unleash/tests/test_provider.py index c47eb21d..ea9902c8 100644 --- a/providers/openfeature-provider-unleash/tests/test_provider.py +++ b/providers/openfeature-provider-unleash/tests/test_provider.py @@ -119,6 +119,11 @@ def test_unleash_provider_all_methods_implemented(): assert hasattr(provider, "add_handler") assert hasattr(provider, "remove_handler") assert hasattr(provider, "track") + # Tracking is not implemented, so track is inherited from + # AbstractProvider and no-ops. Called the way OpenFeatureClient.track + # calls it -- positionally -- since a narrower override raises + # TypeError there. + assert provider.track("some-event", EvaluationContext(), None) is None provider.shutdown() diff --git a/uv.lock b/uv.lock index 3168e251..9cfe32d6 100644 --- a/uv.lock +++ b/uv.lock @@ -1868,7 +1868,7 @@ dev = [ [[package]] name = "openfeature-provider-flagd" -version = "0.5.1" +version = "0.5.2" source = { editable = "providers/openfeature-provider-flagd" } dependencies = [ { name = "cachebox" }, @@ -1885,6 +1885,7 @@ dev = [ { name = "coverage", extra = ["toml"] }, { name = "grpcio-health-checking" }, { name = "mypy" }, + { name = "openfeature-sdk" }, { name = "poethepoet" }, { name = "pytest" }, { name = "pytest-bdd" }, @@ -1910,6 +1911,7 @@ dev = [ { name = "coverage", extras = ["toml"], specifier = ">=7.10.0,<8.0.0" }, { name = "grpcio-health-checking", specifier = ">=1.82.1,<2.0.0" }, { name = "mypy", specifier = ">=1.18.0,<2.0.0" }, + { name = "openfeature-sdk", specifier = ">=0.10.0" }, { name = "poethepoet", specifier = ">=0.37.0" }, { name = "pytest", specifier = ">=9.0.0,<10.0.0" }, { name = "pytest-bdd", specifier = ">=8.1.0,<9.0.0" }, @@ -2075,11 +2077,11 @@ dev = [ [[package]] name = "openfeature-sdk" -version = "0.8.4" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/08/f6698d0614b8703170117b786bd77b7b0a04f3ee00f19fbe9b360d2dee69/openfeature_sdk-0.8.4.tar.gz", hash = "sha256:66abf71f928ec8c0db1111072bb0ef2635dfbd09510f77f4b548e5d0ea0e6c1a", size = 29676, upload-time = "2025-12-09T07:31:13.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/30/cfc684b7d8314398d476ae8ed515c10db99c4d7f950989db464b4ded12ce/openfeature_sdk-0.10.0.tar.gz", hash = "sha256:938c2540bdea4da3b01ef507517ee636f223a35abaaca845c5587e594151b052", size = 33516, upload-time = "2026-06-01T19:45:35.136Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/80/f6532778188c573cc83790b11abccde717d4c1442514e722d6bb6140e55c/openfeature_sdk-0.8.4-py3-none-any.whl", hash = "sha256:805ba090669798fc343ca9fdcbc56ff0f4b57bf6757533f0854d2021192e620a", size = 35986, upload-time = "2025-12-09T07:31:12.092Z" }, + { url = "https://files.pythonhosted.org/packages/da/44/8a4f5225e930ff0d999fd43f5d743a4babeb6c7e76dddc00f0e118878ef3/openfeature_sdk-0.10.0-py3-none-any.whl", hash = "sha256:75497ea75d73f684eef509a25f79ad6386368862e050af80ab70a44ae49b33e4", size = 38941, upload-time = "2026-06-01T19:45:33.011Z" }, ] [[package]]