chore!: drop Python 3.8 support (#441)
* drop Python 3.8 support Signed-off-by: gruebel <anton.gruebel@gmail.com> * pin mypy python version to 3.9 Signed-off-by: gruebel <anton.gruebel@gmail.com> --------- Signed-off-by: gruebel <anton.gruebel@gmail.com> Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com>
This commit is contained in:
parent
d4f53b4de2
commit
bcd1a3807e
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
|
@ -44,7 +44,7 @@ jobs:
|
|||
- name: Run E2E tests with behave
|
||||
run: hatch run e2e
|
||||
|
||||
- if: matrix.python-version == '3.11'
|
||||
- if: matrix.python-version == '3.13'
|
||||
name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1
|
||||
with:
|
||||
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
python-version: "3.13"
|
||||
cache: "pip"
|
||||
|
||||
- name: Run pre-commit
|
||||
|
|
@ -77,7 +77,7 @@ jobs:
|
|||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
default_stages: [commit]
|
||||
default_stages: [pre-commit]
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.9.6
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
### System Requirements
|
||||
|
||||
Python 3.8 and above are required.
|
||||
Python 3.9 and above are required.
|
||||
|
||||
### Target version(s)
|
||||
|
||||
Python 3.8 and above are supported by the SDK.
|
||||
Python 3.9 and above are supported by the SDK.
|
||||
|
||||
### Installation and Dependencies
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
</a>
|
||||
|
||||
<a href="https://www.python.org/downloads/">
|
||||
<img alt="Min python version" src="https://img.shields.io/badge/python->=3.8-blue.svg" />
|
||||
<img alt="Min python version" src="https://img.shields.io/badge/python->=3.9-blue.svg" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.repostatus.org/#wip">
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
### Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- Python 3.9+
|
||||
|
||||
### Install
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import threading
|
||||
from collections import defaultdict
|
||||
from typing import TYPE_CHECKING, Dict, List
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from openfeature.event import (
|
||||
EventDetails,
|
||||
|
|
@ -17,10 +17,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
_global_lock = threading.RLock()
|
||||
_global_handlers: Dict[ProviderEvent, List[EventHandler]] = defaultdict(list)
|
||||
_global_handlers: dict[ProviderEvent, list[EventHandler]] = defaultdict(list)
|
||||
|
||||
_client_lock = threading.RLock()
|
||||
_client_handlers: Dict[OpenFeatureClient, Dict[ProviderEvent, List[EventHandler]]] = (
|
||||
_client_handlers: dict[OpenFeatureClient, dict[ProviderEvent, list[EventHandler]]] = (
|
||||
defaultdict(lambda: defaultdict(list))
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ _evaluation_transaction_context_propagator: TransactionContextPropagator = (
|
|||
NoOpTransactionContextPropagator()
|
||||
)
|
||||
|
||||
_hooks: typing.List[Hook] = []
|
||||
_hooks: list[Hook] = []
|
||||
|
||||
|
||||
def get_client(
|
||||
|
|
@ -96,7 +96,7 @@ def set_transaction_context(evaluation_context: EvaluationContext) -> None:
|
|||
)
|
||||
|
||||
|
||||
def add_hooks(hooks: typing.List[Hook]) -> None:
|
||||
def add_hooks(hooks: list[Hook]) -> None:
|
||||
global _hooks
|
||||
_hooks = _hooks + hooks
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ def clear_hooks() -> None:
|
|||
_hooks = []
|
||||
|
||||
|
||||
def get_hooks() -> typing.List[Hook]:
|
||||
def get_hooks() -> list[Hook]:
|
||||
return _hooks
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -77,14 +77,14 @@ GetDetailCallableAsync = typing.Union[
|
|||
typing.Awaitable[FlagResolutionDetails[typing.Union[dict, list]]],
|
||||
],
|
||||
]
|
||||
TypeMap = typing.Dict[
|
||||
TypeMap = dict[
|
||||
FlagType,
|
||||
typing.Union[
|
||||
typing.Type[bool],
|
||||
typing.Type[int],
|
||||
typing.Type[float],
|
||||
typing.Type[str],
|
||||
typing.Tuple[typing.Type[dict], typing.Type[list]],
|
||||
type[bool],
|
||||
type[int],
|
||||
type[float],
|
||||
type[str],
|
||||
tuple[type[dict], type[list]],
|
||||
],
|
||||
]
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ class OpenFeatureClient:
|
|||
domain: typing.Optional[str],
|
||||
version: typing.Optional[str],
|
||||
context: typing.Optional[EvaluationContext] = None,
|
||||
hooks: typing.Optional[typing.List[Hook]] = None,
|
||||
hooks: typing.Optional[list[Hook]] = None,
|
||||
) -> None:
|
||||
self.domain = domain
|
||||
self.version = version
|
||||
|
|
@ -118,7 +118,7 @@ class OpenFeatureClient:
|
|||
def get_metadata(self) -> ClientMetadata:
|
||||
return ClientMetadata(domain=self.domain)
|
||||
|
||||
def add_hooks(self, hooks: typing.List[Hook]) -> None:
|
||||
def add_hooks(self, hooks: list[Hook]) -> None:
|
||||
self.hooks = self.hooks + hooks
|
||||
|
||||
def get_boolean_value(
|
||||
|
|
@ -423,12 +423,12 @@ class OpenFeatureClient:
|
|||
default_value: typing.Any,
|
||||
evaluation_context: typing.Optional[EvaluationContext],
|
||||
flag_evaluation_options: typing.Optional[FlagEvaluationOptions],
|
||||
) -> typing.Tuple[
|
||||
) -> tuple[
|
||||
FeatureProvider,
|
||||
HookContext,
|
||||
HookHints,
|
||||
typing.List[Hook],
|
||||
typing.List[Hook],
|
||||
list[Hook],
|
||||
list[Hook],
|
||||
]:
|
||||
if evaluation_context is None:
|
||||
evaluation_context = EvaluationContext()
|
||||
|
|
@ -477,7 +477,7 @@ class OpenFeatureClient:
|
|||
self,
|
||||
flag_type: FlagType,
|
||||
hook_context: HookContext,
|
||||
merged_hooks: typing.List[Hook],
|
||||
merged_hooks: list[Hook],
|
||||
hook_hints: HookHints,
|
||||
evaluation_context: typing.Optional[EvaluationContext],
|
||||
) -> EvaluationContext:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Callable, Dict, List, Optional, Union
|
||||
from typing import Callable, Optional, Union
|
||||
|
||||
from openfeature.exception import ErrorCode
|
||||
|
||||
|
|
@ -18,19 +18,19 @@ class ProviderEvent(Enum):
|
|||
|
||||
@dataclass
|
||||
class ProviderEventDetails:
|
||||
flags_changed: Optional[List[str]] = None
|
||||
flags_changed: Optional[list[str]] = None
|
||||
message: Optional[str] = None
|
||||
error_code: Optional[ErrorCode] = None
|
||||
metadata: Dict[str, Union[bool, str, int, float]] = field(default_factory=dict)
|
||||
metadata: dict[str, Union[bool, str, int, float]] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class EventDetails(ProviderEventDetails):
|
||||
provider_name: str = ""
|
||||
flags_changed: Optional[List[str]] = None
|
||||
flags_changed: Optional[list[str]] = None
|
||||
message: Optional[str] = None
|
||||
error_code: Optional[ErrorCode] = None
|
||||
metadata: Dict[str, Union[bool, str, int, float]] = field(default_factory=dict)
|
||||
metadata: dict[str, Union[bool, str, int, float]] = field(default_factory=dict)
|
||||
|
||||
@classmethod
|
||||
def from_provider_event_details(
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class FlagEvaluationDetails(typing.Generic[T_co]):
|
|||
|
||||
@dataclass
|
||||
class FlagEvaluationOptions:
|
||||
hooks: typing.List[Hook] = field(default_factory=list)
|
||||
hooks: list[Hook] = field(default_factory=list)
|
||||
hook_hints: HookHints = field(default_factory=dict)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ HookHints = typing.Mapping[
|
|||
float,
|
||||
str,
|
||||
datetime,
|
||||
typing.List[typing.Any],
|
||||
typing.Dict[str, typing.Any],
|
||||
list[typing.Any],
|
||||
dict[str, typing.Any],
|
||||
],
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ def error_hooks(
|
|||
flag_type: FlagType,
|
||||
hook_context: HookContext,
|
||||
exception: Exception,
|
||||
hooks: typing.List[Hook],
|
||||
hooks: list[Hook],
|
||||
hints: typing.Optional[HookHints] = None,
|
||||
) -> None:
|
||||
kwargs = {"hook_context": hook_context, "exception": exception, "hints": hints}
|
||||
|
|
@ -26,7 +26,7 @@ def after_all_hooks(
|
|||
flag_type: FlagType,
|
||||
hook_context: HookContext,
|
||||
details: FlagEvaluationDetails[typing.Any],
|
||||
hooks: typing.List[Hook],
|
||||
hooks: list[Hook],
|
||||
hints: typing.Optional[HookHints] = None,
|
||||
) -> None:
|
||||
kwargs = {"hook_context": hook_context, "details": details, "hints": hints}
|
||||
|
|
@ -39,7 +39,7 @@ def after_hooks(
|
|||
flag_type: FlagType,
|
||||
hook_context: HookContext,
|
||||
details: FlagEvaluationDetails[typing.Any],
|
||||
hooks: typing.List[Hook],
|
||||
hooks: list[Hook],
|
||||
hints: typing.Optional[HookHints] = None,
|
||||
) -> None:
|
||||
kwargs = {"hook_context": hook_context, "details": details, "hints": hints}
|
||||
|
|
@ -51,7 +51,7 @@ def after_hooks(
|
|||
def before_hooks(
|
||||
flag_type: FlagType,
|
||||
hook_context: HookContext,
|
||||
hooks: typing.List[Hook],
|
||||
hooks: list[Hook],
|
||||
hints: typing.Optional[HookHints] = None,
|
||||
) -> EvaluationContext:
|
||||
kwargs = {"hook_context": hook_context, "hints": hints}
|
||||
|
|
@ -68,7 +68,7 @@ def before_hooks(
|
|||
|
||||
def _execute_hooks(
|
||||
flag_type: FlagType,
|
||||
hooks: typing.List[Hook],
|
||||
hooks: list[Hook],
|
||||
hook_method: HookType,
|
||||
**kwargs: typing.Any,
|
||||
) -> list:
|
||||
|
|
@ -91,10 +91,10 @@ def _execute_hooks(
|
|||
|
||||
def _execute_hooks_unchecked(
|
||||
flag_type: FlagType,
|
||||
hooks: typing.List[Hook],
|
||||
hooks: list[Hook],
|
||||
hook_method: HookType,
|
||||
**kwargs: typing.Any,
|
||||
) -> typing.List[typing.Optional[EvaluationContext]]:
|
||||
) -> list[typing.Optional[EvaluationContext]]:
|
||||
"""
|
||||
Execute a single hook without checking whether an exception is thrown. This is
|
||||
used in the before and after hooks since any exception will be caught in the
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class FeatureProvider(typing.Protocol): # pragma: no cover
|
|||
|
||||
def get_metadata(self) -> Metadata: ...
|
||||
|
||||
def get_provider_hooks(self) -> typing.List[Hook]: ...
|
||||
def get_provider_hooks(self) -> list[Hook]: ...
|
||||
|
||||
def resolve_boolean_details(
|
||||
self,
|
||||
|
|
@ -134,7 +134,7 @@ class AbstractProvider(FeatureProvider):
|
|||
def get_metadata(self) -> Metadata:
|
||||
pass
|
||||
|
||||
def get_provider_hooks(self) -> typing.List[Hook]:
|
||||
def get_provider_hooks(self) -> list[Hook]:
|
||||
return []
|
||||
|
||||
@abstractmethod
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ from openfeature.provider.no_op_provider import NoOpProvider
|
|||
|
||||
class ProviderRegistry:
|
||||
_default_provider: FeatureProvider
|
||||
_providers: typing.Dict[str, FeatureProvider]
|
||||
_provider_status: typing.Dict[FeatureProvider, ProviderStatus]
|
||||
_providers: dict[str, FeatureProvider]
|
||||
_provider_status: dict[FeatureProvider, ProviderStatus]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._default_provider = NoOpProvider()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class InMemoryFlag(typing.Generic[T_co]):
|
|||
DISABLED = "DISABLED"
|
||||
|
||||
default_variant: str
|
||||
variants: typing.Dict[str, T_co]
|
||||
variants: dict[str, T_co]
|
||||
flag_metadata: FlagMetadata = field(default_factory=dict)
|
||||
state: State = State.ENABLED
|
||||
context_evaluator: typing.Optional[
|
||||
|
|
@ -51,7 +51,7 @@ class InMemoryFlag(typing.Generic[T_co]):
|
|||
)
|
||||
|
||||
|
||||
FlagStorage = typing.Dict[str, InMemoryFlag[typing.Any]]
|
||||
FlagStorage = dict[str, InMemoryFlag[typing.Any]]
|
||||
|
||||
V = typing.TypeVar("V")
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ class InMemoryProvider(AbstractProvider):
|
|||
def get_metadata(self) -> Metadata:
|
||||
return InMemoryMetadata()
|
||||
|
||||
def get_provider_hooks(self) -> typing.List[Hook]:
|
||||
def get_provider_hooks(self) -> list[Hook]:
|
||||
return []
|
||||
|
||||
def resolve_boolean_details(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class NoOpProvider(AbstractProvider):
|
|||
def get_metadata(self) -> Metadata:
|
||||
return NoOpMetadata()
|
||||
|
||||
def get_provider_hooks(self) -> typing.List[Hook]:
|
||||
def get_provider_hooks(self) -> list[Hook]:
|
||||
return []
|
||||
|
||||
def resolve_boolean_details(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ keywords = [
|
|||
"toggles",
|
||||
]
|
||||
dependencies = []
|
||||
requires-python = ">=3.8"
|
||||
requires-python = ">=3.9"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/open-feature/python-sdk"
|
||||
|
|
@ -66,6 +66,8 @@ packages = ["openfeature"]
|
|||
|
||||
[tool.mypy]
|
||||
files = "openfeature"
|
||||
|
||||
python_version = "3.9" # should be identical to the minimum supported version
|
||||
namespace_packages = true
|
||||
explicit_package_bases = true
|
||||
local_partial_types = true # will become the new default from version 2
|
||||
|
|
@ -73,6 +75,9 @@ pretty = true
|
|||
strict = true
|
||||
disallow_any_generics = false
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_default_fixture_loop_scope = "function"
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
".git",
|
||||
|
|
@ -80,7 +85,7 @@ exclude = [
|
|||
"__pycache__",
|
||||
"venv",
|
||||
]
|
||||
target-version = "py38"
|
||||
target-version = "py39"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
|
|
|
|||
Loading…
Reference in New Issue