Skip to content

Commit 0ecd1d4

Browse files
authored
chore!: drop Python 3.9 support (#364)
* drop Python 3.9 support Signed-off-by: gruebel <anton.gruebel@gmail.com> * fix type Signed-off-by: gruebel <anton.gruebel@gmail.com> * fix merge conflicts Signed-off-by: gruebel <anton.gruebel@gmail.com> * lint Signed-off-by: gruebel <anton.gruebel@gmail.com> --------- Signed-off-by: gruebel <anton.gruebel@gmail.com>
1 parent 67bdbb6 commit 0ecd1d4

44 files changed

Lines changed: 354 additions & 1187 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ on:
2020
permissions:
2121
contents: read
2222

23+
env:
24+
TARGET_PYTHON_VERSION: "3.14"
25+
2326
jobs:
2427
changes:
2528
runs-on: ubuntu-latest
@@ -60,7 +63,7 @@ jobs:
6063
runs-on: ubuntu-latest
6164
strategy:
6265
matrix:
63-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
66+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
6467
package: ${{ fromJSON(needs.changes.outputs.packages) }}
6568
steps:
6669
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
@@ -82,15 +85,15 @@ jobs:
8285
run: uv build
8386

8487
- name: Type checking
85-
if: matrix.python-version == '3.13'
88+
if: matrix.python-version == env.TARGET_PYTHON_VERSION
8689
working-directory: ${{ matrix.package }}
8790
run: poe mypy
8891

8992
- name: Test with pytest
9093
working-directory: ${{ matrix.package }}
9194
run: poe cov
9295

93-
- if: matrix.python-version == '3.13'
96+
- if: matrix.python-version == env.TARGET_PYTHON_VERSION
9497
name: Upload coverage to Codecov
9598
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
9699
with:
@@ -108,7 +111,7 @@ jobs:
108111
- name: Install uv and set the python version
109112
uses: astral-sh/setup-uv@6ee6290f1cbc4156c0bdd66691b2c144ef8df19a # v7
110113
with:
111-
python-version: "3.13"
114+
python-version: ${{ env.TARGET_PYTHON_VERSION }}
112115
enable-cache: false # caching is done automatically in `pre-commit/action`
113116

114117
- name: Run pre-commit

.github/workflows/release.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ on:
1111
permissions:
1212
contents: read
1313

14+
env:
15+
TARGET_PYTHON_VERSION: "3.14"
16+
1417
jobs:
1518
release-please:
1619
runs-on: ubuntu-latest
@@ -54,7 +57,7 @@ jobs:
5457
- name: Install uv and set the python version
5558
uses: astral-sh/setup-uv@6ee6290f1cbc4156c0bdd66691b2c144ef8df19a # v7
5659
with:
57-
python-version: "3.13"
60+
python-version: ${{ env.TARGET_PYTHON_VERSION }}
5861

5962
- name: Install dependencies
6063
working-directory: ${{ matrix.path }}

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.10

hooks/openfeature-hooks-opentelemetry/pyproject.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies = [
2020
"opentelemetry-api",
2121
"opentelemetry-semantic-conventions>=0.50b0",
2222
]
23-
requires-python = ">=3.9"
23+
requires-python = ">=3.10"
2424

2525
[project.urls]
2626
Homepage = "https://github.com/open-feature/python-sdk-contrib"
@@ -30,7 +30,7 @@ dev = [
3030
"coverage[toml]>=7.10.0,<8.0.0",
3131
"mypy>=1.18.0,<2.0.0",
3232
"poethepoet>=0.37.0",
33-
"pytest>=8.4.0,<9.0.0",
33+
"pytest>=9.0.0,<10.0.0",
3434
]
3535

3636
[tool.uv.build-backend]
@@ -42,7 +42,7 @@ namespace = true
4242
mypy_path = "src"
4343
files = "src"
4444

45-
python_version = "3.9" # should be identical to the minimum supported version
45+
python_version = "3.10" # should be identical to the minimum supported version
4646
namespace_packages = true
4747
explicit_package_bases = true
4848
local_partial_types = true # will become the new default from version 2
@@ -53,6 +53,9 @@ pretty = true
5353
strict = true
5454
disallow_any_generics = false
5555

56+
[tool.pytest]
57+
strict = true
58+
5659
[tool.poe.tasks]
5760
test = "pytest tests"
5861
test-cov = "coverage run -m pytest tests"

providers/openfeature-provider-aws-ssm/pyproject.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies = [
2020
"boto3>=1.28.0",
2121
"cachebox>=5.1.0,<6.0.0",
2222
]
23-
requires-python = ">=3.9"
23+
requires-python = ">=3.10"
2424

2525
[project.optional-dependencies]
2626
async = ["aioboto3>=12.3.0"]
@@ -36,8 +36,8 @@ dev = [
3636
"coverage[toml]>=7.10.0,<8.0.0",
3737
"mypy>=1.18.0,<2.0.0",
3838
"poethepoet>=0.37.0",
39-
"pytest>=8.4.0,<9.0.0",
40-
"pytest-asyncio>=0.23.0",
39+
"pytest>=9.0.0,<10.0.0",
40+
"pytest-asyncio>=1.3.0,<2.0.0",
4141
"moto[ssm]>=5.0.0,<6.0.0",
4242
]
4343

@@ -50,7 +50,7 @@ namespace = true
5050
mypy_path = "src"
5151
files = "src"
5252

53-
python_version = "3.9" # should be identical to the minimum supported version
53+
python_version = "3.10" # should be identical to the minimum supported version
5454
namespace_packages = true
5555
explicit_package_bases = true
5656
local_partial_types = true # will become the new default from version 2
@@ -66,6 +66,9 @@ omit = [
6666
"tests/**",
6767
]
6868

69+
[tool.pytest]
70+
strict = true
71+
6972
[tool.poe.tasks]
7073
test = "pytest tests"
7174
test-cov = "coverage run -m pytest tests"

providers/openfeature-provider-aws-ssm/src/openfeature/contrib/provider/awsssm/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@ class AwsSsmProviderConfig:
6565
"""
6666

6767
config: Optional["Config"] = None
68-
endpoint_url: Optional[str] = None
68+
endpoint_url: str | None = None
6969
enable_decryption: bool = False
70-
cache_config: Optional[CacheConfig] = field(default_factory=CacheConfig)
70+
cache_config: CacheConfig | None = field(default_factory=CacheConfig)

providers/openfeature-provider-aws-ssm/src/openfeature/contrib/provider/awsssm/parsers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import json
2-
from typing import Any, Union
2+
from typing import Any
33

44
from openfeature.exception import ParseError, TypeMismatchError
55

@@ -65,7 +65,7 @@ def parse_float(value: str) -> float:
6565
raise TypeMismatchError(f"Cannot parse '{value}' as float") from e
6666

6767

68-
def parse_object(value: str) -> Union[dict[str, Any], list[Any]]:
68+
def parse_object(value: str) -> dict[str, Any] | list[Any]:
6969
"""
7070
Parse a string value as a JSON object.
7171

providers/openfeature-provider-aws-ssm/src/openfeature/contrib/provider/awsssm/provider.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from collections.abc import Mapping, Sequence
2-
from typing import Any, Callable, Optional, TypeVar, Union, cast
1+
from collections.abc import Callable, Mapping, Sequence
2+
from typing import Any, TypeVar, cast
33

44
from cachebox import BaseCacheImpl, LRUCache, TTLCache
55

@@ -17,7 +17,7 @@
1717
class AwsSsmProvider(AbstractProvider):
1818
"""Provider for AWS Systems Manager Parameter Store."""
1919

20-
def __init__(self, config: Optional[AwsSsmProviderConfig] = None) -> None:
20+
def __init__(self, config: AwsSsmProviderConfig | None = None) -> None:
2121
"""
2222
Initialize the AWS SSM Provider.
2323
@@ -31,7 +31,7 @@ def __init__(self, config: Optional[AwsSsmProviderConfig] = None) -> None:
3131
enable_decryption=self.config.enable_decryption,
3232
)
3333

34-
self.cache: Optional[BaseCacheImpl] = None
34+
self.cache: BaseCacheImpl | None = None
3535
if self.config.cache_config:
3636
cache_config = self.config.cache_config
3737
if cache_config.cache_type == "lru":
@@ -48,7 +48,7 @@ def get_metadata(self) -> Metadata:
4848
"""
4949
return Metadata(name="aws-ssm")
5050

51-
def _get_cached_value(self, flag_key: str) -> Optional[Any]:
51+
def _get_cached_value(self, flag_key: str) -> Any | None:
5252
"""
5353
Get value from cache if available.
5454
@@ -76,7 +76,7 @@ def _set_cache_value(self, flag_key: str, value: Any) -> None:
7676
def _resolve_with_cache(
7777
self,
7878
flag_key: str,
79-
parser: Optional[Callable[[str], T]] = None,
79+
parser: Callable[[str], T] | None = None,
8080
) -> FlagResolutionDetails[T]:
8181
"""
8282
Base resolution logic with caching for synchronous operations.
@@ -108,7 +108,7 @@ def _resolve_with_cache(
108108
async def _resolve_with_cache_async(
109109
self,
110110
flag_key: str,
111-
parser: Optional[Callable[[str], T]] = None,
111+
parser: Callable[[str], T] | None = None,
112112
) -> FlagResolutionDetails[T]:
113113
"""
114114
Base resolution logic with caching for asynchronous operations.
@@ -141,7 +141,7 @@ def resolve_boolean_details(
141141
self,
142142
flag_key: str,
143143
default_value: bool,
144-
evaluation_context: Optional[EvaluationContext] = None,
144+
evaluation_context: EvaluationContext | None = None,
145145
) -> FlagResolutionDetails[bool]:
146146
"""
147147
Resolve a boolean flag.
@@ -160,7 +160,7 @@ def resolve_string_details(
160160
self,
161161
flag_key: str,
162162
default_value: str,
163-
evaluation_context: Optional[EvaluationContext] = None,
163+
evaluation_context: EvaluationContext | None = None,
164164
) -> FlagResolutionDetails[str]:
165165
"""
166166
Resolve a string flag.
@@ -179,7 +179,7 @@ def resolve_integer_details(
179179
self,
180180
flag_key: str,
181181
default_value: int,
182-
evaluation_context: Optional[EvaluationContext] = None,
182+
evaluation_context: EvaluationContext | None = None,
183183
) -> FlagResolutionDetails[int]:
184184
"""
185185
Resolve an integer flag.
@@ -198,7 +198,7 @@ def resolve_float_details(
198198
self,
199199
flag_key: str,
200200
default_value: float,
201-
evaluation_context: Optional[EvaluationContext] = None,
201+
evaluation_context: EvaluationContext | None = None,
202202
) -> FlagResolutionDetails[float]:
203203
"""
204204
Resolve a float flag.
@@ -216,11 +216,9 @@ def resolve_float_details(
216216
def resolve_object_details(
217217
self,
218218
flag_key: str,
219-
default_value: Union[Sequence[FlagValueType], Mapping[str, FlagValueType]],
220-
evaluation_context: Optional[EvaluationContext] = None,
221-
) -> FlagResolutionDetails[
222-
Union[Sequence[FlagValueType], Mapping[str, FlagValueType]]
223-
]:
219+
default_value: Sequence[FlagValueType] | Mapping[str, FlagValueType],
220+
evaluation_context: EvaluationContext | None = None,
221+
) -> FlagResolutionDetails[Sequence[FlagValueType] | Mapping[str, FlagValueType]]:
224222
"""
225223
Resolve an object flag.
226224
@@ -239,7 +237,7 @@ async def resolve_boolean_details_async(
239237
self,
240238
flag_key: str,
241239
default_value: bool,
242-
evaluation_context: Optional[EvaluationContext] = None,
240+
evaluation_context: EvaluationContext | None = None,
243241
) -> FlagResolutionDetails[bool]:
244242
"""
245243
Resolve a boolean flag asynchronously.
@@ -258,7 +256,7 @@ async def resolve_string_details_async(
258256
self,
259257
flag_key: str,
260258
default_value: str,
261-
evaluation_context: Optional[EvaluationContext] = None,
259+
evaluation_context: EvaluationContext | None = None,
262260
) -> FlagResolutionDetails[str]:
263261
"""
264262
Resolve a string flag asynchronously.
@@ -277,7 +275,7 @@ async def resolve_integer_details_async(
277275
self,
278276
flag_key: str,
279277
default_value: int,
280-
evaluation_context: Optional[EvaluationContext] = None,
278+
evaluation_context: EvaluationContext | None = None,
281279
) -> FlagResolutionDetails[int]:
282280
"""
283281
Resolve an integer flag asynchronously.
@@ -296,7 +294,7 @@ async def resolve_float_details_async(
296294
self,
297295
flag_key: str,
298296
default_value: float,
299-
evaluation_context: Optional[EvaluationContext] = None,
297+
evaluation_context: EvaluationContext | None = None,
300298
) -> FlagResolutionDetails[float]:
301299
"""
302300
Resolve a float flag asynchronously.
@@ -314,11 +312,9 @@ async def resolve_float_details_async(
314312
async def resolve_object_details_async(
315313
self,
316314
flag_key: str,
317-
default_value: Union[Sequence[FlagValueType], Mapping[str, FlagValueType]],
318-
evaluation_context: Optional[EvaluationContext] = None,
319-
) -> FlagResolutionDetails[
320-
Union[Sequence[FlagValueType], Mapping[str, FlagValueType]]
321-
]:
315+
default_value: Sequence[FlagValueType] | Mapping[str, FlagValueType],
316+
evaluation_context: EvaluationContext | None = None,
317+
) -> FlagResolutionDetails[Sequence[FlagValueType] | Mapping[str, FlagValueType]]:
322318
"""
323319
Resolve an object flag asynchronously.
324320

providers/openfeature-provider-aws-ssm/src/openfeature/contrib/provider/awsssm/ssm_service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TYPE_CHECKING, Any, Optional
1+
from typing import TYPE_CHECKING, Any
22

33
import boto3
44
from botocore.config import Config
@@ -25,8 +25,8 @@ class SsmService:
2525

2626
def __init__(
2727
self,
28-
config: Optional[Config] = None,
29-
endpoint_url: Optional[str] = None,
28+
config: Config | None = None,
29+
endpoint_url: str | None = None,
3030
enable_decryption: bool = False,
3131
) -> None:
3232
"""
@@ -38,8 +38,8 @@ def __init__(
3838
enable_decryption: Whether to decrypt SecureString parameters
3939
"""
4040
self.enable_decryption = enable_decryption
41-
self._client: Optional[SSMClient] = None
42-
self._async_session: Optional[aioboto3.Session] = None
41+
self._client: SSMClient | None = None
42+
self._async_session: aioboto3.Session | None = None
4343
self._client_kwargs: dict[str, Any] = {}
4444
if endpoint_url:
4545
self._client_kwargs["endpoint_url"] = endpoint_url

providers/openfeature-provider-aws-ssm/tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections.abc import Awaitable, Callable, Generator, Iterator
22
from contextlib import contextmanager
33
from dataclasses import dataclass
4-
from typing import TypeVar, Union
4+
from typing import TypeVar
55

66
import aiobotocore.endpoint
77
import boto3
@@ -25,7 +25,7 @@
2525
class _PatchedAWSResponseContent:
2626
"""Patched version of `botocore.awsrequest.AWSResponse.content`."""
2727

28-
content: Union[bytes, Awaitable[bytes]]
28+
content: bytes | Awaitable[bytes]
2929

3030
def decode(self, encoding: str) -> str:
3131
assert isinstance(self.content, bytes)

0 commit comments

Comments
 (0)