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.
Files changed (25) hide show
  1. frequenz_core-1.1.0/PKG-INFO +236 -0
  2. frequenz_core-1.1.0/README.md +172 -0
  3. frequenz_core-1.1.0/RELEASE_NOTES.md +21 -0
  4. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/pyproject.toml +15 -15
  5. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/datetime.py +1 -1
  6. frequenz_core-1.1.0/src/frequenz/core/enum.py +207 -0
  7. frequenz_core-1.1.0/src/frequenz_core.egg-info/PKG-INFO +236 -0
  8. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz_core.egg-info/SOURCES.txt +1 -0
  9. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz_core.egg-info/requires.txt +14 -14
  10. frequenz_core-1.0.2/PKG-INFO +0 -91
  11. frequenz_core-1.0.2/README.md +0 -27
  12. frequenz_core-1.0.2/RELEASE_NOTES.md +0 -17
  13. frequenz_core-1.0.2/src/frequenz_core.egg-info/PKG-INFO +0 -91
  14. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/LICENSE +0 -0
  15. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/MANIFEST.in +0 -0
  16. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/setup.cfg +0 -0
  17. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/__init__.py +0 -0
  18. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/conftest.py +0 -0
  19. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/id.py +0 -0
  20. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/math.py +0 -0
  21. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/module.py +0 -0
  22. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/py.typed +0 -0
  23. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz/core/typing.py +0 -0
  24. {frequenz_core-1.0.2 → frequenz_core-1.1.0}/src/frequenz_core.egg-info/dependency_links.txt +0 -0
  25. {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
+ [![Build Status](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
68
+ [![PyPI Package](https://img.shields.io/pypi/v/frequenz-core)](https://pypi.org/project/frequenz-core/)
69
+ [![Docs](https://img.shields.io/badge/docs-latest-informational)](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
+ [![Build Status](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
4
+ [![PyPI Package](https://img.shields.io/pypi/v/frequenz-core)](https://pypi.org/project/frequenz-core/)
5
+ [![Docs](https://img.shields.io/badge/docs-latest-informational)](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.4",
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.2.0",
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.14",
64
- "mkdocstrings[python] == 0.29.1",
65
- "mkdocstrings-python == 1.16.11",
66
- "frequenz-repo-config[lib] == 0.13.4",
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.16.0",
70
- "types-Markdown == 3.8.0.20250415",
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.4"]
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.3.5",
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.4",
83
+ "frequenz-repo-config[extra-lint-examples] == 0.13.5",
84
84
  "pytest-mock == 3.14.1",
85
- "pytest-asyncio == 0.26.0",
86
- "async-solipsism == 0.7",
87
- "hypothesis == 6.132.0",
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]",
@@ -1,7 +1,7 @@
1
1
  # License: MIT
2
2
  # Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3
3
 
4
- """Timeseries basic types."""
4
+ """Date and time utilities."""
5
5
 
6
6
  from datetime import datetime, timezone
7
7
  from typing import Final
@@ -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
+ [![Build Status](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
68
+ [![PyPI Package](https://img.shields.io/pypi/v/frequenz-core)](https://pypi.org/project/frequenz-core/)
69
+ [![Docs](https://img.shields.io/badge/docs-latest-informational)](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).
@@ -6,6 +6,7 @@ pyproject.toml
6
6
  src/frequenz/core/__init__.py
7
7
  src/frequenz/core/conftest.py
8
8
  src/frequenz/core/datetime.py
9
+ src/frequenz/core/enum.py
9
10
  src/frequenz/core/id.py
10
11
  src/frequenz/core/math.py
11
12
  src/frequenz/core/module.py
@@ -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.2.0
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.14
25
- mkdocstrings[python]==0.29.1
26
- mkdocstrings-python==1.16.11
27
- frequenz-repo-config[lib]==0.13.4
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.16.0
31
- types-Markdown==3.8.0.20250415
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.4
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.3.5
42
+ pytest==8.4.1
43
43
  pylint==3.3.7
44
- frequenz-repo-config[extra-lint-examples]==0.13.4
44
+ frequenz-repo-config[extra-lint-examples]==0.13.5
45
45
  pytest-mock==3.14.1
46
- pytest-asyncio==0.26.0
47
- async-solipsism==0.7
48
- hypothesis==6.132.0
46
+ pytest-asyncio==1.1.0
47
+ async-solipsism==0.8
48
+ hypothesis==6.138.2
@@ -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
- [![Build Status](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
68
- [![PyPI Package](https://img.shields.io/pypi/v/frequenz-core)](https://pypi.org/project/frequenz-core/)
69
- [![Docs](https://img.shields.io/badge/docs-latest-informational)](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).
@@ -1,27 +0,0 @@
1
- # Frequenz Core Library
2
-
3
- [![Build Status](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
4
- [![PyPI Package](https://img.shields.io/pypi/v/frequenz-core)](https://pypi.org/project/frequenz-core/)
5
- [![Docs](https://img.shields.io/badge/docs-latest-informational)](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
- [![Build Status](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-core-python/actions/workflows/ci.yaml)
68
- [![PyPI Package](https://img.shields.io/pypi/v/frequenz-core)](https://pypi.org/project/frequenz-core/)
69
- [![Docs](https://img.shields.io/badge/docs-latest-informational)](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