frequenz-core 1.3.0__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {frequenz_core-1.3.0/src/frequenz_core.egg-info → frequenz_core-1.4.0}/PKG-INFO +50 -28
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/README.md +22 -0
- frequenz_core-1.4.0/RELEASE_NOTES.md +23 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/pyproject.toml +33 -28
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/enum.py +4 -2
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/id.py +18 -2
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/math.py +34 -40
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/typing.py +52 -2
- {frequenz_core-1.3.0 → frequenz_core-1.4.0/src/frequenz_core.egg-info}/PKG-INFO +50 -28
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz_core.egg-info/SOURCES.txt +2 -0
- frequenz_core-1.4.0/src/frequenz_core.egg-info/requires.txt +49 -0
- frequenz_core-1.4.0/src/frequenz_core.egg-info/scm_file_list.json +55 -0
- frequenz_core-1.4.0/src/frequenz_core.egg-info/scm_version.json +8 -0
- frequenz_core-1.3.0/RELEASE_NOTES.md +0 -9
- frequenz_core-1.3.0/src/frequenz_core.egg-info/requires.txt +0 -48
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/LICENSE +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/MANIFEST.in +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/setup.cfg +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/__init__.py +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/conftest.py +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/datetime.py +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/module.py +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz/core/py.typed +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz_core.egg-info/dependency_links.txt +0 -0
- {frequenz_core-1.3.0 → frequenz_core-1.4.0}/src/frequenz_core.egg-info/top_level.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frequenz-core
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Core utilities to complement Python's standard library
|
|
5
5
|
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-core-python/
|
|
8
8
|
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-core-python/releases
|
|
9
9
|
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-core-python/issues
|
|
@@ -12,7 +12,6 @@ Project-URL: Support, https://github.com/frequenz-floss/frequenz-core-python/dis
|
|
|
12
12
|
Keywords: asyncio,collections,core,datetime,frequenz,lib,library,math,python,stdlib,typing
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
15
|
Classifier: Programming Language :: Python :: 3
|
|
17
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
17
|
Classifier: Topic :: Software Development :: Libraries
|
|
@@ -23,41 +22,42 @@ License-File: LICENSE
|
|
|
23
22
|
Requires-Dist: typing-extensions<5,>=4.13.0
|
|
24
23
|
Provides-Extra: dev-flake8
|
|
25
24
|
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
25
|
+
Requires-Dist: flake8-datetimez==20.10.0; extra == "dev-flake8"
|
|
26
26
|
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
27
|
-
Requires-Dist: flake8-pyproject==1.2.
|
|
28
|
-
Requires-Dist: pydoclint==0.
|
|
27
|
+
Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
|
|
28
|
+
Requires-Dist: pydoclint==0.9.1; extra == "dev-flake8"
|
|
29
29
|
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
30
30
|
Provides-Extra: dev-formatting
|
|
31
|
-
Requires-Dist: black==
|
|
32
|
-
Requires-Dist: isort==
|
|
31
|
+
Requires-Dist: black==26.5.1; extra == "dev-formatting"
|
|
32
|
+
Requires-Dist: isort==8.0.1; extra == "dev-formatting"
|
|
33
33
|
Provides-Extra: dev-mkdocs
|
|
34
|
-
Requires-Dist: Markdown==3.
|
|
35
|
-
Requires-Dist: black==
|
|
36
|
-
Requires-Dist: mike==2.
|
|
37
|
-
Requires-Dist: mkdocs-gen-files==0.
|
|
38
|
-
Requires-Dist: mkdocs-literate-nav==0.6.
|
|
39
|
-
Requires-Dist: mkdocs-macros-plugin==1.
|
|
40
|
-
Requires-Dist: mkdocs-material==9.
|
|
41
|
-
Requires-Dist: mkdocstrings[python]==0.
|
|
42
|
-
Requires-Dist: mkdocstrings-python==
|
|
43
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
34
|
+
Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
|
|
35
|
+
Requires-Dist: black==26.5.1; extra == "dev-mkdocs"
|
|
36
|
+
Requires-Dist: mike==2.2.0; extra == "dev-mkdocs"
|
|
37
|
+
Requires-Dist: mkdocs-gen-files==0.6.1; extra == "dev-mkdocs"
|
|
38
|
+
Requires-Dist: mkdocs-literate-nav==0.6.3; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mkdocs-material==9.7.7; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocstrings[python]==1.0.6; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocstrings-python==2.0.5; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-mkdocs"
|
|
44
44
|
Provides-Extra: dev-mypy
|
|
45
|
-
Requires-Dist: mypy==
|
|
46
|
-
Requires-Dist: types-Markdown==3.
|
|
45
|
+
Requires-Dist: mypy==2.3.0; extra == "dev-mypy"
|
|
46
|
+
Requires-Dist: types-Markdown==3.10.2.20260712; extra == "dev-mypy"
|
|
47
47
|
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
48
48
|
Provides-Extra: dev-noxfile
|
|
49
|
-
Requires-Dist: nox==
|
|
50
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
49
|
+
Requires-Dist: nox==2026.7.11; extra == "dev-noxfile"
|
|
50
|
+
Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-noxfile"
|
|
51
51
|
Provides-Extra: dev-pylint
|
|
52
52
|
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
53
53
|
Provides-Extra: dev-pytest
|
|
54
|
-
Requires-Dist: pytest==
|
|
55
|
-
Requires-Dist: pylint==
|
|
56
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.
|
|
57
|
-
Requires-Dist: pytest-mock==3.
|
|
58
|
-
Requires-Dist: pytest-asyncio==1.
|
|
59
|
-
Requires-Dist: async-solipsism==0.
|
|
60
|
-
Requires-Dist: hypothesis==6.
|
|
54
|
+
Requires-Dist: pytest==9.1.1; extra == "dev-pytest"
|
|
55
|
+
Requires-Dist: pylint==4.0.6; extra == "dev-pytest"
|
|
56
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.18.0; extra == "dev-pytest"
|
|
57
|
+
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
58
|
+
Requires-Dist: pytest-asyncio==1.4.0; extra == "dev-pytest"
|
|
59
|
+
Requires-Dist: async-solipsism==0.9; extra == "dev-pytest"
|
|
60
|
+
Requires-Dist: hypothesis==6.163.0; extra == "dev-pytest"
|
|
61
61
|
Provides-Extra: dev
|
|
62
62
|
Requires-Dist: frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
63
|
Dynamic: license-file
|
|
@@ -200,6 +200,28 @@ class ApiClient:
|
|
|
200
200
|
client = ApiClient.create("my-api-key") # ✅ Works
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
+
Annotate floating point values honestly, as Python's numeric tower lets `int`
|
|
204
|
+
values through any `float` annotation:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from typing import assert_never
|
|
208
|
+
|
|
209
|
+
from frequenz.core.typing import FloatInt
|
|
210
|
+
|
|
211
|
+
def describe(value: FloatInt | None) -> str:
|
|
212
|
+
match value:
|
|
213
|
+
case float() | int():
|
|
214
|
+
return f"number {value}"
|
|
215
|
+
case None:
|
|
216
|
+
return "nothing"
|
|
217
|
+
case unexpected:
|
|
218
|
+
assert_never(unexpected)
|
|
219
|
+
|
|
220
|
+
assert describe(1) == "number 1" # ✅ `case float():` alone would crash here
|
|
221
|
+
assert describe(1.5) == "number 1.5"
|
|
222
|
+
assert describe(None) == "nothing"
|
|
223
|
+
```
|
|
224
|
+
|
|
203
225
|
### Strongly-Typed IDs
|
|
204
226
|
|
|
205
227
|
Create type-safe identifiers for different entities:
|
|
@@ -136,6 +136,28 @@ class ApiClient:
|
|
|
136
136
|
client = ApiClient.create("my-api-key") # ✅ Works
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
Annotate floating point values honestly, as Python's numeric tower lets `int`
|
|
140
|
+
values through any `float` annotation:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from typing import assert_never
|
|
144
|
+
|
|
145
|
+
from frequenz.core.typing import FloatInt
|
|
146
|
+
|
|
147
|
+
def describe(value: FloatInt | None) -> str:
|
|
148
|
+
match value:
|
|
149
|
+
case float() | int():
|
|
150
|
+
return f"number {value}"
|
|
151
|
+
case None:
|
|
152
|
+
return "nothing"
|
|
153
|
+
case unexpected:
|
|
154
|
+
assert_never(unexpected)
|
|
155
|
+
|
|
156
|
+
assert describe(1) == "number 1" # ✅ `case float():` alone would crash here
|
|
157
|
+
assert describe(1.5) == "number 1.5"
|
|
158
|
+
assert describe(None) == "nothing"
|
|
159
|
+
```
|
|
160
|
+
|
|
139
161
|
### Strongly-Typed IDs
|
|
140
162
|
|
|
141
163
|
Create type-safe identifiers for different entities:
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Frequenz Core Library Release Notes
|
|
2
|
+
|
|
3
|
+
## Upgrading
|
|
4
|
+
|
|
5
|
+
- [`Interval`][frequenz.core.math.Interval]'s type parameter no longer includes `None`. The exported type variable `LessThanComparableOrNoneT` (bound to `LessThanComparable | None`) was deprecated and replaced with `LessThanComparableT` (bound to `LessThanComparable`). `None` is still accepted as a value for `start` / `end` to indicate an unbounded side, but it is treated purely as bound metadata rather than a value in the interval's comparable space.
|
|
6
|
+
|
|
7
|
+
Migration:
|
|
8
|
+
|
|
9
|
+
- `Interval[int | None]` → `Interval[int]`
|
|
10
|
+
- `Interval[LessThanComparable | None]` → `Interval[LessThanComparable]`
|
|
11
|
+
- `LessThanComparableOrNoneT` (importable name) → `LessThanComparableT`
|
|
12
|
+
|
|
13
|
+
Note that explicitly using `Interval[int | None]` still works, as `(int | None) | None` is equivalent to `int | None`, even when it is no longer needed nor recommended. Old code will mostly work, but there is one soft breaking change: `None in some_interval` and `None in some_interval_set` is now a type-check error at call sites, matching the design intent that `None` is a bound marker and never a member value.
|
|
14
|
+
|
|
15
|
+
## New Features
|
|
16
|
+
|
|
17
|
+
- Added [`FloatInt`][frequenz.core.typing.FloatInt], a type alias for `float | int`.
|
|
18
|
+
|
|
19
|
+
[PEP 484](https://peps.python.org/pep-0484/)'s [numeric tower](https://peps.python.org/pep-0484/#the-numeric-tower) makes `int` assignable wherever `float` is annotated, while at runtime `isinstance(1, float)` is `False`. A plain `float` annotation therefore silently admits values that break `match … case float():` arms and `float`-only methods like `hex()`.
|
|
20
|
+
|
|
21
|
+
Annotate with `FloatInt` instead of a plain `float`: the alias docstring documents the trap in detail, including the inherent `bool ⊂ int` leak.
|
|
22
|
+
|
|
23
|
+
- [`is_close_to_zero()`][frequenz.core.math.is_close_to_zero] now annotates its `value` and `abs_tol` parameters as [`FloatInt`][frequenz.core.typing.FloatInt]. This is a pure widening, `int` arguments were always accepted by type checkers, the annotation just didn't admit it.
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
[build-system]
|
|
5
5
|
requires = [
|
|
6
|
-
"setuptools ==
|
|
7
|
-
"setuptools_scm[toml] ==
|
|
8
|
-
"frequenz-repo-config[lib] == 0.
|
|
6
|
+
"setuptools == 83.0.0",
|
|
7
|
+
"setuptools_scm[toml] == 10.2.1",
|
|
8
|
+
"frequenz-repo-config[lib] == 0.18.0",
|
|
9
9
|
]
|
|
10
10
|
build-backend = "setuptools.build_meta"
|
|
11
11
|
|
|
@@ -13,7 +13,8 @@ build-backend = "setuptools.build_meta"
|
|
|
13
13
|
name = "frequenz-core"
|
|
14
14
|
description = "Core utilities to complement Python's standard library"
|
|
15
15
|
readme = "README.md"
|
|
16
|
-
license =
|
|
16
|
+
license = "MIT"
|
|
17
|
+
license-files = ["LICENSE"]
|
|
17
18
|
keywords = [
|
|
18
19
|
"asyncio",
|
|
19
20
|
"collections",
|
|
@@ -30,7 +31,6 @@ keywords = [
|
|
|
30
31
|
classifiers = [
|
|
31
32
|
"Development Status :: 5 - Production/Stable",
|
|
32
33
|
"Intended Audience :: Developers",
|
|
33
|
-
"License :: OSI Approved :: MIT License",
|
|
34
34
|
"Programming Language :: Python :: 3",
|
|
35
35
|
"Programming Language :: Python :: 3 :: Only",
|
|
36
36
|
"Topic :: Software Development :: Libraries",
|
|
@@ -47,44 +47,45 @@ email = "floss@frequenz.com"
|
|
|
47
47
|
[project.optional-dependencies]
|
|
48
48
|
dev-flake8 = [
|
|
49
49
|
"flake8 == 7.3.0",
|
|
50
|
+
"flake8-datetimez == 20.10.0",
|
|
50
51
|
"flake8-docstrings == 1.7.0",
|
|
51
|
-
"flake8-pyproject == 1.2.
|
|
52
|
-
"pydoclint == 0.
|
|
52
|
+
"flake8-pyproject == 1.2.4", # For reading the flake8 config from pyproject.toml
|
|
53
|
+
"pydoclint == 0.9.1",
|
|
53
54
|
"pydocstyle == 6.3.0",
|
|
54
55
|
]
|
|
55
|
-
dev-formatting = ["black ==
|
|
56
|
+
dev-formatting = ["black == 26.5.1", "isort == 8.0.1"]
|
|
56
57
|
dev-mkdocs = [
|
|
57
|
-
"Markdown == 3.
|
|
58
|
-
"black ==
|
|
59
|
-
"mike == 2.
|
|
60
|
-
"mkdocs-gen-files == 0.
|
|
61
|
-
"mkdocs-literate-nav == 0.6.
|
|
62
|
-
"mkdocs-macros-plugin == 1.
|
|
63
|
-
"mkdocs-material == 9.
|
|
64
|
-
"mkdocstrings[python] == 0.
|
|
65
|
-
"mkdocstrings-python ==
|
|
66
|
-
"frequenz-repo-config[lib] == 0.
|
|
58
|
+
"Markdown == 3.10.2",
|
|
59
|
+
"black == 26.5.1",
|
|
60
|
+
"mike == 2.2.0",
|
|
61
|
+
"mkdocs-gen-files == 0.6.1",
|
|
62
|
+
"mkdocs-literate-nav == 0.6.3",
|
|
63
|
+
"mkdocs-macros-plugin == 1.5.0",
|
|
64
|
+
"mkdocs-material == 9.7.7",
|
|
65
|
+
"mkdocstrings[python] == 1.0.6",
|
|
66
|
+
"mkdocstrings-python == 2.0.5",
|
|
67
|
+
"frequenz-repo-config[lib] == 0.18.0",
|
|
67
68
|
]
|
|
68
69
|
dev-mypy = [
|
|
69
|
-
"mypy ==
|
|
70
|
-
"types-Markdown == 3.
|
|
70
|
+
"mypy == 2.3.0",
|
|
71
|
+
"types-Markdown == 3.10.2.20260712",
|
|
71
72
|
# For checking the noxfile, docs/ script, and tests
|
|
72
73
|
"frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
73
74
|
]
|
|
74
|
-
dev-noxfile = ["nox ==
|
|
75
|
+
dev-noxfile = ["nox == 2026.7.11", "frequenz-repo-config[lib] == 0.18.0"]
|
|
75
76
|
dev-pylint = [
|
|
76
77
|
# dev-pytest already defines a dependency to pylint because of the examples
|
|
77
78
|
# For checking the noxfile, docs/ script, and tests
|
|
78
79
|
"frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
79
80
|
]
|
|
80
81
|
dev-pytest = [
|
|
81
|
-
"pytest ==
|
|
82
|
-
"pylint ==
|
|
83
|
-
"frequenz-repo-config[extra-lint-examples] == 0.
|
|
84
|
-
"pytest-mock == 3.
|
|
85
|
-
"pytest-asyncio == 1.
|
|
86
|
-
"async-solipsism == 0.
|
|
87
|
-
"hypothesis == 6.
|
|
82
|
+
"pytest == 9.1.1",
|
|
83
|
+
"pylint == 4.0.6", # We need this to check for the examples
|
|
84
|
+
"frequenz-repo-config[extra-lint-examples] == 0.18.0",
|
|
85
|
+
"pytest-mock == 3.15.1",
|
|
86
|
+
"pytest-asyncio == 1.4.0",
|
|
87
|
+
"async-solipsism == 0.9",
|
|
88
|
+
"hypothesis == 6.163.0",
|
|
88
89
|
]
|
|
89
90
|
dev = [
|
|
90
91
|
"frequenz-core[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]",
|
|
@@ -123,6 +124,10 @@ check-yield-types = false
|
|
|
123
124
|
arg-type-hints-in-docstring = false
|
|
124
125
|
arg-type-hints-in-signature = true
|
|
125
126
|
allow-init-docstring = true
|
|
127
|
+
check-class-attributes = true
|
|
128
|
+
check-style-mismatch = true
|
|
129
|
+
require-inline-class-var-docs = true
|
|
130
|
+
skip-checking-short-docstrings = true
|
|
126
131
|
|
|
127
132
|
[tool.pylint.similarities]
|
|
128
133
|
ignore-comments = ['yes']
|
|
@@ -76,11 +76,13 @@ class DeprecatingEnumType(enum.EnumType):
|
|
|
76
76
|
|
|
77
77
|
Tip:
|
|
78
78
|
Normally it is not necessary to use this class directly, use
|
|
79
|
-
[`Enum`][
|
|
79
|
+
[`Enum`][..Enum] instead.
|
|
80
80
|
|
|
81
81
|
Behavior:
|
|
82
82
|
|
|
83
83
|
- In the class body, members may be declared as `NAME = DeprecatedMember(value, msg)`.
|
|
84
|
+
See [`DeprecatedMember`][..DeprecatedMember] and
|
|
85
|
+
[`deprecated_member()`][..deprecated_member] for details.
|
|
84
86
|
- During class creation, these wrappers are replaced with `value` so that
|
|
85
87
|
a normal enum member or alias is created by [`EnumType`][enum.EnumType].
|
|
86
88
|
- The deprecated names are recorded so that:
|
|
@@ -98,7 +100,7 @@ class DeprecatingEnumType(enum.EnumType):
|
|
|
98
100
|
classdict: Mapping[str, Any],
|
|
99
101
|
**kw: Any,
|
|
100
102
|
) -> type[EnumT]:
|
|
101
|
-
"""Create the new enum class
|
|
103
|
+
"""Create the new enum class rewriting [`DeprecatedMember`][...DeprecatedMember] members."""
|
|
102
104
|
deprecated_names: dict[str, str] = {}
|
|
103
105
|
prepared = super().__prepare__(name, bases, **kw)
|
|
104
106
|
|
|
@@ -20,7 +20,7 @@ the ID and must be unique across all ID types.
|
|
|
20
20
|
|
|
21
21
|
Note:
|
|
22
22
|
The `str_prefix` must be unique across all ID types. If you try to use a
|
|
23
|
-
prefix that is already registered, a
|
|
23
|
+
prefix that is already registered, a warning will be logged when defining
|
|
24
24
|
the class.
|
|
25
25
|
|
|
26
26
|
To encourage consistency, the class name must end with the suffix "Id" (e.g.,
|
|
@@ -61,7 +61,6 @@ Example: Creating an ID type with a non-standard name
|
|
|
61
61
|
```
|
|
62
62
|
'''
|
|
63
63
|
|
|
64
|
-
|
|
65
64
|
import logging
|
|
66
65
|
from typing import Any, ClassVar, Self, cast
|
|
67
66
|
|
|
@@ -157,6 +156,13 @@ class BaseId:
|
|
|
157
156
|
|
|
158
157
|
Equality is defined as being of the exact same type and having the same
|
|
159
158
|
underlying ID.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
other: The object to compare against.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if `other` is of the same type and has the same underlying ID,
|
|
165
|
+
`NotImplemented` if `other` is of a different type.
|
|
160
166
|
"""
|
|
161
167
|
# pylint thinks this is not an unidiomatic typecheck, but in this case
|
|
162
168
|
# it is not. isinstance() returns True for subclasses, which is not
|
|
@@ -173,6 +179,13 @@ class BaseId:
|
|
|
173
179
|
"""Check if this instance is less than another object.
|
|
174
180
|
|
|
175
181
|
Comparison is only defined between instances of the exact same type.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
other: The object to compare against.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
True if this instance is less than `other`, `NotImplemented` if
|
|
188
|
+
`other` is of a different type.
|
|
176
189
|
"""
|
|
177
190
|
# pylint: disable-next=unidiomatic-typecheck
|
|
178
191
|
if type(other) is not type(self):
|
|
@@ -185,6 +198,9 @@ class BaseId:
|
|
|
185
198
|
|
|
186
199
|
The hash is based on the exact type and the underlying ID to ensure
|
|
187
200
|
that IDs of different types but with the same numeric value have different hashes.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
The hash of this instance.
|
|
188
204
|
"""
|
|
189
205
|
return hash((type(self), self._id))
|
|
190
206
|
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
import math
|
|
7
7
|
from dataclasses import dataclass
|
|
8
|
-
from typing import Generic, Protocol, Self, TypeVar
|
|
8
|
+
from typing import Generic, Protocol, Self, TypeVar
|
|
9
9
|
|
|
10
|
+
from .typing import FloatInt
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
def is_close_to_zero(value: FloatInt, abs_tol: FloatInt = 1e-9) -> bool:
|
|
12
14
|
"""Check if a floating point value is close to zero.
|
|
13
15
|
|
|
14
16
|
A value of 1e-9 is a commonly used absolute tolerance to balance precision
|
|
@@ -34,76 +36,68 @@ class LessThanComparable(Protocol):
|
|
|
34
36
|
"""Return whether self is less than other."""
|
|
35
37
|
|
|
36
38
|
|
|
39
|
+
LessThanComparableT = TypeVar("LessThanComparableT", bound=LessThanComparable)
|
|
40
|
+
"""Type variable for a value that is [`LessThanComparable`][..LessThanComparable]."""
|
|
41
|
+
|
|
42
|
+
|
|
37
43
|
LessThanComparableOrNoneT = TypeVar(
|
|
38
44
|
"LessThanComparableOrNoneT", bound=LessThanComparable | None
|
|
39
45
|
)
|
|
40
|
-
"""Type variable for a value that
|
|
46
|
+
"""Type variable for a value that is [`LessThanComparable`][..LessThanComparable] or `None`.
|
|
47
|
+
|
|
48
|
+
Warning: Deprecated
|
|
49
|
+
This type variable is deprecated and it will be removed in a future version. Use
|
|
50
|
+
[`LessThanComparableT`][..LessThanComparableT] instead.
|
|
51
|
+
"""
|
|
41
52
|
|
|
42
53
|
|
|
43
54
|
@dataclass(frozen=True, repr=False)
|
|
44
|
-
class Interval(Generic[
|
|
55
|
+
class Interval(Generic[LessThanComparableT]):
|
|
45
56
|
"""An interval to test if a value is within its limits.
|
|
46
57
|
|
|
47
|
-
The `start
|
|
48
|
-
included in the range when
|
|
58
|
+
The [`.start`][.start] and [`.end`][.end] are inclusive, meaning that the
|
|
59
|
+
[`.start`][.start] and [`.end`][.end] limits are included in the range when
|
|
60
|
+
checking if a value is contained by the interval.
|
|
49
61
|
|
|
50
|
-
If the `start
|
|
51
|
-
direction.
|
|
62
|
+
If the [`.start`][.start] or [`.end`][.end] is `None`, it means that the interval
|
|
63
|
+
is unbounded in that direction. `None` is used purely as a bound marker; it is
|
|
64
|
+
never a value in the interval.
|
|
52
65
|
|
|
53
|
-
If `start
|
|
66
|
+
If [`.start`][.start] is bigger than [`.end`][.end], a `ValueError` is raised.
|
|
54
67
|
|
|
55
68
|
The type stored in the interval must be comparable, meaning that it must implement
|
|
56
69
|
the `__lt__` method to be able to compare values.
|
|
57
70
|
"""
|
|
58
71
|
|
|
59
|
-
start:
|
|
60
|
-
"""The start of the interval."""
|
|
72
|
+
start: LessThanComparableT | None
|
|
73
|
+
"""The start of the interval, or `None` to indicate no lower bound (-∞)."""
|
|
61
74
|
|
|
62
|
-
end:
|
|
63
|
-
"""The end of the interval."""
|
|
75
|
+
end: LessThanComparableT | None
|
|
76
|
+
"""The end of the interval, or `None` to indicate no upper bound (+∞)."""
|
|
64
77
|
|
|
65
78
|
def __post_init__(self) -> None:
|
|
66
79
|
"""Check if the start is less than or equal to the end."""
|
|
67
80
|
if self.start is None or self.end is None:
|
|
68
81
|
return
|
|
69
|
-
start
|
|
70
|
-
end = cast(LessThanComparable, self.end)
|
|
71
|
-
if start > end:
|
|
82
|
+
if self.start > self.end:
|
|
72
83
|
raise ValueError(
|
|
73
84
|
f"The start ({self.start}) can't be bigger than end ({self.end})"
|
|
74
85
|
)
|
|
75
86
|
|
|
76
|
-
def __contains__(self, item:
|
|
77
|
-
"""
|
|
78
|
-
Check if the value is within the range of the container.
|
|
87
|
+
def __contains__(self, item: LessThanComparableT) -> bool:
|
|
88
|
+
"""Check if the value is within the range of the interval.
|
|
79
89
|
|
|
80
90
|
Args:
|
|
81
91
|
item: The value to check.
|
|
82
92
|
|
|
83
93
|
Returns:
|
|
84
|
-
|
|
94
|
+
True if value is within the range, otherwise False.
|
|
85
95
|
"""
|
|
86
|
-
if
|
|
96
|
+
if self.start is not None and item < self.start:
|
|
97
|
+
return False
|
|
98
|
+
if self.end is not None and item > self.end:
|
|
87
99
|
return False
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if self.start is None and self.end is None:
|
|
91
|
-
return True
|
|
92
|
-
if self.start is None:
|
|
93
|
-
start = cast(LessThanComparable, self.end)
|
|
94
|
-
return not casted_item > start
|
|
95
|
-
if self.end is None:
|
|
96
|
-
return not self.start > item
|
|
97
|
-
# mypy seems to get confused here, not being able to narrow start and end to
|
|
98
|
-
# just LessThanComparable, complaining with:
|
|
99
|
-
# error: Unsupported left operand type for <= (some union)
|
|
100
|
-
# But we know if they are not None, they should be LessThanComparable, and
|
|
101
|
-
# actually mypy is being able to figure it out in the lines above, just not in
|
|
102
|
-
# this one, so it should be safe to cast.
|
|
103
|
-
return not (
|
|
104
|
-
casted_item < cast(LessThanComparable, self.start)
|
|
105
|
-
or casted_item > cast(LessThanComparable, self.end)
|
|
106
|
-
)
|
|
100
|
+
return True
|
|
107
101
|
|
|
108
102
|
def __repr__(self) -> str:
|
|
109
103
|
"""Return a string representation of this instance."""
|
|
@@ -11,10 +11,60 @@ It also provides a metaclass used by the decorator to disable the `__init__`:
|
|
|
11
11
|
[`NoInitConstructibleMeta`][frequenz.core.typing.NoInitConstructibleMeta]. This is
|
|
12
12
|
useful mostly for disabling `__init__` while having to use another metaclass too (like
|
|
13
13
|
[`abc.ABCMeta`][abc.ABCMeta]).
|
|
14
|
+
|
|
15
|
+
Finally, it provides [`FloatInt`][frequenz.core.typing.FloatInt], an honest type alias
|
|
16
|
+
for `float | int`, to annotate floating point values that can also be an `int` at
|
|
17
|
+
runtime.
|
|
14
18
|
"""
|
|
15
19
|
|
|
16
20
|
from collections.abc import Callable
|
|
17
|
-
from typing import Any, NoReturn, TypeVar, cast, overload
|
|
21
|
+
from typing import Any, NoReturn, TypeAlias, TypeVar, cast, overload
|
|
22
|
+
|
|
23
|
+
FloatInt: TypeAlias = float | int
|
|
24
|
+
"""A floating point value that can also be an `int` at runtime.
|
|
25
|
+
|
|
26
|
+
[PEP 484's numeric tower](https://peps.python.org/pep-0484/#the-numeric-tower) makes
|
|
27
|
+
`int` assignable to any `float`-annotated parameter, attribute or variable, so a plain
|
|
28
|
+
`float` annotation is a lie: type checkers (even `mypy --strict`) happily accept `int`
|
|
29
|
+
values, but `isinstance(1, float)` is `False` at runtime. This breaks `match … case
|
|
30
|
+
float():` arms (an `int` value falls through to
|
|
31
|
+
[`assert_never()`][typing.assert_never]), calls to `float`-only methods like
|
|
32
|
+
[`hex()`][float.hex], and any other code dispatching on the concrete runtime type.
|
|
33
|
+
|
|
34
|
+
Annotating with this alias instead makes the heterogeneity explicit, so type checkers
|
|
35
|
+
push the code reading these values to handle both branches, typically by matching with
|
|
36
|
+
`case float() | int():`.
|
|
37
|
+
|
|
38
|
+
The full analysis, including the alternatives that were rejected, is recorded in
|
|
39
|
+
[issue #250](https://github.com/frequenz-floss/frequenz-client-common-python/issues/250).
|
|
40
|
+
|
|
41
|
+
Danger:
|
|
42
|
+
`bool` is a subclass of `int`, so `True` and `False` also satisfy this alias. This
|
|
43
|
+
is inherent to Python's type system and is not guarded against.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
```python
|
|
47
|
+
from typing import assert_never
|
|
48
|
+
|
|
49
|
+
from frequenz.core.typing import FloatInt
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def describe(value: FloatInt | None) -> str:
|
|
53
|
+
match value:
|
|
54
|
+
case float() | int():
|
|
55
|
+
return f"number {value}"
|
|
56
|
+
case None:
|
|
57
|
+
return "nothing"
|
|
58
|
+
case unexpected:
|
|
59
|
+
assert_never(unexpected)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
assert describe(1) == "number 1"
|
|
63
|
+
assert describe(1.5) == "number 1.5"
|
|
64
|
+
assert describe(None) == "nothing"
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
67
|
+
|
|
18
68
|
|
|
19
69
|
TypeT = TypeVar("TypeT", bound=type)
|
|
20
70
|
"""A type variable that is bound to a type."""
|
|
@@ -207,7 +257,7 @@ class NoInitConstructibleMeta(type):
|
|
|
207
257
|
class NoInitConstructibleABCMeta(ABCMeta, NoInitConstructibleMeta):
|
|
208
258
|
pass
|
|
209
259
|
|
|
210
|
-
class MyAbstractClass(
|
|
260
|
+
class MyAbstractClass(metaclass=NoInitConstructibleABCMeta):
|
|
211
261
|
@abstractmethod
|
|
212
262
|
def do_something(self) -> None:
|
|
213
263
|
...
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frequenz-core
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Core utilities to complement Python's standard library
|
|
5
5
|
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-core-python/
|
|
8
8
|
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-core-python/releases
|
|
9
9
|
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-core-python/issues
|
|
@@ -12,7 +12,6 @@ Project-URL: Support, https://github.com/frequenz-floss/frequenz-core-python/dis
|
|
|
12
12
|
Keywords: asyncio,collections,core,datetime,frequenz,lib,library,math,python,stdlib,typing
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
15
|
Classifier: Programming Language :: Python :: 3
|
|
17
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
17
|
Classifier: Topic :: Software Development :: Libraries
|
|
@@ -23,41 +22,42 @@ License-File: LICENSE
|
|
|
23
22
|
Requires-Dist: typing-extensions<5,>=4.13.0
|
|
24
23
|
Provides-Extra: dev-flake8
|
|
25
24
|
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
25
|
+
Requires-Dist: flake8-datetimez==20.10.0; extra == "dev-flake8"
|
|
26
26
|
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
27
|
-
Requires-Dist: flake8-pyproject==1.2.
|
|
28
|
-
Requires-Dist: pydoclint==0.
|
|
27
|
+
Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
|
|
28
|
+
Requires-Dist: pydoclint==0.9.1; extra == "dev-flake8"
|
|
29
29
|
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
30
30
|
Provides-Extra: dev-formatting
|
|
31
|
-
Requires-Dist: black==
|
|
32
|
-
Requires-Dist: isort==
|
|
31
|
+
Requires-Dist: black==26.5.1; extra == "dev-formatting"
|
|
32
|
+
Requires-Dist: isort==8.0.1; extra == "dev-formatting"
|
|
33
33
|
Provides-Extra: dev-mkdocs
|
|
34
|
-
Requires-Dist: Markdown==3.
|
|
35
|
-
Requires-Dist: black==
|
|
36
|
-
Requires-Dist: mike==2.
|
|
37
|
-
Requires-Dist: mkdocs-gen-files==0.
|
|
38
|
-
Requires-Dist: mkdocs-literate-nav==0.6.
|
|
39
|
-
Requires-Dist: mkdocs-macros-plugin==1.
|
|
40
|
-
Requires-Dist: mkdocs-material==9.
|
|
41
|
-
Requires-Dist: mkdocstrings[python]==0.
|
|
42
|
-
Requires-Dist: mkdocstrings-python==
|
|
43
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
34
|
+
Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
|
|
35
|
+
Requires-Dist: black==26.5.1; extra == "dev-mkdocs"
|
|
36
|
+
Requires-Dist: mike==2.2.0; extra == "dev-mkdocs"
|
|
37
|
+
Requires-Dist: mkdocs-gen-files==0.6.1; extra == "dev-mkdocs"
|
|
38
|
+
Requires-Dist: mkdocs-literate-nav==0.6.3; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mkdocs-material==9.7.7; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocstrings[python]==1.0.6; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocstrings-python==2.0.5; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-mkdocs"
|
|
44
44
|
Provides-Extra: dev-mypy
|
|
45
|
-
Requires-Dist: mypy==
|
|
46
|
-
Requires-Dist: types-Markdown==3.
|
|
45
|
+
Requires-Dist: mypy==2.3.0; extra == "dev-mypy"
|
|
46
|
+
Requires-Dist: types-Markdown==3.10.2.20260712; extra == "dev-mypy"
|
|
47
47
|
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
48
48
|
Provides-Extra: dev-noxfile
|
|
49
|
-
Requires-Dist: nox==
|
|
50
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
49
|
+
Requires-Dist: nox==2026.7.11; extra == "dev-noxfile"
|
|
50
|
+
Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-noxfile"
|
|
51
51
|
Provides-Extra: dev-pylint
|
|
52
52
|
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
53
53
|
Provides-Extra: dev-pytest
|
|
54
|
-
Requires-Dist: pytest==
|
|
55
|
-
Requires-Dist: pylint==
|
|
56
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.
|
|
57
|
-
Requires-Dist: pytest-mock==3.
|
|
58
|
-
Requires-Dist: pytest-asyncio==1.
|
|
59
|
-
Requires-Dist: async-solipsism==0.
|
|
60
|
-
Requires-Dist: hypothesis==6.
|
|
54
|
+
Requires-Dist: pytest==9.1.1; extra == "dev-pytest"
|
|
55
|
+
Requires-Dist: pylint==4.0.6; extra == "dev-pytest"
|
|
56
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.18.0; extra == "dev-pytest"
|
|
57
|
+
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
58
|
+
Requires-Dist: pytest-asyncio==1.4.0; extra == "dev-pytest"
|
|
59
|
+
Requires-Dist: async-solipsism==0.9; extra == "dev-pytest"
|
|
60
|
+
Requires-Dist: hypothesis==6.163.0; extra == "dev-pytest"
|
|
61
61
|
Provides-Extra: dev
|
|
62
62
|
Requires-Dist: frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
63
|
Dynamic: license-file
|
|
@@ -200,6 +200,28 @@ class ApiClient:
|
|
|
200
200
|
client = ApiClient.create("my-api-key") # ✅ Works
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
+
Annotate floating point values honestly, as Python's numeric tower lets `int`
|
|
204
|
+
values through any `float` annotation:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from typing import assert_never
|
|
208
|
+
|
|
209
|
+
from frequenz.core.typing import FloatInt
|
|
210
|
+
|
|
211
|
+
def describe(value: FloatInt | None) -> str:
|
|
212
|
+
match value:
|
|
213
|
+
case float() | int():
|
|
214
|
+
return f"number {value}"
|
|
215
|
+
case None:
|
|
216
|
+
return "nothing"
|
|
217
|
+
case unexpected:
|
|
218
|
+
assert_never(unexpected)
|
|
219
|
+
|
|
220
|
+
assert describe(1) == "number 1" # ✅ `case float():` alone would crash here
|
|
221
|
+
assert describe(1.5) == "number 1.5"
|
|
222
|
+
assert describe(None) == "nothing"
|
|
223
|
+
```
|
|
224
|
+
|
|
203
225
|
### Strongly-Typed IDs
|
|
204
226
|
|
|
205
227
|
Create type-safe identifiers for different entities:
|
|
@@ -16,4 +16,6 @@ src/frequenz_core.egg-info/PKG-INFO
|
|
|
16
16
|
src/frequenz_core.egg-info/SOURCES.txt
|
|
17
17
|
src/frequenz_core.egg-info/dependency_links.txt
|
|
18
18
|
src/frequenz_core.egg-info/requires.txt
|
|
19
|
+
src/frequenz_core.egg-info/scm_file_list.json
|
|
20
|
+
src/frequenz_core.egg-info/scm_version.json
|
|
19
21
|
src/frequenz_core.egg-info/top_level.txt
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
typing-extensions<5,>=4.13.0
|
|
2
|
+
|
|
3
|
+
[dev]
|
|
4
|
+
frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]
|
|
5
|
+
|
|
6
|
+
[dev-flake8]
|
|
7
|
+
flake8==7.3.0
|
|
8
|
+
flake8-datetimez==20.10.0
|
|
9
|
+
flake8-docstrings==1.7.0
|
|
10
|
+
flake8-pyproject==1.2.4
|
|
11
|
+
pydoclint==0.9.1
|
|
12
|
+
pydocstyle==6.3.0
|
|
13
|
+
|
|
14
|
+
[dev-formatting]
|
|
15
|
+
black==26.5.1
|
|
16
|
+
isort==8.0.1
|
|
17
|
+
|
|
18
|
+
[dev-mkdocs]
|
|
19
|
+
Markdown==3.10.2
|
|
20
|
+
black==26.5.1
|
|
21
|
+
mike==2.2.0
|
|
22
|
+
mkdocs-gen-files==0.6.1
|
|
23
|
+
mkdocs-literate-nav==0.6.3
|
|
24
|
+
mkdocs-macros-plugin==1.5.0
|
|
25
|
+
mkdocs-material==9.7.7
|
|
26
|
+
mkdocstrings[python]==1.0.6
|
|
27
|
+
mkdocstrings-python==2.0.5
|
|
28
|
+
frequenz-repo-config[lib]==0.18.0
|
|
29
|
+
|
|
30
|
+
[dev-mypy]
|
|
31
|
+
mypy==2.3.0
|
|
32
|
+
types-Markdown==3.10.2.20260712
|
|
33
|
+
frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
34
|
+
|
|
35
|
+
[dev-noxfile]
|
|
36
|
+
nox==2026.7.11
|
|
37
|
+
frequenz-repo-config[lib]==0.18.0
|
|
38
|
+
|
|
39
|
+
[dev-pylint]
|
|
40
|
+
frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
41
|
+
|
|
42
|
+
[dev-pytest]
|
|
43
|
+
pytest==9.1.1
|
|
44
|
+
pylint==4.0.6
|
|
45
|
+
frequenz-repo-config[extra-lint-examples]==0.18.0
|
|
46
|
+
pytest-mock==3.15.1
|
|
47
|
+
pytest-asyncio==1.4.0
|
|
48
|
+
async-solipsism==0.9
|
|
49
|
+
hypothesis==6.163.0
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
".cookiecutter-replay.json",
|
|
4
|
+
".editorconfig",
|
|
5
|
+
".github/ISSUE_TEMPLATE/bug.yml",
|
|
6
|
+
".github/ISSUE_TEMPLATE/config.yml",
|
|
7
|
+
".github/ISSUE_TEMPLATE/feature.yml",
|
|
8
|
+
".github/RELEASE_NOTES.template.md",
|
|
9
|
+
".github/dependabot.yml",
|
|
10
|
+
".github/keylabeler.yml",
|
|
11
|
+
".github/labeler.yml",
|
|
12
|
+
".github/workflows/auto-dependabot.yaml",
|
|
13
|
+
".github/workflows/black-migration.yaml",
|
|
14
|
+
".github/workflows/ci-pr.yaml",
|
|
15
|
+
".github/workflows/ci.yaml",
|
|
16
|
+
".github/workflows/dco-merge-queue.yml",
|
|
17
|
+
".github/workflows/isort-migration.yaml",
|
|
18
|
+
".github/workflows/labeler.yml",
|
|
19
|
+
".github/workflows/release-notes-check.yml",
|
|
20
|
+
".github/workflows/repo-config-migration.yaml",
|
|
21
|
+
".gitignore",
|
|
22
|
+
"CODEOWNERS",
|
|
23
|
+
"CONTRIBUTING.md",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"MANIFEST.in",
|
|
26
|
+
"README.md",
|
|
27
|
+
"RELEASE_NOTES.md",
|
|
28
|
+
"docs/CONTRIBUTING.md",
|
|
29
|
+
"docs/SUMMARY.md",
|
|
30
|
+
"docs/_css/mkdocstrings.css",
|
|
31
|
+
"docs/_css/style.css",
|
|
32
|
+
"docs/_img/logo.png",
|
|
33
|
+
"docs/_overrides/main.html",
|
|
34
|
+
"docs/_scripts/mkdocstrings_autoapi.py",
|
|
35
|
+
"docs/index.md",
|
|
36
|
+
"mkdocs.yml",
|
|
37
|
+
"noxfile.py",
|
|
38
|
+
"pyproject.toml",
|
|
39
|
+
"src/frequenz/core/__init__.py",
|
|
40
|
+
"src/frequenz/core/conftest.py",
|
|
41
|
+
"src/frequenz/core/datetime.py",
|
|
42
|
+
"src/frequenz/core/enum.py",
|
|
43
|
+
"src/frequenz/core/id.py",
|
|
44
|
+
"src/frequenz/core/math.py",
|
|
45
|
+
"src/frequenz/core/module.py",
|
|
46
|
+
"src/frequenz/core/py.typed",
|
|
47
|
+
"src/frequenz/core/typing.py",
|
|
48
|
+
"tests/math/test_interval.py",
|
|
49
|
+
"tests/math/test_is_close_to_zero.py",
|
|
50
|
+
"tests/test_enum.py",
|
|
51
|
+
"tests/test_id.py",
|
|
52
|
+
"tests/test_module.py",
|
|
53
|
+
"tests/test_typing.py"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Frequenz Core Library Release Notes
|
|
2
|
-
|
|
3
|
-
## Upgrading
|
|
4
|
-
|
|
5
|
-
- If you used `enum.DeprecatedMember` directly anywhere, you should probably switch to using `enum.deprecated_member` instead, which will tag the member value with the appropriate type.
|
|
6
|
-
|
|
7
|
-
## New Features
|
|
8
|
-
|
|
9
|
-
- A new `enum.deprecated_member` function has been added to create deprecated enum members with proper typing.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
typing-extensions<5,>=4.13.0
|
|
2
|
-
|
|
3
|
-
[dev]
|
|
4
|
-
frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]
|
|
5
|
-
|
|
6
|
-
[dev-flake8]
|
|
7
|
-
flake8==7.3.0
|
|
8
|
-
flake8-docstrings==1.7.0
|
|
9
|
-
flake8-pyproject==1.2.3
|
|
10
|
-
pydoclint==0.6.11
|
|
11
|
-
pydocstyle==6.3.0
|
|
12
|
-
|
|
13
|
-
[dev-formatting]
|
|
14
|
-
black==25.1.0
|
|
15
|
-
isort==6.0.1
|
|
16
|
-
|
|
17
|
-
[dev-mkdocs]
|
|
18
|
-
Markdown==3.8.2
|
|
19
|
-
black==25.1.0
|
|
20
|
-
mike==2.1.3
|
|
21
|
-
mkdocs-gen-files==0.5.0
|
|
22
|
-
mkdocs-literate-nav==0.6.2
|
|
23
|
-
mkdocs-macros-plugin==1.3.9
|
|
24
|
-
mkdocs-material==9.6.18
|
|
25
|
-
mkdocstrings[python]==0.30.0
|
|
26
|
-
mkdocstrings-python==1.18.2
|
|
27
|
-
frequenz-repo-config[lib]==0.13.5
|
|
28
|
-
|
|
29
|
-
[dev-mypy]
|
|
30
|
-
mypy==1.17.1
|
|
31
|
-
types-Markdown==3.8.0.20250809
|
|
32
|
-
frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
33
|
-
|
|
34
|
-
[dev-noxfile]
|
|
35
|
-
nox==2025.5.1
|
|
36
|
-
frequenz-repo-config[lib]==0.13.5
|
|
37
|
-
|
|
38
|
-
[dev-pylint]
|
|
39
|
-
frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
40
|
-
|
|
41
|
-
[dev-pytest]
|
|
42
|
-
pytest==8.4.1
|
|
43
|
-
pylint==3.3.8
|
|
44
|
-
frequenz-repo-config[extra-lint-examples]==0.13.5
|
|
45
|
-
pytest-mock==3.14.1
|
|
46
|
-
pytest-asyncio==1.1.0
|
|
47
|
-
async-solipsism==0.8
|
|
48
|
-
hypothesis==6.138.11
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|