Skip to content

Commit 0408bd8

Browse files
authored
Drop support for Python versions older than 3.11 (#401)
- Reformat `pyproject.toml` using `taplo` - Drop support for Python versions older than 3.11 - Improve typing annotations - Remove Sample comparison hack Fixes #148, fixes #399.
2 parents 89af7da + b21841d commit 0408bd8

File tree

6 files changed

+100
-113
lines changed

6 files changed

+100
-113
lines changed

.github/workflows/ci.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ on:
1111
- 'gh-readonly-queue/**'
1212
workflow_dispatch:
1313

14+
env:
15+
DEFAULT_PYTHON_VERSION: '3.11'
1416

1517
jobs:
1618
test:
@@ -20,9 +22,6 @@ jobs:
2022
os:
2123
- ubuntu-20.04
2224
python:
23-
- "3.8"
24-
- "3.9"
25-
- "3.10"
2625
- "3.11"
2726
runs-on: ${{ matrix.os }}
2827

@@ -34,6 +33,7 @@ jobs:
3433
uses: actions/setup-python@v4
3534
with:
3635
python-version: ${{ matrix.python }}
36+
cache: 'pip'
3737

3838
- uses: actions/cache@v3
3939
with:
@@ -60,7 +60,8 @@ jobs:
6060
- name: Set up Python
6161
uses: actions/setup-python@v4
6262
with:
63-
python-version: "3.11"
63+
python-version: '3.11'
64+
cache: 'pip'
6465

6566
- name: Install build dependencies
6667
run: |
@@ -91,6 +92,7 @@ jobs:
9192
uses: actions/setup-python@v4
9293
with:
9394
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
95+
cache: 'pip'
9496

9597
- name: Install build dependencies
9698
run: |
@@ -168,6 +170,7 @@ jobs:
168170
uses: actions/setup-python@v4
169171
with:
170172
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
173+
cache: 'pip'
171174

172175
- name: Install build dependencies
173176
if: steps.mike-metadata.outputs.version

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ A development kit to interact with the Frequenz development platform.
88

99
## Supported Python versions
1010

11-
* For x86_64 Python 3.8 - 3.11 are supported (tested).
12-
* For arm64 only Python 3.8 is supported (due to some dependencies that only support 3.8).
11+
* Only Python 3.11 is fully supported (tested).
1312

1413
## Contributing
1514

RELEASE_NOTES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
## Summary
44

5-
<!-- Here goes a general summary of what this release is about -->
5+
This release drops support for Python versions older than 3.11.
66

77
## Upgrading
88

9+
* Now Python 3.11 is the minimum supported version. All users must upgrade to Python 3.11 (including virtual environments used for development).
10+
911
* Now `float` is used everywhere for representing power (before power metrics were `float` but setting power was done using `int`).
1012
* `frequenz.sdk.actor.power_distributing`: the `power` attribute of the `Request` class has been updated from `int` to a `float`.
1113
* `frequenz.sdk.microgrid`: the `set_power()` method of both the `MicrogridApiClient` and `MicrogridGrpcClient` classes now expect a `float` value for the `power_w` parameter instead of `int`.
1214

1315
* The `LogicalMeter` no longer takes a `component_graph` parameter.
1416

17+
* Now `frequenz.sdk.timeseries.Sample` uses a more sensible comparison. Before this release `Sample`s were compared only based on the `timestamp`. This was due to a limitation in Python versions earlier than 3.10. Now that the minimum supported version is 3.11 this hack is not needed anymore and `Sample`s are compared using both `timestamp` and `value` as most people probably expects.
18+
1519
## New Features
1620

1721
<!-- Here goes the main new features and examples or instructions on how to use them -->

pyproject.toml

Lines changed: 70 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[build-system]
22
requires = [
3-
"setuptools >= 65.3.0, < 66",
4-
"setuptools_scm[toml] >= 7.0.5, < 8",
5-
"wheel"
3+
"setuptools >= 65.3.0, < 66",
4+
"setuptools_scm[toml] >= 7.0.5, < 8",
5+
"wheel",
66
]
77
build-backend = "setuptools.build_meta"
88

@@ -11,87 +11,76 @@ name = "frequenz-sdk"
1111
description = "Frequenz Python SDK"
1212
readme = "README.md"
1313
license = { text = "MIT" }
14-
keywords = [ "frequenz", "sdk", "microgrid", "actor" ]
14+
keywords = ["frequenz", "sdk", "microgrid", "actor"]
1515
classifiers = [
16-
"Development Status :: 3 - Alpha",
17-
"Intended Audience :: Developers",
18-
"License :: OSI Approved :: MIT License",
19-
"Programming Language :: Python :: 3",
20-
"Programming Language :: Python :: 3 :: Only",
21-
"Programming Language :: Python :: 3.8",
22-
"Programming Language :: Python :: 3.9",
23-
"Programming Language :: Python :: 3.10",
24-
"Topic :: Software Development :: Libraries",
16+
"Development Status :: 3 - Alpha",
17+
"Intended Audience :: Developers",
18+
"License :: OSI Approved :: MIT License",
19+
"Programming Language :: Python :: 3",
20+
"Programming Language :: Python :: 3 :: Only",
21+
"Topic :: Software Development :: Libraries",
2522
]
26-
requires-python = ">= 3.8, < 4"
23+
requires-python = ">= 3.11, < 4"
2724
dependencies = [
28-
"frequenz-api-microgrid >= 0.11.0, < 0.12.0",
29-
"frequenz-channels >= 0.14.0, < 0.15.0",
30-
"google-api-python-client >= 2.71, < 3",
31-
"grpcio >= 1.51.1, < 2",
32-
"grpcio-tools >= 1.51.1, < 2",
33-
"networkx >= 2.8, < 4",
34-
"numpy >= 1.24.2, < 2",
35-
"protobuf >= 4.21.6, < 5",
36-
"pydantic >= 1.9",
37-
"toml >= 0.10",
38-
"tqdm >= 4.38.0, < 5",
39-
"typing_extensions >= 4.4.0, < 5",
40-
"watchfiles >= 0.15.0",
25+
"frequenz-api-microgrid >= 0.11.0, < 0.12.0",
26+
"frequenz-channels >= 0.14.0, < 0.15.0",
27+
"google-api-python-client >= 2.71, < 3",
28+
"grpcio >= 1.51.1, < 2",
29+
"grpcio-tools >= 1.51.1, < 2",
30+
"networkx >= 2.8, < 4",
31+
"numpy >= 1.24.2, < 2",
32+
"protobuf >= 4.21.6, < 5",
33+
"pydantic >= 1.9",
34+
"toml >= 0.10",
35+
"tqdm >= 4.38.0, < 5",
36+
"typing_extensions >= 4.4.0, < 5",
37+
"watchfiles >= 0.15.0",
4138
]
42-
dynamic = [ "version" ]
39+
dynamic = ["version"]
4340

4441
[[project.authors]]
45-
name ="Frequenz Energy-as-a-Service GmbH"
42+
name = "Frequenz Energy-as-a-Service GmbH"
4643
4744

4845
[project.optional-dependencies]
4946
docs-gen = [
50-
"mike == 1.1.2",
51-
"mkdocs-gen-files == 0.5.0",
52-
"mkdocs-literate-nav == 0.6.0",
53-
"mkdocs-material == 9.1.14",
54-
"mkdocs-section-index == 0.3.5",
55-
"mkdocstrings[python] == 0.21.2",
47+
"mike == 1.1.2",
48+
"mkdocs-gen-files == 0.5.0",
49+
"mkdocs-literate-nav == 0.6.0",
50+
"mkdocs-material == 9.1.14",
51+
"mkdocs-section-index == 0.3.5",
52+
"mkdocstrings[python] == 0.21.2",
5653
]
5754
docs-lint = [
58-
"pydocstyle == 6.3.0",
59-
"darglint == 1.8.1",
60-
"tomli == 2.0.1", # Needed by pydocstyle to read pyproject.toml
61-
]
62-
format = [
63-
"black == 23.3.0",
64-
"isort == 5.12.0",
65-
]
66-
nox = [
67-
"nox == 2023.4.22",
68-
"toml == 0.10.2",
55+
"pydocstyle == 6.3.0",
56+
"darglint == 1.8.1",
57+
"tomli == 2.0.1", # Needed by pydocstyle to read pyproject.toml
6958
]
59+
format = ["black == 23.3.0", "isort == 5.12.0"]
60+
nox = ["nox == 2023.4.22", "toml == 0.10.2"]
7061
pytest = [
71-
"pytest == 7.3.1",
72-
"pytest-cov == 4.0.0",
73-
"pytest-mock == 3.10.0",
74-
"pytest-asyncio == 0.21.0",
75-
"time-machine == 2.9.0",
76-
"async-solipsism == 0.5",
77-
# For checking docstring code examples
78-
"sybil == 5.0.2",
79-
"pylint == 2.17.4",
62+
"pytest == 7.3.1",
63+
"pytest-cov == 4.0.0",
64+
"pytest-mock == 3.10.0",
65+
"pytest-asyncio == 0.21.0",
66+
"time-machine == 2.9.0",
67+
"async-solipsism == 0.5",
68+
# For checking docstring code examples
69+
"sybil == 5.0.2",
70+
"pylint == 2.17.4",
8071
]
8172
mypy = [
82-
"mypy == 1.3.0",
83-
"grpc-stubs == 1.24.12", # This dependency introduces breaking changes in patch releases
84-
# For checking the noxfile, docs/ script, and tests
85-
"frequenz-sdk[docs-gen,nox,pytest]",
73+
"mypy == 1.3.0",
74+
"grpc-stubs == 1.24.12", # This dependency introduces breaking changes in patch releases
75+
# For checking the noxfile, docs/ script, and tests
76+
"frequenz-sdk[docs-gen,nox,pytest]",
8677
]
8778
pylint = [
88-
"pylint == 2.17.4",
89-
# For checking the noxfile, docs/ script, and tests
90-
"frequenz-sdk[docs-gen,nox,pytest]",
91-
]
92-
dev = [
93-
"frequenz-sdk[docs-gen,docs-lint,format,nox,pytest,mypy,pylint]",
79+
"pylint == 2.17.4",
80+
# For checking the noxfile, docs/ script, and tests
81+
"frequenz-sdk[docs-gen,nox,pytest]",
9482
]
83+
dev = ["frequenz-sdk[docs-gen,docs-lint,format,nox,pytest,mypy,pylint]"]
9584

9685
[project.urls]
9786
Changelog = "https://github.com/frequenz-floss/frequenz-sdk-python/releases"
@@ -111,21 +100,21 @@ target-version = ['py38']
111100
include = '\.pyi?$'
112101

113102
[tool.pylint.similarities]
114-
ignore-comments=['yes']
115-
ignore-docstrings=['yes']
116-
ignore-imports=['no']
117-
min-similarity-lines=40
103+
ignore-comments = ['yes']
104+
ignore-docstrings = ['yes']
105+
ignore-imports = ['no']
106+
min-similarity-lines = 40
118107

119108
[tool.pylint.messages_control]
120109
disable = [
121-
"too-few-public-methods",
122-
# disabled because it conflicts with isort
123-
"wrong-import-order",
124-
"ungrouped-imports"
110+
"too-few-public-methods",
111+
# disabled because it conflicts with isort
112+
"wrong-import-order",
113+
"ungrouped-imports",
125114
]
126115

127116
[tool.pylint.design]
128-
max-attributes=12
117+
max-attributes = 12
129118

130119
[tool.isort]
131120
profile = "black"
@@ -134,21 +123,18 @@ src_paths = ["src", "examples", "tests"]
134123

135124
[tool.pytest.ini_options]
136125
asyncio_mode = "auto"
137-
required_plugins = [ "pytest-asyncio", "pytest-mock" ]
126+
required_plugins = ["pytest-asyncio", "pytest-mock"]
138127

139128
[[tool.mypy.overrides]]
140129
module = [
141-
"grpc.aio",
142-
"grpc.aio.*",
143-
# There is a stubs package available, but it's not working:
144-
# https://github.com/eggplants/networkx-stubs/issues/1
145-
"networkx",
130+
"grpc.aio",
131+
"grpc.aio.*",
132+
# There is a stubs package available, but it's not working:
133+
# https://github.com/eggplants/networkx-stubs/issues/1
134+
"networkx",
146135
]
147136
ignore_missing_imports = true
148137

149138
[[tool.mypy.overrides]]
150-
module = [
151-
"async_solipsism",
152-
"async_solipsism.*",
153-
]
139+
module = ["async_solipsism", "async_solipsism.*"]
154140
ignore_missing_imports = true

src/frequenz/sdk/timeseries/_base_types.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,15 @@
33

44
"""Timeseries basic types."""
55

6-
from __future__ import annotations
7-
86
import functools
9-
from dataclasses import dataclass, field
7+
from dataclasses import dataclass
108
from datetime import datetime, timezone
11-
from typing import Callable, Iterator, Optional, overload
9+
from typing import Callable, Iterator, Self, overload
1210

1311
UNIX_EPOCH = datetime.fromtimestamp(0.0, tz=timezone.utc)
1412
"""The UNIX epoch (in UTC)."""
1513

1614

17-
# Ordering by timestamp is a bit arbitrary, and it is not always what might be
18-
# wanted. We are using this order now because usually we need to do binary
19-
# searches on sequences of samples, and the Python `bisect` module doesn't
20-
# support providing a key until Python 3.10.
2115
@dataclass(frozen=True, order=True)
2216
class Sample:
2317
"""A measurement taken at a particular point in time.
@@ -27,10 +21,10 @@ class Sample:
2721
coherent view on a group of component metrics for a particular timestamp.
2822
"""
2923

30-
timestamp: datetime = field(compare=True)
24+
timestamp: datetime
3125
"""The time when this sample was generated."""
3226

33-
value: Optional[float] = field(compare=False, default=None)
27+
value: float | None = None
3428
"""The value of this sample."""
3529

3630

@@ -46,13 +40,13 @@ class Sample3Phase:
4640

4741
timestamp: datetime
4842
"""The time when this sample was generated."""
49-
value_p1: Optional[float]
43+
value_p1: float | None
5044
"""The value of the 1st phase in this sample."""
5145

52-
value_p2: Optional[float]
46+
value_p2: float | None
5347
"""The value of the 2nd phase in this sample."""
5448

55-
value_p3: Optional[float]
49+
value_p3: float | None
5650
"""The value of the 3rd phase in this sample."""
5751

5852
def __iter__(self) -> Iterator[float | None]:
@@ -117,7 +111,7 @@ def min(self, default: float | None = None) -> float | None:
117111

118112
def map(
119113
self, function: Callable[[float], float], default: float | None = None
120-
) -> Sample3Phase:
114+
) -> Self:
121115
"""Apply the given function on each of the phase values and return the result.
122116
123117
If a phase value is `None`, replace it with `default` instead.
@@ -127,10 +121,10 @@ def map(
127121
default: The value to apply if a phase value is `None`.
128122
129123
Returns:
130-
A new `Sample3Phase` instance, with the given function applied on values
131-
for each of the phases.
124+
A new instance, with the given function applied on values for each of the
125+
phases.
132126
"""
133-
return Sample3Phase(
127+
return self.__class__(
134128
timestamp=self.timestamp,
135129
value_p1=default if self.value_p1 is None else function(self.value_p1),
136130
value_p2=default if self.value_p2 is None else function(self.value_p2),

src/frequenz/sdk/timeseries/_resampling.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -705,11 +705,12 @@ def resample(self, timestamp: datetime) -> Sample:
705705
)
706706
minimum_relevant_timestamp = timestamp - period * conf.max_data_age_in_periods
707707

708-
# We need to pass a dummy Sample to bisect because it only support
709-
# specifying a key extraction function in Python 3.10, so we need to
710-
# compare samples at the moment.
711-
min_index = bisect(self._buffer, Sample(minimum_relevant_timestamp, None))
712-
max_index = bisect(self._buffer, Sample(timestamp, None))
708+
min_index = bisect(
709+
self._buffer,
710+
minimum_relevant_timestamp,
711+
key=lambda s: s.timestamp,
712+
)
713+
max_index = bisect(self._buffer, timestamp, key=lambda s: s.timestamp)
713714
# Using itertools for slicing doesn't look very efficient, but
714715
# experiments with a custom (ring) buffer that can slice showed that
715716
# it is not that bad. See:

0 commit comments

Comments
 (0)