frequenz-core 1.0.2__tar.gz → 1.1.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.1.0/PKG-INFO +236 -0
- frequenz_core-1.1.0/README.md +172 -0
- frequenz_core-1.1.0/RELEASE_NOTES.md +21 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/pyproject.toml +15 -15
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/datetime.py +1 -1
- frequenz_core-1.1.0/src/frequenz/core/enum.py +207 -0
- frequenz_core-1.1.0/src/frequenz_core.egg-info/PKG-INFO +236 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz_core.egg-info/SOURCES.txt +1 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz_core.egg-info/requires.txt +14 -14
- frequenz_core-1.0.2/PKG-INFO +0 -91
- frequenz_core-1.0.2/README.md +0 -27
- frequenz_core-1.0.2/RELEASE_NOTES.md +0 -17
- frequenz_core-1.0.2/src/frequenz_core.egg-info/PKG-INFO +0 -91
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/LICENSE +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/MANIFEST.in +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/setup.cfg +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/__init__.py +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/conftest.py +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/id.py +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/math.py +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/module.py +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/py.typed +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/typing.py +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz_core.egg-info/dependency_links.txt +0 -0
- {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz_core.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frequenz-core
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Core utilities to complement Python's standard library
|
|
5
|
+
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-core-python/
|
|
8
|
+
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-core-python/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-core-python/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-core-python
|
|
11
|
+
Project-URL: Support, https://github.com/frequenz-floss/frequenz-core-python/discussions/categories/support
|
|
12
|
+
Keywords: asyncio,collections,core,datetime,frequenz,lib,library,math,python,stdlib,typing
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: <4,>=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: typing-extensions<5,>=4.13.0
|
|
24
|
+
Provides-Extra: dev-flake8
|
|
25
|
+
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
26
|
+
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
27
|
+
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
28
|
+
Requires-Dist: pydoclint==0.6.6; extra == "dev-flake8"
|
|
29
|
+
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
30
|
+
Provides-Extra: dev-formatting
|
|
31
|
+
Requires-Dist: black==25.1.0; extra == "dev-formatting"
|
|
32
|
+
Requires-Dist: isort==6.0.1; extra == "dev-formatting"
|
|
33
|
+
Provides-Extra: dev-mkdocs
|
|
34
|
+
Requires-Dist: Markdown==3.8.2; extra == "dev-mkdocs"
|
|
35
|
+
Requires-Dist: black==25.1.0; extra == "dev-mkdocs"
|
|
36
|
+
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
37
|
+
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
38
|
+
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: mkdocs-macros-plugin==1.3.7; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mkdocs-material==9.6.16; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocstrings[python]==0.30.0; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocstrings-python==1.17.0; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-mkdocs"
|
|
44
|
+
Provides-Extra: dev-mypy
|
|
45
|
+
Requires-Dist: mypy==1.17.1; extra == "dev-mypy"
|
|
46
|
+
Requires-Dist: types-Markdown==3.8.0.20250708; extra == "dev-mypy"
|
|
47
|
+
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
48
|
+
Provides-Extra: dev-noxfile
|
|
49
|
+
Requires-Dist: nox==2025.5.1; extra == "dev-noxfile"
|
|
50
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-noxfile"
|
|
51
|
+
Provides-Extra: dev-pylint
|
|
52
|
+
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
53
|
+
Provides-Extra: dev-pytest
|
|
54
|
+
Requires-Dist: pytest==8.4.1; extra == "dev-pytest"
|
|
55
|
+
Requires-Dist: pylint==3.3.7; extra == "dev-pytest"
|
|
56
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.5; extra == "dev-pytest"
|
|
57
|
+
Requires-Dist: pytest-mock==3.14.1; extra == "dev-pytest"
|
|
58
|
+
Requires-Dist: pytest-asyncio==1.1.0; extra == "dev-pytest"
|
|
59
|
+
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
60
|
+
Requires-Dist: hypothesis==6.138.2; extra == "dev-pytest"
|
|
61
|
+
Provides-Extra: dev
|
|
62
|
+
Requires-Dist: frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
|
+
Dynamic: license-file
|
|
64
|
+
|
|
65
|
+
# Frequenz Core Library
|
|
66
|
+
|
|
67
|
+
[](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
|
|
68
|
+
[](https://pypi.org/project/frequenz-core/)
|
|
69
|
+
[](https://frequenz-floss.github.io/frequenz-core-python/)
|
|
70
|
+
|
|
71
|
+
## Introduction
|
|
72
|
+
|
|
73
|
+
Core utilities to complement Python's standard library. This library provides
|
|
74
|
+
essential building blocks for Python applications, including mathematical
|
|
75
|
+
utilities, datetime constants, typing helpers, strongly-typed identifiers, and
|
|
76
|
+
module introspection tools.
|
|
77
|
+
|
|
78
|
+
The `frequenz-core` library is designed to be lightweight, type-safe, and
|
|
79
|
+
follow modern Python best practices. It fills common gaps in the standard
|
|
80
|
+
library with utilities that are frequently needed across different projects.
|
|
81
|
+
|
|
82
|
+
## Supported Platforms
|
|
83
|
+
|
|
84
|
+
The following platforms are officially supported (tested):
|
|
85
|
+
|
|
86
|
+
- **Python:** 3.11
|
|
87
|
+
- **Operating System:** Ubuntu Linux 20.04
|
|
88
|
+
- **Architectures:** amd64, arm64
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
You can install the library from PyPI using pip:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python -m pip install frequenz-core
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or add it to your project's dependencies in `pyproject.toml`:
|
|
99
|
+
|
|
100
|
+
```toml
|
|
101
|
+
[project]
|
|
102
|
+
dependencies = [
|
|
103
|
+
"frequenz-core >= 1.0.2, < 2",
|
|
104
|
+
]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> [!NOTE]
|
|
108
|
+
> We recommend pinning the dependency to the latest version for programs,
|
|
109
|
+
> like `"frequenz-core == 1.0.2"`, and specifying a version range spanning
|
|
110
|
+
> one major version for libraries, like `"frequenz-core >= 1.0.2, < 2"`.
|
|
111
|
+
> We follow [semver](https://semver.org/).
|
|
112
|
+
|
|
113
|
+
## Quick Start
|
|
114
|
+
|
|
115
|
+
Here's a quick overview of the main functionality:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from frequenz.core.math import is_close_to_zero, Interval
|
|
119
|
+
from frequenz.core.datetime import UNIX_EPOCH
|
|
120
|
+
from frequenz.core.module import get_public_module_name
|
|
121
|
+
|
|
122
|
+
# Math utilities
|
|
123
|
+
print(is_close_to_zero(1e-10)) # True - check if float is close to zero
|
|
124
|
+
interval = Interval(1, 10)
|
|
125
|
+
print(5 in interval) # True - check if value is in range
|
|
126
|
+
|
|
127
|
+
# Datetime utilities
|
|
128
|
+
print(UNIX_EPOCH) # 1970-01-01 00:00:00+00:00
|
|
129
|
+
|
|
130
|
+
# Module utilities
|
|
131
|
+
public_name = get_public_module_name("my.package._private.module")
|
|
132
|
+
print(public_name) # "my.package"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Code Examples
|
|
136
|
+
|
|
137
|
+
### Math Utilities
|
|
138
|
+
|
|
139
|
+
The math module provides utilities for floating-point comparisons and interval
|
|
140
|
+
checking:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from frequenz.core.math import is_close_to_zero, Interval
|
|
144
|
+
|
|
145
|
+
# Robust floating-point zero comparison
|
|
146
|
+
assert is_close_to_zero(1e-10) # True
|
|
147
|
+
assert not is_close_to_zero(0.1) # False
|
|
148
|
+
|
|
149
|
+
# Interval checking with inclusive bounds
|
|
150
|
+
numbers = Interval(0, 100)
|
|
151
|
+
assert 50 in numbers # True
|
|
152
|
+
assert not (150 in numbers) # False - 150 is outside the interval
|
|
153
|
+
|
|
154
|
+
# Unbounded intervals
|
|
155
|
+
positive = Interval(0, None) # [0, ∞]
|
|
156
|
+
assert 1000 in positive # True
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `Enum` with deprecated members
|
|
160
|
+
|
|
161
|
+
Define enums with deprecated members that raise deprecation warnings when
|
|
162
|
+
accessed:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from frequenz.core.enum import Enum, DeprecatedMember
|
|
166
|
+
|
|
167
|
+
class TaskStatus(Enum):
|
|
168
|
+
OPEN = 1
|
|
169
|
+
IN_PROGRESS = 2
|
|
170
|
+
PENDING = DeprecatedMember(1, "PENDING is deprecated, use OPEN instead")
|
|
171
|
+
DONE = DeprecatedMember(3, "DONE is deprecated, use FINISHED instead")
|
|
172
|
+
FINISHED = 4
|
|
173
|
+
|
|
174
|
+
status1 = TaskStatus.PENDING # Warns: "PENDING is deprecated, use OPEN instead"
|
|
175
|
+
assert status1 is TaskStatus.OPEN
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Typing Utilities
|
|
179
|
+
|
|
180
|
+
Disable class constructors to enforce factory pattern usage:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from frequenz.core.typing import disable_init
|
|
184
|
+
|
|
185
|
+
@disable_init
|
|
186
|
+
class ApiClient:
|
|
187
|
+
@classmethod
|
|
188
|
+
def create(cls, api_key: str) -> "ApiClient":
|
|
189
|
+
# Factory method with validation
|
|
190
|
+
instance = cls.__new__(cls)
|
|
191
|
+
# Custom initialization logic here
|
|
192
|
+
return instance
|
|
193
|
+
|
|
194
|
+
# This will raise TypeError:
|
|
195
|
+
# client = ApiClient() # ❌ TypeError
|
|
196
|
+
|
|
197
|
+
# Use factory method instead:
|
|
198
|
+
client = ApiClient.create("my-api-key") # ✅ Works
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Strongly-Typed IDs
|
|
202
|
+
|
|
203
|
+
Create type-safe identifiers for different entities:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from frequenz.core.id import BaseId
|
|
207
|
+
|
|
208
|
+
class UserId(BaseId, str_prefix="USR"):
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
class OrderId(BaseId, str_prefix="ORD"):
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
user_id = UserId(123)
|
|
215
|
+
order_id = OrderId(456)
|
|
216
|
+
|
|
217
|
+
print(f"User: {user_id}") # User: USR123
|
|
218
|
+
print(f"Order: {order_id}") # Order: ORD456
|
|
219
|
+
|
|
220
|
+
# Type safety prevents mixing different ID types
|
|
221
|
+
def process_user(user_id: UserId) -> None:
|
|
222
|
+
print(f"Processing user: {user_id}")
|
|
223
|
+
|
|
224
|
+
process_user(user_id) # ✅ Works
|
|
225
|
+
# process_user(order_id) # ❌ Type error
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Documentation
|
|
229
|
+
|
|
230
|
+
For information on how to use this library, please refer to the
|
|
231
|
+
[documentation](https://frequenz-floss.github.io/frequenz-core-python/).
|
|
232
|
+
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
If you want to know how to build this project and contribute to it, please
|
|
236
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Frequenz Core Library
|
|
2
|
+
|
|
3
|
+
[](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
|
|
4
|
+
[](https://pypi.org/project/frequenz-core/)
|
|
5
|
+
[](https://frequenz-floss.github.io/frequenz-core-python/)
|
|
6
|
+
|
|
7
|
+
## Introduction
|
|
8
|
+
|
|
9
|
+
Core utilities to complement Python's standard library. This library provides
|
|
10
|
+
essential building blocks for Python applications, including mathematical
|
|
11
|
+
utilities, datetime constants, typing helpers, strongly-typed identifiers, and
|
|
12
|
+
module introspection tools.
|
|
13
|
+
|
|
14
|
+
The `frequenz-core` library is designed to be lightweight, type-safe, and
|
|
15
|
+
follow modern Python best practices. It fills common gaps in the standard
|
|
16
|
+
library with utilities that are frequently needed across different projects.
|
|
17
|
+
|
|
18
|
+
## Supported Platforms
|
|
19
|
+
|
|
20
|
+
The following platforms are officially supported (tested):
|
|
21
|
+
|
|
22
|
+
- **Python:** 3.11
|
|
23
|
+
- **Operating System:** Ubuntu Linux 20.04
|
|
24
|
+
- **Architectures:** amd64, arm64
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
You can install the library from PyPI using pip:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python -m pip install frequenz-core
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or add it to your project's dependencies in `pyproject.toml`:
|
|
35
|
+
|
|
36
|
+
```toml
|
|
37
|
+
[project]
|
|
38
|
+
dependencies = [
|
|
39
|
+
"frequenz-core >= 1.0.2, < 2",
|
|
40
|
+
]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> [!NOTE]
|
|
44
|
+
> We recommend pinning the dependency to the latest version for programs,
|
|
45
|
+
> like `"frequenz-core == 1.0.2"`, and specifying a version range spanning
|
|
46
|
+
> one major version for libraries, like `"frequenz-core >= 1.0.2, < 2"`.
|
|
47
|
+
> We follow [semver](https://semver.org/).
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
Here's a quick overview of the main functionality:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from frequenz.core.math import is_close_to_zero, Interval
|
|
55
|
+
from frequenz.core.datetime import UNIX_EPOCH
|
|
56
|
+
from frequenz.core.module import get_public_module_name
|
|
57
|
+
|
|
58
|
+
# Math utilities
|
|
59
|
+
print(is_close_to_zero(1e-10)) # True - check if float is close to zero
|
|
60
|
+
interval = Interval(1, 10)
|
|
61
|
+
print(5 in interval) # True - check if value is in range
|
|
62
|
+
|
|
63
|
+
# Datetime utilities
|
|
64
|
+
print(UNIX_EPOCH) # 1970-01-01 00:00:00+00:00
|
|
65
|
+
|
|
66
|
+
# Module utilities
|
|
67
|
+
public_name = get_public_module_name("my.package._private.module")
|
|
68
|
+
print(public_name) # "my.package"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Code Examples
|
|
72
|
+
|
|
73
|
+
### Math Utilities
|
|
74
|
+
|
|
75
|
+
The math module provides utilities for floating-point comparisons and interval
|
|
76
|
+
checking:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from frequenz.core.math import is_close_to_zero, Interval
|
|
80
|
+
|
|
81
|
+
# Robust floating-point zero comparison
|
|
82
|
+
assert is_close_to_zero(1e-10) # True
|
|
83
|
+
assert not is_close_to_zero(0.1) # False
|
|
84
|
+
|
|
85
|
+
# Interval checking with inclusive bounds
|
|
86
|
+
numbers = Interval(0, 100)
|
|
87
|
+
assert 50 in numbers # True
|
|
88
|
+
assert not (150 in numbers) # False - 150 is outside the interval
|
|
89
|
+
|
|
90
|
+
# Unbounded intervals
|
|
91
|
+
positive = Interval(0, None) # [0, ∞]
|
|
92
|
+
assert 1000 in positive # True
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `Enum` with deprecated members
|
|
96
|
+
|
|
97
|
+
Define enums with deprecated members that raise deprecation warnings when
|
|
98
|
+
accessed:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from frequenz.core.enum import Enum, DeprecatedMember
|
|
102
|
+
|
|
103
|
+
class TaskStatus(Enum):
|
|
104
|
+
OPEN = 1
|
|
105
|
+
IN_PROGRESS = 2
|
|
106
|
+
PENDING = DeprecatedMember(1, "PENDING is deprecated, use OPEN instead")
|
|
107
|
+
DONE = DeprecatedMember(3, "DONE is deprecated, use FINISHED instead")
|
|
108
|
+
FINISHED = 4
|
|
109
|
+
|
|
110
|
+
status1 = TaskStatus.PENDING # Warns: "PENDING is deprecated, use OPEN instead"
|
|
111
|
+
assert status1 is TaskStatus.OPEN
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Typing Utilities
|
|
115
|
+
|
|
116
|
+
Disable class constructors to enforce factory pattern usage:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from frequenz.core.typing import disable_init
|
|
120
|
+
|
|
121
|
+
@disable_init
|
|
122
|
+
class ApiClient:
|
|
123
|
+
@classmethod
|
|
124
|
+
def create(cls, api_key: str) -> "ApiClient":
|
|
125
|
+
# Factory method with validation
|
|
126
|
+
instance = cls.__new__(cls)
|
|
127
|
+
# Custom initialization logic here
|
|
128
|
+
return instance
|
|
129
|
+
|
|
130
|
+
# This will raise TypeError:
|
|
131
|
+
# client = ApiClient() # ❌ TypeError
|
|
132
|
+
|
|
133
|
+
# Use factory method instead:
|
|
134
|
+
client = ApiClient.create("my-api-key") # ✅ Works
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Strongly-Typed IDs
|
|
138
|
+
|
|
139
|
+
Create type-safe identifiers for different entities:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from frequenz.core.id import BaseId
|
|
143
|
+
|
|
144
|
+
class UserId(BaseId, str_prefix="USR"):
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
class OrderId(BaseId, str_prefix="ORD"):
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
user_id = UserId(123)
|
|
151
|
+
order_id = OrderId(456)
|
|
152
|
+
|
|
153
|
+
print(f"User: {user_id}") # User: USR123
|
|
154
|
+
print(f"Order: {order_id}") # Order: ORD456
|
|
155
|
+
|
|
156
|
+
# Type safety prevents mixing different ID types
|
|
157
|
+
def process_user(user_id: UserId) -> None:
|
|
158
|
+
print(f"Processing user: {user_id}")
|
|
159
|
+
|
|
160
|
+
process_user(user_id) # ✅ Works
|
|
161
|
+
# process_user(order_id) # ❌ Type error
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Documentation
|
|
165
|
+
|
|
166
|
+
For information on how to use this library, please refer to the
|
|
167
|
+
[documentation](https://frequenz-floss.github.io/frequenz-core-python/).
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
If you want to know how to build this project and contribute to it, please
|
|
172
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Frequenz Core Library Release Notes
|
|
2
|
+
|
|
3
|
+
## New Features
|
|
4
|
+
|
|
5
|
+
- A new `frequenz.core.enum` module was added, providing a drop-in replacement `Enum` that supports deprecating members.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from frequenz.core.enum import Enum, DeprecatedMember
|
|
11
|
+
|
|
12
|
+
class TaskStatus(Enum):
|
|
13
|
+
OPEN = 1
|
|
14
|
+
IN_PROGRESS = 2
|
|
15
|
+
PENDING = DeprecatedMember(1, "PENDING is deprecated, use OPEN instead")
|
|
16
|
+
DONE = DeprecatedMember(3, "DONE is deprecated, use FINISHED instead")
|
|
17
|
+
FINISHED = 4
|
|
18
|
+
|
|
19
|
+
status1 = TaskStatus.PENDING # Warns: "PENDING is deprecated, use OPEN instead"
|
|
20
|
+
assert status1 is TaskStatus.OPEN
|
|
21
|
+
```
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
requires = [
|
|
6
6
|
"setuptools == 80.9.0",
|
|
7
7
|
"setuptools_scm[toml] == 8.3.1",
|
|
8
|
-
"frequenz-repo-config[lib] == 0.13.
|
|
8
|
+
"frequenz-repo-config[lib] == 0.13.5",
|
|
9
9
|
]
|
|
10
10
|
build-backend = "setuptools.build_meta"
|
|
11
11
|
|
|
@@ -46,7 +46,7 @@ email = "floss@frequenz.com"
|
|
|
46
46
|
|
|
47
47
|
[project.optional-dependencies]
|
|
48
48
|
dev-flake8 = [
|
|
49
|
-
"flake8 == 7.
|
|
49
|
+
"flake8 == 7.3.0",
|
|
50
50
|
"flake8-docstrings == 1.7.0",
|
|
51
51
|
"flake8-pyproject == 1.2.3", # For reading the flake8 config from pyproject.toml
|
|
52
52
|
"pydoclint == 0.6.6",
|
|
@@ -54,37 +54,37 @@ dev-flake8 = [
|
|
|
54
54
|
]
|
|
55
55
|
dev-formatting = ["black == 25.1.0", "isort == 6.0.1"]
|
|
56
56
|
dev-mkdocs = [
|
|
57
|
-
"Markdown == 3.8",
|
|
57
|
+
"Markdown == 3.8.2",
|
|
58
58
|
"black == 25.1.0",
|
|
59
59
|
"mike == 2.1.3",
|
|
60
60
|
"mkdocs-gen-files == 0.5.0",
|
|
61
61
|
"mkdocs-literate-nav == 0.6.2",
|
|
62
62
|
"mkdocs-macros-plugin == 1.3.7",
|
|
63
|
-
"mkdocs-material == 9.6.
|
|
64
|
-
"mkdocstrings[python] == 0.
|
|
65
|
-
"mkdocstrings-python == 1.
|
|
66
|
-
"frequenz-repo-config[lib] == 0.13.
|
|
63
|
+
"mkdocs-material == 9.6.16",
|
|
64
|
+
"mkdocstrings[python] == 0.30.0",
|
|
65
|
+
"mkdocstrings-python == 1.17.0",
|
|
66
|
+
"frequenz-repo-config[lib] == 0.13.5",
|
|
67
67
|
]
|
|
68
68
|
dev-mypy = [
|
|
69
|
-
"mypy == 1.
|
|
70
|
-
"types-Markdown == 3.8.0.
|
|
69
|
+
"mypy == 1.17.1",
|
|
70
|
+
"types-Markdown == 3.8.0.20250708",
|
|
71
71
|
# For checking the noxfile, docs/ script, and tests
|
|
72
72
|
"frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
73
73
|
]
|
|
74
|
-
dev-noxfile = ["nox == 2025.5.1", "frequenz-repo-config[lib] == 0.13.
|
|
74
|
+
dev-noxfile = ["nox == 2025.5.1", "frequenz-repo-config[lib] == 0.13.5"]
|
|
75
75
|
dev-pylint = [
|
|
76
76
|
# dev-pytest already defines a dependency to pylint because of the examples
|
|
77
77
|
# For checking the noxfile, docs/ script, and tests
|
|
78
78
|
"frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
79
79
|
]
|
|
80
80
|
dev-pytest = [
|
|
81
|
-
"pytest == 8.
|
|
81
|
+
"pytest == 8.4.1",
|
|
82
82
|
"pylint == 3.3.7", # We need this to check for the examples
|
|
83
|
-
"frequenz-repo-config[extra-lint-examples] == 0.13.
|
|
83
|
+
"frequenz-repo-config[extra-lint-examples] == 0.13.5",
|
|
84
84
|
"pytest-mock == 3.14.1",
|
|
85
|
-
"pytest-asyncio ==
|
|
86
|
-
"async-solipsism == 0.
|
|
87
|
-
"hypothesis == 6.
|
|
85
|
+
"pytest-asyncio == 1.1.0",
|
|
86
|
+
"async-solipsism == 0.8",
|
|
87
|
+
"hypothesis == 6.138.2",
|
|
88
88
|
]
|
|
89
89
|
dev = [
|
|
90
90
|
"frequenz-core[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]",
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Enum utilities with support for deprecated members.
|
|
5
|
+
|
|
6
|
+
This module provides an [`Enum`][frequenz.core.enum.Enum] base class that extends the
|
|
7
|
+
standard library's [`enum.Enum`][] to support marking certain members as deprecated.
|
|
8
|
+
|
|
9
|
+
See the [class documentation][frequenz.core.enum.Enum] for details and examples.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import enum
|
|
15
|
+
import warnings
|
|
16
|
+
from collections.abc import Mapping
|
|
17
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Self, TypeVar, cast
|
|
18
|
+
|
|
19
|
+
# Note: This module contains more casts and uses of Any than what's typically
|
|
20
|
+
# ideal. This is because type hinting EnumType and Enum subclasses is quite
|
|
21
|
+
# challenging, as there is a lot of special behavior in `mypy` for these classes.
|
|
22
|
+
#
|
|
23
|
+
# The resulting enum should be treated as a regular enum by mypy, so hopefully everthing
|
|
24
|
+
# still works as expected.
|
|
25
|
+
|
|
26
|
+
EnumT = TypeVar("EnumT", bound=enum.Enum)
|
|
27
|
+
"""Type variable for enum types."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DeprecatedMemberWarning(DeprecationWarning):
|
|
31
|
+
"""Warning category for deprecated enum members."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DeprecatedMember:
|
|
35
|
+
"""Marker used in enum class bodies to declare deprecated members.
|
|
36
|
+
|
|
37
|
+
Please read the [`Enum`][frequenz.core.enum.Enum] documentation for details and
|
|
38
|
+
examples.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Using slots is just an optimization to make the class more lightweight and avoid
|
|
42
|
+
# the creation of a `__dict__` for each instance and its corresponding lookup.
|
|
43
|
+
__slots__ = ("value", "message")
|
|
44
|
+
|
|
45
|
+
def __init__(self, value: Any, message: str) -> None:
|
|
46
|
+
"""Initialize this instance."""
|
|
47
|
+
self.value = value
|
|
48
|
+
self.message = message
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DeprecatingEnumType(enum.EnumType):
|
|
52
|
+
"""Enum metaclass that supports `DeprecatedMember` wrappers.
|
|
53
|
+
|
|
54
|
+
Tip:
|
|
55
|
+
Normally it is not necessary to use this class directly, use
|
|
56
|
+
[`Enum`][frequenz.core.enum.Enum] instead.
|
|
57
|
+
|
|
58
|
+
Behavior:
|
|
59
|
+
|
|
60
|
+
- In the class body, members may be declared as `NAME = DeprecatedMember(value, msg)`.
|
|
61
|
+
- During class creation, these wrappers are replaced with `value` so that
|
|
62
|
+
a normal enum member or alias is created by [`EnumType`][enum.EnumType].
|
|
63
|
+
- The deprecated names are recorded so that:
|
|
64
|
+
|
|
65
|
+
* `MyEnum.NAME` warns (attribute access by name)
|
|
66
|
+
* `MyEnum["NAME"]` warns (lookup by name)
|
|
67
|
+
* `MyEnum(value)` warns **only if** the resolved member has **no**
|
|
68
|
+
non-deprecated aliases (all names for that member are deprecated).
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __new__( # pylint: disable=too-many-locals
|
|
72
|
+
mcs,
|
|
73
|
+
name: str,
|
|
74
|
+
bases: tuple[type[EnumT], ...],
|
|
75
|
+
classdict: Mapping[str, Any],
|
|
76
|
+
**kw: Any,
|
|
77
|
+
) -> type[EnumT]:
|
|
78
|
+
"""Create the new enum class, rewriting `DeprecatedMember` instances."""
|
|
79
|
+
deprecated_names: dict[str, str] = {}
|
|
80
|
+
prepared = super().__prepare__(name, bases, **kw)
|
|
81
|
+
|
|
82
|
+
# Unwrap DeprecatedMembers and record them as deprecated
|
|
83
|
+
for key, value in classdict.items():
|
|
84
|
+
if isinstance(value, DeprecatedMember):
|
|
85
|
+
deprecated_names[key] = value.message
|
|
86
|
+
prepared[key] = value.value
|
|
87
|
+
else:
|
|
88
|
+
prepared[key] = value
|
|
89
|
+
|
|
90
|
+
cls = cast(type[EnumT], super().__new__(mcs, name, bases, prepared, **kw))
|
|
91
|
+
|
|
92
|
+
# Build alias groups: member -> list of names
|
|
93
|
+
member_to_names: dict[EnumT, list[str]] = {}
|
|
94
|
+
member: EnumT
|
|
95
|
+
for member_name, member in cls.__members__.items():
|
|
96
|
+
member_to_names.setdefault(member, []).append(member_name)
|
|
97
|
+
|
|
98
|
+
warned_by_member: dict[EnumT, str] = {}
|
|
99
|
+
for member, names in member_to_names.items():
|
|
100
|
+
# warn on value only if all alias names are deprecated
|
|
101
|
+
deprecated_aliases = [n for n in names if n in deprecated_names]
|
|
102
|
+
if deprecated_aliases and len(deprecated_aliases) == len(names):
|
|
103
|
+
warned_by_member[member] = deprecated_names[deprecated_aliases[0]]
|
|
104
|
+
|
|
105
|
+
# Inject maps quietly
|
|
106
|
+
type.__setattr__(cls, "__deprecated_names__", deprecated_names)
|
|
107
|
+
type.__setattr__(cls, "__deprecated_value_map__", warned_by_member)
|
|
108
|
+
|
|
109
|
+
return cls
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def _name_map(cls_: type[Any]) -> Mapping[str, str]:
|
|
113
|
+
"""Map from member names to deprecation messages."""
|
|
114
|
+
return cast(
|
|
115
|
+
Mapping[str, str],
|
|
116
|
+
type.__getattribute__(cls_, "__dict__").get("__deprecated_names__", {}),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _value_map(cls_: type[Any]) -> Mapping[Any, str]:
|
|
121
|
+
"""Map from enum members to deprecation messages."""
|
|
122
|
+
return cast(
|
|
123
|
+
Mapping[Any, str],
|
|
124
|
+
type.__getattribute__(cls_, "__dict__").get("__deprecated_value_map__", {}),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def __getattribute__(cls, name: str) -> Any:
|
|
128
|
+
"""Resolve `name` to a member, warning if the member is deprecated."""
|
|
129
|
+
if name in ("__deprecated_names__", "__deprecated_value_map__"):
|
|
130
|
+
return type.__getattribute__(cls, name)
|
|
131
|
+
deprecated = DeprecatingEnumType._name_map(cls)
|
|
132
|
+
if name in deprecated:
|
|
133
|
+
warnings.warn(deprecated[name], DeprecatedMemberWarning, stacklevel=2)
|
|
134
|
+
return super().__getattribute__(name)
|
|
135
|
+
|
|
136
|
+
def __getitem__(cls, name: str) -> Any:
|
|
137
|
+
"""Resolve `name` to a member, warning if the member is deprecated."""
|
|
138
|
+
deprecated = DeprecatingEnumType._name_map(cls)
|
|
139
|
+
if name in deprecated:
|
|
140
|
+
warnings.warn(deprecated[name], DeprecatedMemberWarning, stacklevel=2)
|
|
141
|
+
return super().__getitem__(name)
|
|
142
|
+
|
|
143
|
+
def __call__(cls, value: Any, *args: Any, **kwargs: Any) -> Any:
|
|
144
|
+
"""Resolve `value` to a member, warning if the member is purely deprecated."""
|
|
145
|
+
member = super().__call__(value, *args, **kwargs)
|
|
146
|
+
value_map: Mapping[Any, str] = DeprecatingEnumType._value_map(cls)
|
|
147
|
+
msg = value_map.get(member)
|
|
148
|
+
if msg is not None:
|
|
149
|
+
warnings.warn(msg, DeprecatedMemberWarning, stacklevel=2)
|
|
150
|
+
return member
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if TYPE_CHECKING:
|
|
154
|
+
# Make type checkers treat it as a plain Enum (so member checks work), if we don't
|
|
155
|
+
# do this, mypy will consider the resulting enum completely dynamic and never
|
|
156
|
+
# complain if an unexisting member is accessed.
|
|
157
|
+
|
|
158
|
+
# pylint: disable-next=missing-class-docstring
|
|
159
|
+
class Enum(enum.Enum): # noqa
|
|
160
|
+
__deprecated_names__: ClassVar[Mapping[str, str]]
|
|
161
|
+
__deprecated_value_map__: ClassVar[Mapping[Enum, str]]
|
|
162
|
+
|
|
163
|
+
else:
|
|
164
|
+
|
|
165
|
+
class Enum(enum.Enum, metaclass=DeprecatingEnumType):
|
|
166
|
+
"""Base class for enums that support DeprecatedMember.
|
|
167
|
+
|
|
168
|
+
This class extends the standard library's [`enum.Enum`][] to support marking
|
|
169
|
+
certain members as deprecated. Deprecated members can be accessed, but doing so
|
|
170
|
+
will emit a [`DeprecationWarning`][], specifically
|
|
171
|
+
a [`DeprecatedMemberWarning`][frequenz.core.enum.DeprecatedMemberWarning].
|
|
172
|
+
|
|
173
|
+
To declare a deprecated member, use the
|
|
174
|
+
[`DeprecatedMember`][frequenz.core.enum.DeprecatedMember] wrapper in the class body.
|
|
175
|
+
|
|
176
|
+
When using the enum constructor (i.e. `MyEnum(value)`), a warning is only emitted if
|
|
177
|
+
the resolved member has no non-deprecated aliases. If there is at least one
|
|
178
|
+
non-deprecated alias for the member, no warning is emitted.
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
```python
|
|
182
|
+
from frequenz.core.enum import Enum, DeprecatedMember
|
|
183
|
+
|
|
184
|
+
class TaskStatus(Enum):
|
|
185
|
+
OPEN = 1
|
|
186
|
+
IN_PROGRESS = 2
|
|
187
|
+
PENDING = DeprecatedMember(1, "PENDING is deprecated, use OPEN instead")
|
|
188
|
+
DONE = DeprecatedMember(3, "DONE is deprecated, use FINISHED instead")
|
|
189
|
+
FINISHED = 4
|
|
190
|
+
|
|
191
|
+
# Accessing deprecated members:
|
|
192
|
+
status1 = TaskStatus.PENDING # Warns: "PENDING is deprecated, use OPEN instead"
|
|
193
|
+
assert status1 is TaskStatus.OPEN
|
|
194
|
+
|
|
195
|
+
status2 = TaskStatus["DONE"] # Warns: "DONE is deprecated, use FINISHED instead"
|
|
196
|
+
assert status2 is TaskStatus.FINISHED
|
|
197
|
+
|
|
198
|
+
status3 = TaskStatus(1) # No warning, resolves to OPEN which has a non-deprecated alias
|
|
199
|
+
assert status3 is TaskStatus.OPEN
|
|
200
|
+
|
|
201
|
+
status4 = TaskStatus(3) # Warns: "DONE is deprecated, use FINISHED instead"
|
|
202
|
+
assert status4 is TaskStatus.FINISHED
|
|
203
|
+
```
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
__deprecated_names__: ClassVar[Mapping[str, str]]
|
|
207
|
+
__deprecated_value_map__: ClassVar[Mapping[Self, str]]
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frequenz-core
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Core utilities to complement Python's standard library
|
|
5
|
+
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-core-python/
|
|
8
|
+
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-core-python/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-core-python/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-core-python
|
|
11
|
+
Project-URL: Support, https://github.com/frequenz-floss/frequenz-core-python/discussions/categories/support
|
|
12
|
+
Keywords: asyncio,collections,core,datetime,frequenz,lib,library,math,python,stdlib,typing
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: <4,>=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: typing-extensions<5,>=4.13.0
|
|
24
|
+
Provides-Extra: dev-flake8
|
|
25
|
+
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
26
|
+
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
27
|
+
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
28
|
+
Requires-Dist: pydoclint==0.6.6; extra == "dev-flake8"
|
|
29
|
+
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
30
|
+
Provides-Extra: dev-formatting
|
|
31
|
+
Requires-Dist: black==25.1.0; extra == "dev-formatting"
|
|
32
|
+
Requires-Dist: isort==6.0.1; extra == "dev-formatting"
|
|
33
|
+
Provides-Extra: dev-mkdocs
|
|
34
|
+
Requires-Dist: Markdown==3.8.2; extra == "dev-mkdocs"
|
|
35
|
+
Requires-Dist: black==25.1.0; extra == "dev-mkdocs"
|
|
36
|
+
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
37
|
+
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
38
|
+
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: mkdocs-macros-plugin==1.3.7; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mkdocs-material==9.6.16; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocstrings[python]==0.30.0; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocstrings-python==1.17.0; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-mkdocs"
|
|
44
|
+
Provides-Extra: dev-mypy
|
|
45
|
+
Requires-Dist: mypy==1.17.1; extra == "dev-mypy"
|
|
46
|
+
Requires-Dist: types-Markdown==3.8.0.20250708; extra == "dev-mypy"
|
|
47
|
+
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
48
|
+
Provides-Extra: dev-noxfile
|
|
49
|
+
Requires-Dist: nox==2025.5.1; extra == "dev-noxfile"
|
|
50
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-noxfile"
|
|
51
|
+
Provides-Extra: dev-pylint
|
|
52
|
+
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
53
|
+
Provides-Extra: dev-pytest
|
|
54
|
+
Requires-Dist: pytest==8.4.1; extra == "dev-pytest"
|
|
55
|
+
Requires-Dist: pylint==3.3.7; extra == "dev-pytest"
|
|
56
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.5; extra == "dev-pytest"
|
|
57
|
+
Requires-Dist: pytest-mock==3.14.1; extra == "dev-pytest"
|
|
58
|
+
Requires-Dist: pytest-asyncio==1.1.0; extra == "dev-pytest"
|
|
59
|
+
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
60
|
+
Requires-Dist: hypothesis==6.138.2; extra == "dev-pytest"
|
|
61
|
+
Provides-Extra: dev
|
|
62
|
+
Requires-Dist: frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
|
+
Dynamic: license-file
|
|
64
|
+
|
|
65
|
+
# Frequenz Core Library
|
|
66
|
+
|
|
67
|
+
[](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
|
|
68
|
+
[](https://pypi.org/project/frequenz-core/)
|
|
69
|
+
[](https://frequenz-floss.github.io/frequenz-core-python/)
|
|
70
|
+
|
|
71
|
+
## Introduction
|
|
72
|
+
|
|
73
|
+
Core utilities to complement Python's standard library. This library provides
|
|
74
|
+
essential building blocks for Python applications, including mathematical
|
|
75
|
+
utilities, datetime constants, typing helpers, strongly-typed identifiers, and
|
|
76
|
+
module introspection tools.
|
|
77
|
+
|
|
78
|
+
The `frequenz-core` library is designed to be lightweight, type-safe, and
|
|
79
|
+
follow modern Python best practices. It fills common gaps in the standard
|
|
80
|
+
library with utilities that are frequently needed across different projects.
|
|
81
|
+
|
|
82
|
+
## Supported Platforms
|
|
83
|
+
|
|
84
|
+
The following platforms are officially supported (tested):
|
|
85
|
+
|
|
86
|
+
- **Python:** 3.11
|
|
87
|
+
- **Operating System:** Ubuntu Linux 20.04
|
|
88
|
+
- **Architectures:** amd64, arm64
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
You can install the library from PyPI using pip:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python -m pip install frequenz-core
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or add it to your project's dependencies in `pyproject.toml`:
|
|
99
|
+
|
|
100
|
+
```toml
|
|
101
|
+
[project]
|
|
102
|
+
dependencies = [
|
|
103
|
+
"frequenz-core >= 1.0.2, < 2",
|
|
104
|
+
]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> [!NOTE]
|
|
108
|
+
> We recommend pinning the dependency to the latest version for programs,
|
|
109
|
+
> like `"frequenz-core == 1.0.2"`, and specifying a version range spanning
|
|
110
|
+
> one major version for libraries, like `"frequenz-core >= 1.0.2, < 2"`.
|
|
111
|
+
> We follow [semver](https://semver.org/).
|
|
112
|
+
|
|
113
|
+
## Quick Start
|
|
114
|
+
|
|
115
|
+
Here's a quick overview of the main functionality:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from frequenz.core.math import is_close_to_zero, Interval
|
|
119
|
+
from frequenz.core.datetime import UNIX_EPOCH
|
|
120
|
+
from frequenz.core.module import get_public_module_name
|
|
121
|
+
|
|
122
|
+
# Math utilities
|
|
123
|
+
print(is_close_to_zero(1e-10)) # True - check if float is close to zero
|
|
124
|
+
interval = Interval(1, 10)
|
|
125
|
+
print(5 in interval) # True - check if value is in range
|
|
126
|
+
|
|
127
|
+
# Datetime utilities
|
|
128
|
+
print(UNIX_EPOCH) # 1970-01-01 00:00:00+00:00
|
|
129
|
+
|
|
130
|
+
# Module utilities
|
|
131
|
+
public_name = get_public_module_name("my.package._private.module")
|
|
132
|
+
print(public_name) # "my.package"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Code Examples
|
|
136
|
+
|
|
137
|
+
### Math Utilities
|
|
138
|
+
|
|
139
|
+
The math module provides utilities for floating-point comparisons and interval
|
|
140
|
+
checking:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from frequenz.core.math import is_close_to_zero, Interval
|
|
144
|
+
|
|
145
|
+
# Robust floating-point zero comparison
|
|
146
|
+
assert is_close_to_zero(1e-10) # True
|
|
147
|
+
assert not is_close_to_zero(0.1) # False
|
|
148
|
+
|
|
149
|
+
# Interval checking with inclusive bounds
|
|
150
|
+
numbers = Interval(0, 100)
|
|
151
|
+
assert 50 in numbers # True
|
|
152
|
+
assert not (150 in numbers) # False - 150 is outside the interval
|
|
153
|
+
|
|
154
|
+
# Unbounded intervals
|
|
155
|
+
positive = Interval(0, None) # [0, ∞]
|
|
156
|
+
assert 1000 in positive # True
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `Enum` with deprecated members
|
|
160
|
+
|
|
161
|
+
Define enums with deprecated members that raise deprecation warnings when
|
|
162
|
+
accessed:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from frequenz.core.enum import Enum, DeprecatedMember
|
|
166
|
+
|
|
167
|
+
class TaskStatus(Enum):
|
|
168
|
+
OPEN = 1
|
|
169
|
+
IN_PROGRESS = 2
|
|
170
|
+
PENDING = DeprecatedMember(1, "PENDING is deprecated, use OPEN instead")
|
|
171
|
+
DONE = DeprecatedMember(3, "DONE is deprecated, use FINISHED instead")
|
|
172
|
+
FINISHED = 4
|
|
173
|
+
|
|
174
|
+
status1 = TaskStatus.PENDING # Warns: "PENDING is deprecated, use OPEN instead"
|
|
175
|
+
assert status1 is TaskStatus.OPEN
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Typing Utilities
|
|
179
|
+
|
|
180
|
+
Disable class constructors to enforce factory pattern usage:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from frequenz.core.typing import disable_init
|
|
184
|
+
|
|
185
|
+
@disable_init
|
|
186
|
+
class ApiClient:
|
|
187
|
+
@classmethod
|
|
188
|
+
def create(cls, api_key: str) -> "ApiClient":
|
|
189
|
+
# Factory method with validation
|
|
190
|
+
instance = cls.__new__(cls)
|
|
191
|
+
# Custom initialization logic here
|
|
192
|
+
return instance
|
|
193
|
+
|
|
194
|
+
# This will raise TypeError:
|
|
195
|
+
# client = ApiClient() # ❌ TypeError
|
|
196
|
+
|
|
197
|
+
# Use factory method instead:
|
|
198
|
+
client = ApiClient.create("my-api-key") # ✅ Works
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Strongly-Typed IDs
|
|
202
|
+
|
|
203
|
+
Create type-safe identifiers for different entities:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from frequenz.core.id import BaseId
|
|
207
|
+
|
|
208
|
+
class UserId(BaseId, str_prefix="USR"):
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
class OrderId(BaseId, str_prefix="ORD"):
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
user_id = UserId(123)
|
|
215
|
+
order_id = OrderId(456)
|
|
216
|
+
|
|
217
|
+
print(f"User: {user_id}") # User: USR123
|
|
218
|
+
print(f"Order: {order_id}") # Order: ORD456
|
|
219
|
+
|
|
220
|
+
# Type safety prevents mixing different ID types
|
|
221
|
+
def process_user(user_id: UserId) -> None:
|
|
222
|
+
print(f"Processing user: {user_id}")
|
|
223
|
+
|
|
224
|
+
process_user(user_id) # ✅ Works
|
|
225
|
+
# process_user(order_id) # ❌ Type error
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Documentation
|
|
229
|
+
|
|
230
|
+
For information on how to use this library, please refer to the
|
|
231
|
+
[documentation](https://frequenz-floss.github.io/frequenz-core-python/).
|
|
232
|
+
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
If you want to know how to build this project and contribute to it, please
|
|
236
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -4,7 +4,7 @@ typing-extensions<5,>=4.13.0
|
|
|
4
4
|
frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]
|
|
5
5
|
|
|
6
6
|
[dev-flake8]
|
|
7
|
-
flake8==7.
|
|
7
|
+
flake8==7.3.0
|
|
8
8
|
flake8-docstrings==1.7.0
|
|
9
9
|
flake8-pyproject==1.2.3
|
|
10
10
|
pydoclint==0.6.6
|
|
@@ -15,34 +15,34 @@ black==25.1.0
|
|
|
15
15
|
isort==6.0.1
|
|
16
16
|
|
|
17
17
|
[dev-mkdocs]
|
|
18
|
-
Markdown==3.8
|
|
18
|
+
Markdown==3.8.2
|
|
19
19
|
black==25.1.0
|
|
20
20
|
mike==2.1.3
|
|
21
21
|
mkdocs-gen-files==0.5.0
|
|
22
22
|
mkdocs-literate-nav==0.6.2
|
|
23
23
|
mkdocs-macros-plugin==1.3.7
|
|
24
|
-
mkdocs-material==9.6.
|
|
25
|
-
mkdocstrings[python]==0.
|
|
26
|
-
mkdocstrings-python==1.
|
|
27
|
-
frequenz-repo-config[lib]==0.13.
|
|
24
|
+
mkdocs-material==9.6.16
|
|
25
|
+
mkdocstrings[python]==0.30.0
|
|
26
|
+
mkdocstrings-python==1.17.0
|
|
27
|
+
frequenz-repo-config[lib]==0.13.5
|
|
28
28
|
|
|
29
29
|
[dev-mypy]
|
|
30
|
-
mypy==1.
|
|
31
|
-
types-Markdown==3.8.0.
|
|
30
|
+
mypy==1.17.1
|
|
31
|
+
types-Markdown==3.8.0.20250708
|
|
32
32
|
frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
33
33
|
|
|
34
34
|
[dev-noxfile]
|
|
35
35
|
nox==2025.5.1
|
|
36
|
-
frequenz-repo-config[lib]==0.13.
|
|
36
|
+
frequenz-repo-config[lib]==0.13.5
|
|
37
37
|
|
|
38
38
|
[dev-pylint]
|
|
39
39
|
frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
40
40
|
|
|
41
41
|
[dev-pytest]
|
|
42
|
-
pytest==8.
|
|
42
|
+
pytest==8.4.1
|
|
43
43
|
pylint==3.3.7
|
|
44
|
-
frequenz-repo-config[extra-lint-examples]==0.13.
|
|
44
|
+
frequenz-repo-config[extra-lint-examples]==0.13.5
|
|
45
45
|
pytest-mock==3.14.1
|
|
46
|
-
pytest-asyncio==
|
|
47
|
-
async-solipsism==0.
|
|
48
|
-
hypothesis==6.
|
|
46
|
+
pytest-asyncio==1.1.0
|
|
47
|
+
async-solipsism==0.8
|
|
48
|
+
hypothesis==6.138.2
|
frequenz_core-1.0.2/PKG-INFO
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: frequenz-core
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: Core utilities to complement Python's standard library
|
|
5
|
-
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-core-python/
|
|
8
|
-
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-core-python/releases
|
|
9
|
-
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-core-python/issues
|
|
10
|
-
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-core-python
|
|
11
|
-
Project-URL: Support, https://github.com/frequenz-floss/frequenz-core-python/discussions/categories/support
|
|
12
|
-
Keywords: asyncio,collections,core,datetime,frequenz,lib,library,math,python,stdlib,typing
|
|
13
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
-
Classifier: Typing :: Typed
|
|
20
|
-
Requires-Python: <4,>=3.11
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: typing-extensions<5,>=4.13.0
|
|
24
|
-
Provides-Extra: dev-flake8
|
|
25
|
-
Requires-Dist: flake8==7.2.0; extra == "dev-flake8"
|
|
26
|
-
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
27
|
-
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
28
|
-
Requires-Dist: pydoclint==0.6.6; extra == "dev-flake8"
|
|
29
|
-
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
30
|
-
Provides-Extra: dev-formatting
|
|
31
|
-
Requires-Dist: black==25.1.0; extra == "dev-formatting"
|
|
32
|
-
Requires-Dist: isort==6.0.1; extra == "dev-formatting"
|
|
33
|
-
Provides-Extra: dev-mkdocs
|
|
34
|
-
Requires-Dist: Markdown==3.8; extra == "dev-mkdocs"
|
|
35
|
-
Requires-Dist: black==25.1.0; extra == "dev-mkdocs"
|
|
36
|
-
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
37
|
-
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
38
|
-
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
39
|
-
Requires-Dist: mkdocs-macros-plugin==1.3.7; extra == "dev-mkdocs"
|
|
40
|
-
Requires-Dist: mkdocs-material==9.6.14; extra == "dev-mkdocs"
|
|
41
|
-
Requires-Dist: mkdocstrings[python]==0.29.1; extra == "dev-mkdocs"
|
|
42
|
-
Requires-Dist: mkdocstrings-python==1.16.11; extra == "dev-mkdocs"
|
|
43
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.4; extra == "dev-mkdocs"
|
|
44
|
-
Provides-Extra: dev-mypy
|
|
45
|
-
Requires-Dist: mypy==1.16.0; extra == "dev-mypy"
|
|
46
|
-
Requires-Dist: types-Markdown==3.8.0.20250415; extra == "dev-mypy"
|
|
47
|
-
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
48
|
-
Provides-Extra: dev-noxfile
|
|
49
|
-
Requires-Dist: nox==2025.5.1; extra == "dev-noxfile"
|
|
50
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.4; extra == "dev-noxfile"
|
|
51
|
-
Provides-Extra: dev-pylint
|
|
52
|
-
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
53
|
-
Provides-Extra: dev-pytest
|
|
54
|
-
Requires-Dist: pytest==8.3.5; extra == "dev-pytest"
|
|
55
|
-
Requires-Dist: pylint==3.3.7; extra == "dev-pytest"
|
|
56
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.4; extra == "dev-pytest"
|
|
57
|
-
Requires-Dist: pytest-mock==3.14.1; extra == "dev-pytest"
|
|
58
|
-
Requires-Dist: pytest-asyncio==0.26.0; extra == "dev-pytest"
|
|
59
|
-
Requires-Dist: async-solipsism==0.7; extra == "dev-pytest"
|
|
60
|
-
Requires-Dist: hypothesis==6.132.0; extra == "dev-pytest"
|
|
61
|
-
Provides-Extra: dev
|
|
62
|
-
Requires-Dist: frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
|
-
Dynamic: license-file
|
|
64
|
-
|
|
65
|
-
# Frequenz Core Library
|
|
66
|
-
|
|
67
|
-
[](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
|
|
68
|
-
[](https://pypi.org/project/frequenz-core/)
|
|
69
|
-
[](https://frequenz-floss.github.io/frequenz-core-python/)
|
|
70
|
-
|
|
71
|
-
## Introduction
|
|
72
|
-
|
|
73
|
-
Core utilities to complement Python's standard library.
|
|
74
|
-
|
|
75
|
-
## Documentation
|
|
76
|
-
|
|
77
|
-
For information on how to use this library, please refer to the
|
|
78
|
-
[documentation](https://frequenz-floss.github.io/frequenz-core-python/).
|
|
79
|
-
|
|
80
|
-
## Supported Platforms
|
|
81
|
-
|
|
82
|
-
The following platforms are officially supported (tested):
|
|
83
|
-
|
|
84
|
-
- **Python:** 3.11
|
|
85
|
-
- **Operating System:** Ubuntu Linux 20.04
|
|
86
|
-
- **Architectures:** amd64, arm64
|
|
87
|
-
|
|
88
|
-
## Contributing
|
|
89
|
-
|
|
90
|
-
If you want to know how to build this project and contribute to it, please
|
|
91
|
-
check out the [Contributing Guide](CONTRIBUTING.md).
|
frequenz_core-1.0.2/README.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Frequenz Core Library
|
|
2
|
-
|
|
3
|
-
[](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
|
|
4
|
-
[](https://pypi.org/project/frequenz-core/)
|
|
5
|
-
[](https://frequenz-floss.github.io/frequenz-core-python/)
|
|
6
|
-
|
|
7
|
-
## Introduction
|
|
8
|
-
|
|
9
|
-
Core utilities to complement Python's standard library.
|
|
10
|
-
|
|
11
|
-
## Documentation
|
|
12
|
-
|
|
13
|
-
For information on how to use this library, please refer to the
|
|
14
|
-
[documentation](https://frequenz-floss.github.io/frequenz-core-python/).
|
|
15
|
-
|
|
16
|
-
## Supported Platforms
|
|
17
|
-
|
|
18
|
-
The following platforms are officially supported (tested):
|
|
19
|
-
|
|
20
|
-
- **Python:** 3.11
|
|
21
|
-
- **Operating System:** Ubuntu Linux 20.04
|
|
22
|
-
- **Architectures:** amd64, arm64
|
|
23
|
-
|
|
24
|
-
## Contributing
|
|
25
|
-
|
|
26
|
-
If you want to know how to build this project and contribute to it, please
|
|
27
|
-
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Frequenz Core Library Release Notes
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
<!-- Here goes a general summary of what this release is about -->
|
|
6
|
-
|
|
7
|
-
## Upgrading
|
|
8
|
-
|
|
9
|
-
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->
|
|
10
|
-
|
|
11
|
-
## New Features
|
|
12
|
-
|
|
13
|
-
<!-- Here goes the main new features and examples or instructions on how to use them -->
|
|
14
|
-
|
|
15
|
-
## Bug Fixes
|
|
16
|
-
|
|
17
|
-
* `BaseId` will now log instead of raising a warning when a duplicate prefix is detected. This is to fix [a problem with code examples](https://github.com/frequenz-floss/frequenz-repo-config-python/issues/421) being tested using sybil and the class being imported multiple times, which caused the exception to be raised. We first tried to use `warn()` but that complicated the building process for all downstream projects, requiring them to add an exception for exactly this warning.
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: frequenz-core
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: Core utilities to complement Python's standard library
|
|
5
|
-
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-core-python/
|
|
8
|
-
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-core-python/releases
|
|
9
|
-
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-core-python/issues
|
|
10
|
-
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-core-python
|
|
11
|
-
Project-URL: Support, https://github.com/frequenz-floss/frequenz-core-python/discussions/categories/support
|
|
12
|
-
Keywords: asyncio,collections,core,datetime,frequenz,lib,library,math,python,stdlib,typing
|
|
13
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
-
Classifier: Typing :: Typed
|
|
20
|
-
Requires-Python: <4,>=3.11
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: typing-extensions<5,>=4.13.0
|
|
24
|
-
Provides-Extra: dev-flake8
|
|
25
|
-
Requires-Dist: flake8==7.2.0; extra == "dev-flake8"
|
|
26
|
-
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
27
|
-
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
28
|
-
Requires-Dist: pydoclint==0.6.6; extra == "dev-flake8"
|
|
29
|
-
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
30
|
-
Provides-Extra: dev-formatting
|
|
31
|
-
Requires-Dist: black==25.1.0; extra == "dev-formatting"
|
|
32
|
-
Requires-Dist: isort==6.0.1; extra == "dev-formatting"
|
|
33
|
-
Provides-Extra: dev-mkdocs
|
|
34
|
-
Requires-Dist: Markdown==3.8; extra == "dev-mkdocs"
|
|
35
|
-
Requires-Dist: black==25.1.0; extra == "dev-mkdocs"
|
|
36
|
-
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
37
|
-
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
38
|
-
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
39
|
-
Requires-Dist: mkdocs-macros-plugin==1.3.7; extra == "dev-mkdocs"
|
|
40
|
-
Requires-Dist: mkdocs-material==9.6.14; extra == "dev-mkdocs"
|
|
41
|
-
Requires-Dist: mkdocstrings[python]==0.29.1; extra == "dev-mkdocs"
|
|
42
|
-
Requires-Dist: mkdocstrings-python==1.16.11; extra == "dev-mkdocs"
|
|
43
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.4; extra == "dev-mkdocs"
|
|
44
|
-
Provides-Extra: dev-mypy
|
|
45
|
-
Requires-Dist: mypy==1.16.0; extra == "dev-mypy"
|
|
46
|
-
Requires-Dist: types-Markdown==3.8.0.20250415; extra == "dev-mypy"
|
|
47
|
-
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
48
|
-
Provides-Extra: dev-noxfile
|
|
49
|
-
Requires-Dist: nox==2025.5.1; extra == "dev-noxfile"
|
|
50
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.4; extra == "dev-noxfile"
|
|
51
|
-
Provides-Extra: dev-pylint
|
|
52
|
-
Requires-Dist: frequenz-core[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
53
|
-
Provides-Extra: dev-pytest
|
|
54
|
-
Requires-Dist: pytest==8.3.5; extra == "dev-pytest"
|
|
55
|
-
Requires-Dist: pylint==3.3.7; extra == "dev-pytest"
|
|
56
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.4; extra == "dev-pytest"
|
|
57
|
-
Requires-Dist: pytest-mock==3.14.1; extra == "dev-pytest"
|
|
58
|
-
Requires-Dist: pytest-asyncio==0.26.0; extra == "dev-pytest"
|
|
59
|
-
Requires-Dist: async-solipsism==0.7; extra == "dev-pytest"
|
|
60
|
-
Requires-Dist: hypothesis==6.132.0; extra == "dev-pytest"
|
|
61
|
-
Provides-Extra: dev
|
|
62
|
-
Requires-Dist: frequenz-core[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
|
-
Dynamic: license-file
|
|
64
|
-
|
|
65
|
-
# Frequenz Core Library
|
|
66
|
-
|
|
67
|
-
[](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
|
|
68
|
-
[](https://pypi.org/project/frequenz-core/)
|
|
69
|
-
[](https://frequenz-floss.github.io/frequenz-core-python/)
|
|
70
|
-
|
|
71
|
-
## Introduction
|
|
72
|
-
|
|
73
|
-
Core utilities to complement Python's standard library.
|
|
74
|
-
|
|
75
|
-
## Documentation
|
|
76
|
-
|
|
77
|
-
For information on how to use this library, please refer to the
|
|
78
|
-
[documentation](https://frequenz-floss.github.io/frequenz-core-python/).
|
|
79
|
-
|
|
80
|
-
## Supported Platforms
|
|
81
|
-
|
|
82
|
-
The following platforms are officially supported (tested):
|
|
83
|
-
|
|
84
|
-
- **Python:** 3.11
|
|
85
|
-
- **Operating System:** Ubuntu Linux 20.04
|
|
86
|
-
- **Architectures:** amd64, arm64
|
|
87
|
-
|
|
88
|
-
## Contributing
|
|
89
|
-
|
|
90
|
-
If you want to know how to build this project and contribute to it, please
|
|
91
|
-
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
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
|
|
File without changes
|
|
File without changes
|