frequenz-gridpool 0.7.1__tar.gz → 0.9.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_gridpool-0.7.1/src/frequenz_gridpool.egg-info → frequenz_gridpool-0.9.0}/PKG-INFO +15 -14
- frequenz_gridpool-0.9.0/RELEASE_NOTES.md +20 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/pyproject.toml +23 -17
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/_dump_config.py +2 -4
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/_patch_config.py +2 -4
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_assets.py +9 -4
- frequenz_gridpool-0.9.0/src/frequenz/gridpool/config/_fields.py +29 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_microgrid.py +10 -8
- frequenz_gridpool-0.9.0/src/frequenz/gridpool/config/_migrations.py +58 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_topology.py +4 -2
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0/src/frequenz_gridpool.egg-info}/PKG-INFO +15 -14
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/SOURCES.txt +1 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/requires.txt +14 -13
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/scm_file_list.json +1 -1
- frequenz_gridpool-0.9.0/src/frequenz_gridpool.egg-info/scm_version.json +8 -0
- frequenz_gridpool-0.7.1/RELEASE_NOTES.md +0 -42
- frequenz_gridpool-0.7.1/src/frequenz/gridpool/config/_migrations.py +0 -139
- frequenz_gridpool-0.7.1/src/frequenz_gridpool.egg-info/scm_version.json +0 -8
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/LICENSE +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/MANIFEST.in +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/README.md +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/setup.cfg +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/__init__.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/_graph_generator.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/__init__.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/__main__.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/_render_graph.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/__init__.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_gridpool.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_load.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/conftest.py +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/py.typed +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/dependency_links.txt +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/entry_points.txt +0 -0
- {frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frequenz-gridpool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: High-level interface to grid pools for the Frequenz platform.
|
|
5
5
|
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,9 +19,10 @@ Classifier: Typing :: Typed
|
|
|
19
19
|
Requires-Python: <4,>=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
+
Requires-Dist: marshmallow<5,>=3.18.0
|
|
22
23
|
Requires-Dist: marshmallow-dataclass<9,>=8.7.1
|
|
23
24
|
Requires-Dist: typing-extensions<5,>=4.14.1
|
|
24
|
-
Requires-Dist: frequenz-microgrid-component-graph<0.
|
|
25
|
+
Requires-Dist: frequenz-microgrid-component-graph<0.7,>=0.6.0
|
|
25
26
|
Requires-Dist: frequenz-client-assets<0.6,>=0.5.0
|
|
26
27
|
Provides-Extra: cli
|
|
27
28
|
Requires-Dist: asyncclick<9,>=8.3.0.4; extra == "cli"
|
|
@@ -31,8 +32,8 @@ Requires-Dist: frequenz-gridpool[cli]; extra == "render-graph"
|
|
|
31
32
|
Requires-Dist: matplotlib<4,>=3.7.0; extra == "render-graph"
|
|
32
33
|
Requires-Dist: networkx<4,>=3.0; extra == "render-graph"
|
|
33
34
|
Provides-Extra: dev-flake8
|
|
34
|
-
Requires-Dist: flake8==7.
|
|
35
|
-
Requires-Dist: flake8-datetimez==
|
|
35
|
+
Requires-Dist: flake8==7.4.1; extra == "dev-flake8"
|
|
36
|
+
Requires-Dist: flake8-datetimez==26.8.1; extra == "dev-flake8"
|
|
36
37
|
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
37
38
|
Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
|
|
38
39
|
Requires-Dist: pydoclint==0.9.1; extra == "dev-flake8"
|
|
@@ -41,31 +42,31 @@ Provides-Extra: dev-formatting
|
|
|
41
42
|
Requires-Dist: black==26.5.1; extra == "dev-formatting"
|
|
42
43
|
Requires-Dist: isort==8.0.1; extra == "dev-formatting"
|
|
43
44
|
Provides-Extra: dev-mkdocs
|
|
44
|
-
Requires-Dist: Markdown==3.
|
|
45
|
+
Requires-Dist: Markdown==3.11; extra == "dev-mkdocs"
|
|
45
46
|
Requires-Dist: black==26.5.1; extra == "dev-mkdocs"
|
|
46
47
|
Requires-Dist: mike==2.2.0; extra == "dev-mkdocs"
|
|
47
48
|
Requires-Dist: mkdocs-gen-files==0.6.1; extra == "dev-mkdocs"
|
|
48
49
|
Requires-Dist: mkdocs-literate-nav==0.6.3; extra == "dev-mkdocs"
|
|
49
50
|
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
50
51
|
Requires-Dist: mkdocs-material==9.7.7; extra == "dev-mkdocs"
|
|
51
|
-
Requires-Dist: mkdocstrings[python]==1.0.
|
|
52
|
-
Requires-Dist: mkdocstrings-python==2.0.
|
|
53
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
52
|
+
Requires-Dist: mkdocstrings[python]==1.0.7; extra == "dev-mkdocs"
|
|
53
|
+
Requires-Dist: mkdocstrings-python==2.0.9; extra == "dev-mkdocs"
|
|
54
|
+
Requires-Dist: frequenz-repo-config[lib]==0.19.0; extra == "dev-mkdocs"
|
|
54
55
|
Provides-Extra: dev-mypy
|
|
55
|
-
Requires-Dist: mypy==2.3.
|
|
56
|
+
Requires-Dist: mypy==2.3.1; extra == "dev-mypy"
|
|
56
57
|
Requires-Dist: types-networkx>=3.6.1.20251220; extra == "dev-mypy"
|
|
57
58
|
Requires-Dist: types-Markdown==3.10.2.20260712; extra == "dev-mypy"
|
|
58
59
|
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
59
60
|
Provides-Extra: dev-noxfile
|
|
60
|
-
Requires-Dist: nox==2026.
|
|
61
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
61
|
+
Requires-Dist: nox==2026.8.17; extra == "dev-noxfile"
|
|
62
|
+
Requires-Dist: frequenz-repo-config[lib]==0.19.0; extra == "dev-noxfile"
|
|
62
63
|
Provides-Extra: dev-pylint
|
|
63
64
|
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
64
65
|
Provides-Extra: dev-pytest
|
|
65
66
|
Requires-Dist: pytest==9.1.1; extra == "dev-pytest"
|
|
66
|
-
Requires-Dist: pylint==4.0.
|
|
67
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.
|
|
68
|
-
Requires-Dist: pytest-mock==3.
|
|
67
|
+
Requires-Dist: pylint==4.0.9; extra == "dev-pytest"
|
|
68
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.19.0; extra == "dev-pytest"
|
|
69
|
+
Requires-Dist: pytest-mock==3.16.0; extra == "dev-pytest"
|
|
69
70
|
Requires-Dist: pytest-asyncio==1.4.0; extra == "dev-pytest"
|
|
70
71
|
Requires-Dist: async-solipsism==0.9; extra == "dev-pytest"
|
|
71
72
|
Requires-Dist: frequenz-gridpool[render-graph]; extra == "dev-pytest"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Frequenz Gridpool Library Release Notes
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
<!-- Here goes a general summary of what this release is about -->
|
|
6
|
+
|
|
7
|
+
## Upgrading
|
|
8
|
+
|
|
9
|
+
- Requires `frequenz-microgrid-component-graph` 0.6, which stops clamping the consumer
|
|
10
|
+
and producer formulas. `ComponentGraph.consumer_formula()` and
|
|
11
|
+
`ComponentGraph.producer_formula()` can now return values previously pinned
|
|
12
|
+
to zero: a negative consumer total, or a positive producer total.
|
|
13
|
+
|
|
14
|
+
## New Features
|
|
15
|
+
|
|
16
|
+
<!-- Here goes the main new features and examples or instructions on how to use them -->
|
|
17
|
+
|
|
18
|
+
## Bug Fixes
|
|
19
|
+
|
|
20
|
+
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
[build-system]
|
|
5
5
|
requires = [
|
|
6
|
-
"setuptools ==
|
|
7
|
-
"setuptools_scm[toml] == 10.
|
|
8
|
-
"frequenz-repo-config[lib] == 0.
|
|
6
|
+
"setuptools == 84.0.0",
|
|
7
|
+
"setuptools_scm[toml] == 10.3.4",
|
|
8
|
+
"frequenz-repo-config[lib] == 0.19.0",
|
|
9
9
|
]
|
|
10
10
|
build-backend = "setuptools.build_meta"
|
|
11
11
|
|
|
@@ -26,9 +26,13 @@ classifiers = [
|
|
|
26
26
|
]
|
|
27
27
|
requires-python = ">= 3.11, < 4"
|
|
28
28
|
dependencies = [
|
|
29
|
+
# Used transitively via marshmallow-dataclass (every config schema is a
|
|
30
|
+
# `marshmallow.Schema`); declared directly so `pytest_min` pins its lower
|
|
31
|
+
# bound and the minimum-version path is actually tested.
|
|
32
|
+
"marshmallow >= 3.18.0, < 5",
|
|
29
33
|
"marshmallow-dataclass >= 8.7.1, < 9",
|
|
30
34
|
"typing-extensions >= 4.14.1, < 5",
|
|
31
|
-
"frequenz-microgrid-component-graph >= 0.
|
|
35
|
+
"frequenz-microgrid-component-graph >= 0.6.0, < 0.7",
|
|
32
36
|
"frequenz-client-assets >= 0.5.0, < 0.6",
|
|
33
37
|
]
|
|
34
38
|
dynamic = ["version"]
|
|
@@ -52,8 +56,8 @@ render-graph = [
|
|
|
52
56
|
]
|
|
53
57
|
|
|
54
58
|
dev-flake8 = [
|
|
55
|
-
"flake8 == 7.
|
|
56
|
-
"flake8-datetimez ==
|
|
59
|
+
"flake8 == 7.4.1",
|
|
60
|
+
"flake8-datetimez == 26.8.1",
|
|
57
61
|
"flake8-docstrings == 1.7.0",
|
|
58
62
|
"flake8-pyproject == 1.2.4", # For reading the flake8 config from pyproject.toml
|
|
59
63
|
"pydoclint == 0.9.1",
|
|
@@ -61,27 +65,27 @@ dev-flake8 = [
|
|
|
61
65
|
]
|
|
62
66
|
dev-formatting = ["black == 26.5.1", "isort == 8.0.1"]
|
|
63
67
|
dev-mkdocs = [
|
|
64
|
-
"Markdown == 3.
|
|
68
|
+
"Markdown == 3.11",
|
|
65
69
|
"black == 26.5.1",
|
|
66
70
|
"mike == 2.2.0",
|
|
67
71
|
"mkdocs-gen-files == 0.6.1",
|
|
68
72
|
"mkdocs-literate-nav == 0.6.3",
|
|
69
73
|
"mkdocs-macros-plugin == 1.5.0",
|
|
70
74
|
"mkdocs-material == 9.7.7",
|
|
71
|
-
"mkdocstrings[python] == 1.0.
|
|
72
|
-
"mkdocstrings-python == 2.0.
|
|
73
|
-
"frequenz-repo-config[lib] == 0.
|
|
75
|
+
"mkdocstrings[python] == 1.0.7",
|
|
76
|
+
"mkdocstrings-python == 2.0.9",
|
|
77
|
+
"frequenz-repo-config[lib] == 0.19.0",
|
|
74
78
|
]
|
|
75
79
|
dev-mypy = [
|
|
76
|
-
"mypy == 2.3.
|
|
80
|
+
"mypy == 2.3.1",
|
|
77
81
|
"types-networkx>=3.6.1.20251220",
|
|
78
82
|
"types-Markdown == 3.10.2.20260712",
|
|
79
83
|
# For checking the noxfile, docs/ script, and tests
|
|
80
84
|
"frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
81
85
|
]
|
|
82
86
|
dev-noxfile = [
|
|
83
|
-
"nox == 2026.
|
|
84
|
-
"frequenz-repo-config[lib] == 0.
|
|
87
|
+
"nox == 2026.8.17",
|
|
88
|
+
"frequenz-repo-config[lib] == 0.19.0",
|
|
85
89
|
]
|
|
86
90
|
dev-pylint = [
|
|
87
91
|
# dev-pytest already defines a dependency to pylint because of the examples
|
|
@@ -90,9 +94,9 @@ dev-pylint = [
|
|
|
90
94
|
]
|
|
91
95
|
dev-pytest = [
|
|
92
96
|
"pytest == 9.1.1",
|
|
93
|
-
"pylint == 4.0.
|
|
94
|
-
"frequenz-repo-config[extra-lint-examples] == 0.
|
|
95
|
-
"pytest-mock == 3.
|
|
97
|
+
"pylint == 4.0.9", # We need this to check for the examples
|
|
98
|
+
"frequenz-repo-config[extra-lint-examples] == 0.19.0",
|
|
99
|
+
"pytest-mock == 3.16.0",
|
|
96
100
|
"pytest-asyncio == 1.4.0",
|
|
97
101
|
"async-solipsism == 0.9",
|
|
98
102
|
# The tests cover the cli package and the graph renderer. render-graph
|
|
@@ -168,13 +172,15 @@ disable = [
|
|
|
168
172
|
]
|
|
169
173
|
|
|
170
174
|
[tool.pytest.ini_options]
|
|
171
|
-
addopts = "-W=all -Werror -Wdefault::DeprecationWarning -Wdefault::PendingDeprecationWarning
|
|
175
|
+
addopts = "-W=all -Werror -Wdefault::DeprecationWarning -Wdefault::PendingDeprecationWarning"
|
|
172
176
|
testpaths = ["tests", "src"]
|
|
177
|
+
asyncio_debug = true
|
|
173
178
|
asyncio_mode = "auto"
|
|
174
179
|
asyncio_default_fixture_loop_scope = "function"
|
|
175
180
|
required_plugins = ["pytest-asyncio", "pytest-mock"]
|
|
176
181
|
|
|
177
182
|
[tool.mypy]
|
|
183
|
+
enable_error_code = ["exhaustive-match"]
|
|
178
184
|
explicit_package_bases = true
|
|
179
185
|
namespace_packages = true
|
|
180
186
|
# This option disables mypy cache, and it is sometimes useful to enable it if
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/_dump_config.py
RENAMED
|
@@ -17,6 +17,7 @@ escaping, key-quoting, list/number/datetime formatting) is delegated to
|
|
|
17
17
|
fields are done here, since TOML has no null and `tomlkit` rejects it.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
+
from dataclasses import asdict
|
|
20
21
|
from typing import Any
|
|
21
22
|
|
|
22
23
|
import tomlkit
|
|
@@ -86,13 +87,10 @@ def dump_map(configs: dict[int, MicrogridConfig]) -> str:
|
|
|
86
87
|
The TOML representation as a string, with one blank line between
|
|
87
88
|
microgrids and entries sorted by numeric microgrid ID.
|
|
88
89
|
"""
|
|
89
|
-
schema = MicrogridConfig.Schema()
|
|
90
90
|
doc = tomlkit.document()
|
|
91
91
|
doc.append(tomlkit.key(["assets", "version"]), _CURRENT_VERSION)
|
|
92
92
|
for mid in sorted(configs):
|
|
93
|
-
|
|
94
|
-
assert isinstance(dumped, dict)
|
|
95
|
-
leaves = _iter_leaves([*_MICROGRID_PREFIX, str(mid)], dumped)
|
|
93
|
+
leaves = _iter_leaves([*_MICROGRID_PREFIX, str(mid)], asdict(configs[mid]))
|
|
96
94
|
if not leaves:
|
|
97
95
|
continue
|
|
98
96
|
doc.add(tomlkit.nl())
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/_patch_config.py
RENAMED
|
@@ -10,6 +10,7 @@ survive. By default it refreshes the managed leaves, overwriting them; with
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
from collections.abc import Mapping
|
|
13
|
+
from dataclasses import asdict
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import Any
|
|
15
16
|
|
|
@@ -54,7 +55,6 @@ def patch_text(
|
|
|
54
55
|
"""
|
|
55
56
|
doc = tomlkit.parse(original)
|
|
56
57
|
_reject_legacy_layout(doc)
|
|
57
|
-
schema = MicrogridConfig.Schema()
|
|
58
58
|
|
|
59
59
|
# Leaves needing a whole new sub-table can't be inserted via item assignment
|
|
60
60
|
# without tomlkit falling back to bracket-header syntax, so they are spliced
|
|
@@ -63,9 +63,7 @@ def patch_text(
|
|
|
63
63
|
|
|
64
64
|
for microgrid_id, cfg in configs.items():
|
|
65
65
|
mid = str(microgrid_id)
|
|
66
|
-
|
|
67
|
-
assert isinstance(dumped, dict)
|
|
68
|
-
leaves = _iter_leaves([], dumped)
|
|
66
|
+
leaves = _iter_leaves([], asdict(cfg))
|
|
69
67
|
if not leaves:
|
|
70
68
|
continue
|
|
71
69
|
|
|
@@ -443,23 +443,28 @@ class AssetsConfig:
|
|
|
443
443
|
"""Read the raw `assets` table from a TOML file.
|
|
444
444
|
|
|
445
445
|
The document is migrated to the current format before its `assets`
|
|
446
|
-
table is returned for merging.
|
|
446
|
+
table is returned for merging. A file with no `assets` table contributes
|
|
447
|
+
nothing to the merge, so consumers can pass a mixed list of files and
|
|
448
|
+
gridpool reads only the `assets`-bearing ones.
|
|
447
449
|
|
|
448
450
|
Args:
|
|
449
451
|
config_path: The path to the TOML configuration file.
|
|
450
452
|
|
|
451
453
|
Returns:
|
|
452
|
-
The raw `assets` table, unvalidated, for merging before it is loaded
|
|
454
|
+
The raw `assets` table, unvalidated, for merging before it is loaded,
|
|
455
|
+
or an empty table if the file has none.
|
|
453
456
|
|
|
454
457
|
Raises:
|
|
455
|
-
TypeError: If `assets` is not a table.
|
|
458
|
+
TypeError: If `assets` is present but not a table.
|
|
456
459
|
"""
|
|
457
460
|
with config_path.open("rb") as f:
|
|
458
461
|
data: dict[str, Any] = tomllib.load(f)
|
|
459
462
|
|
|
460
463
|
data = migrate(data, config_path)
|
|
461
464
|
|
|
462
|
-
assets = data
|
|
465
|
+
assets = data.get("assets")
|
|
466
|
+
if assets is None:
|
|
467
|
+
return {}
|
|
463
468
|
if not isinstance(assets, dict):
|
|
464
469
|
raise TypeError(
|
|
465
470
|
f"{config_path}: `assets` must be a table, got {type(assets)}"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Reusable marshmallow fields for config data models."""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import marshmallow
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TomlAwareDateTime(marshmallow.fields.DateTime):
|
|
13
|
+
"""A `DateTime` that also accepts an already-parsed `datetime`.
|
|
14
|
+
|
|
15
|
+
marshmallow 3's `DateTime` deserialises only ISO strings, but `tomllib`
|
|
16
|
+
yields native `datetime` objects before the value reaches marshmallow, so
|
|
17
|
+
those inputs must pass through unchanged. marshmallow 4 accepts them itself;
|
|
18
|
+
this keeps both behaving the same.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def _deserialize(self, value: Any, *args: Any, **kwargs: Any) -> datetime:
|
|
22
|
+
if isinstance(value, datetime):
|
|
23
|
+
return value
|
|
24
|
+
return super()._deserialize(value, *args, **kwargs)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def toml_datetime_metadata() -> dict[str, Any]:
|
|
28
|
+
"""Field metadata mapping a TOML-sourced `datetime` to `TomlAwareDateTime`."""
|
|
29
|
+
return {"marshmallow_field": TomlAwareDateTime(allow_none=True)}
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_microgrid.py
RENAMED
|
@@ -11,6 +11,8 @@ from typing import Any, ClassVar, Literal, Self, Type, cast, get_args
|
|
|
11
11
|
from marshmallow import Schema
|
|
12
12
|
from marshmallow_dataclass import dataclass
|
|
13
13
|
|
|
14
|
+
from ._fields import toml_datetime_metadata
|
|
15
|
+
|
|
14
16
|
ComponentType = Literal["grid", "pv", "battery", "consumption", "chp", "ev"]
|
|
15
17
|
"""Valid component types."""
|
|
16
18
|
|
|
@@ -98,10 +100,10 @@ class ComponentTypeConfig:
|
|
|
98
100
|
class PVConfig:
|
|
99
101
|
"""Configuration of a PV system in a microgrid."""
|
|
100
102
|
|
|
101
|
-
start_time: datetime | None = None
|
|
103
|
+
start_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
102
104
|
"""Start time of the PV system installation."""
|
|
103
105
|
|
|
104
|
-
end_time: datetime | None = None
|
|
106
|
+
end_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
105
107
|
"""End time of the PV system installation."""
|
|
106
108
|
|
|
107
109
|
peak_power: float | None = None
|
|
@@ -119,10 +121,10 @@ class WindConfig:
|
|
|
119
121
|
# pylint: disable=too-many-instance-attributes
|
|
120
122
|
"""Configuration of a wind turbine in a microgrid."""
|
|
121
123
|
|
|
122
|
-
start_time: datetime | None = None
|
|
124
|
+
start_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
123
125
|
"""Start time of the wind turbine installation."""
|
|
124
126
|
|
|
125
|
-
end_time: datetime | None = None
|
|
127
|
+
end_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
126
128
|
"""End time of the wind turbine installation."""
|
|
127
129
|
|
|
128
130
|
turbine_model: str | None = None
|
|
@@ -151,10 +153,10 @@ class WindConfig:
|
|
|
151
153
|
class BatteryConfig:
|
|
152
154
|
"""Configuration of a battery in a microgrid."""
|
|
153
155
|
|
|
154
|
-
start_time: datetime | None = None
|
|
156
|
+
start_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
155
157
|
"""Start time of the battery installation."""
|
|
156
158
|
|
|
157
|
-
end_time: datetime | None = None
|
|
159
|
+
end_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
158
160
|
"""End time of the battery installation."""
|
|
159
161
|
|
|
160
162
|
capacity: float | None = None
|
|
@@ -187,10 +189,10 @@ class MicrogridConfig:
|
|
|
187
189
|
altitude: float | None = None
|
|
188
190
|
"""Geographic altitude of the microgrid."""
|
|
189
191
|
|
|
190
|
-
start_time: datetime | None = None
|
|
192
|
+
start_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
191
193
|
"""Start time of the microgrid operation."""
|
|
192
194
|
|
|
193
|
-
end_time: datetime | None = None
|
|
195
|
+
end_time: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
194
196
|
"""End time of the microgrid operation."""
|
|
195
197
|
|
|
196
198
|
pv: dict[str, PVConfig] | None = None
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Format migrations for raw config documents.
|
|
5
|
+
|
|
6
|
+
Loading validates `assets.version` and stamps the current version, so a file
|
|
7
|
+
written for a newer reader is rejected rather than silently misread. Each
|
|
8
|
+
migration step rewrites the raw `dict` before it is loaded into the typed
|
|
9
|
+
model; steps are self-detecting and idempotent, so the `version` field does not
|
|
10
|
+
gate whether a step runs. No legacy layouts remain, so `_STEPS` is empty.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Callable
|
|
15
|
+
|
|
16
|
+
_CURRENT_VERSION = 1
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def migrate(raw: dict[str, Any], source: Path) -> dict[str, Any]:
|
|
20
|
+
"""Migrate a raw config document to the current format.
|
|
21
|
+
|
|
22
|
+
Reads and validates `assets.version` (absent means the oldest, `0`), applies
|
|
23
|
+
every self-detecting step, and stamps the current version. The version sits
|
|
24
|
+
inside `assets` so it tracks the assets format alone, not the whole document.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
raw: The raw document as parsed from a file.
|
|
28
|
+
source: The file it was parsed from, named in error messages.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
The document in the current format.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
TypeError: If `assets.version` is not an integer.
|
|
35
|
+
ValueError: If `assets.version` is outside the supported range.
|
|
36
|
+
"""
|
|
37
|
+
assets = raw.get("assets")
|
|
38
|
+
version = assets.get("version", 0) if isinstance(assets, dict) else 0
|
|
39
|
+
if not isinstance(version, int) or isinstance(version, bool):
|
|
40
|
+
raise TypeError(f"{source}: `assets.version` must be an integer")
|
|
41
|
+
if version < 0:
|
|
42
|
+
raise ValueError(f"{source}: `assets.version` must not be negative")
|
|
43
|
+
if version > _CURRENT_VERSION:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"{source}: assets version {version} is newer than supported "
|
|
46
|
+
f"version {_CURRENT_VERSION}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
for _, step in _STEPS:
|
|
50
|
+
raw = step(raw, source)
|
|
51
|
+
assets = raw.get("assets")
|
|
52
|
+
if isinstance(assets, dict):
|
|
53
|
+
assets["version"] = _CURRENT_VERSION
|
|
54
|
+
return raw
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_STEPS: list[tuple[int, Callable[[dict[str, Any], Path], dict[str, Any]]]] = []
|
|
58
|
+
"""Migration steps as `(from_version, step)`, oldest first."""
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_topology.py
RENAMED
|
@@ -26,6 +26,8 @@ from frequenz.client.assets import (
|
|
|
26
26
|
from marshmallow import Schema
|
|
27
27
|
from marshmallow_dataclass import dataclass
|
|
28
28
|
|
|
29
|
+
from ._fields import toml_datetime_metadata
|
|
30
|
+
|
|
29
31
|
_EIC_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-"
|
|
30
32
|
_MARKET_AREA_EU_DE = 101
|
|
31
33
|
|
|
@@ -165,10 +167,10 @@ class ValidityConfig:
|
|
|
165
167
|
participation: MarketParticipationType | None = None
|
|
166
168
|
"""The market use case, unset for a plain validity window."""
|
|
167
169
|
|
|
168
|
-
start: datetime | None = None
|
|
170
|
+
start: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
169
171
|
"""Inclusive start of the period."""
|
|
170
172
|
|
|
171
|
-
end: datetime | None = None
|
|
173
|
+
end: datetime | None = field(default=None, metadata=toml_datetime_metadata())
|
|
172
174
|
"""Exclusive end of the period."""
|
|
173
175
|
|
|
174
176
|
Schema: ClassVar[Type[Schema]] = Schema
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: frequenz-gridpool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: High-level interface to grid pools for the Frequenz platform.
|
|
5
5
|
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,9 +19,10 @@ Classifier: Typing :: Typed
|
|
|
19
19
|
Requires-Python: <4,>=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
+
Requires-Dist: marshmallow<5,>=3.18.0
|
|
22
23
|
Requires-Dist: marshmallow-dataclass<9,>=8.7.1
|
|
23
24
|
Requires-Dist: typing-extensions<5,>=4.14.1
|
|
24
|
-
Requires-Dist: frequenz-microgrid-component-graph<0.
|
|
25
|
+
Requires-Dist: frequenz-microgrid-component-graph<0.7,>=0.6.0
|
|
25
26
|
Requires-Dist: frequenz-client-assets<0.6,>=0.5.0
|
|
26
27
|
Provides-Extra: cli
|
|
27
28
|
Requires-Dist: asyncclick<9,>=8.3.0.4; extra == "cli"
|
|
@@ -31,8 +32,8 @@ Requires-Dist: frequenz-gridpool[cli]; extra == "render-graph"
|
|
|
31
32
|
Requires-Dist: matplotlib<4,>=3.7.0; extra == "render-graph"
|
|
32
33
|
Requires-Dist: networkx<4,>=3.0; extra == "render-graph"
|
|
33
34
|
Provides-Extra: dev-flake8
|
|
34
|
-
Requires-Dist: flake8==7.
|
|
35
|
-
Requires-Dist: flake8-datetimez==
|
|
35
|
+
Requires-Dist: flake8==7.4.1; extra == "dev-flake8"
|
|
36
|
+
Requires-Dist: flake8-datetimez==26.8.1; extra == "dev-flake8"
|
|
36
37
|
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
37
38
|
Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
|
|
38
39
|
Requires-Dist: pydoclint==0.9.1; extra == "dev-flake8"
|
|
@@ -41,31 +42,31 @@ Provides-Extra: dev-formatting
|
|
|
41
42
|
Requires-Dist: black==26.5.1; extra == "dev-formatting"
|
|
42
43
|
Requires-Dist: isort==8.0.1; extra == "dev-formatting"
|
|
43
44
|
Provides-Extra: dev-mkdocs
|
|
44
|
-
Requires-Dist: Markdown==3.
|
|
45
|
+
Requires-Dist: Markdown==3.11; extra == "dev-mkdocs"
|
|
45
46
|
Requires-Dist: black==26.5.1; extra == "dev-mkdocs"
|
|
46
47
|
Requires-Dist: mike==2.2.0; extra == "dev-mkdocs"
|
|
47
48
|
Requires-Dist: mkdocs-gen-files==0.6.1; extra == "dev-mkdocs"
|
|
48
49
|
Requires-Dist: mkdocs-literate-nav==0.6.3; extra == "dev-mkdocs"
|
|
49
50
|
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
50
51
|
Requires-Dist: mkdocs-material==9.7.7; extra == "dev-mkdocs"
|
|
51
|
-
Requires-Dist: mkdocstrings[python]==1.0.
|
|
52
|
-
Requires-Dist: mkdocstrings-python==2.0.
|
|
53
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
52
|
+
Requires-Dist: mkdocstrings[python]==1.0.7; extra == "dev-mkdocs"
|
|
53
|
+
Requires-Dist: mkdocstrings-python==2.0.9; extra == "dev-mkdocs"
|
|
54
|
+
Requires-Dist: frequenz-repo-config[lib]==0.19.0; extra == "dev-mkdocs"
|
|
54
55
|
Provides-Extra: dev-mypy
|
|
55
|
-
Requires-Dist: mypy==2.3.
|
|
56
|
+
Requires-Dist: mypy==2.3.1; extra == "dev-mypy"
|
|
56
57
|
Requires-Dist: types-networkx>=3.6.1.20251220; extra == "dev-mypy"
|
|
57
58
|
Requires-Dist: types-Markdown==3.10.2.20260712; extra == "dev-mypy"
|
|
58
59
|
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
59
60
|
Provides-Extra: dev-noxfile
|
|
60
|
-
Requires-Dist: nox==2026.
|
|
61
|
-
Requires-Dist: frequenz-repo-config[lib]==0.
|
|
61
|
+
Requires-Dist: nox==2026.8.17; extra == "dev-noxfile"
|
|
62
|
+
Requires-Dist: frequenz-repo-config[lib]==0.19.0; extra == "dev-noxfile"
|
|
62
63
|
Provides-Extra: dev-pylint
|
|
63
64
|
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
64
65
|
Provides-Extra: dev-pytest
|
|
65
66
|
Requires-Dist: pytest==9.1.1; extra == "dev-pytest"
|
|
66
|
-
Requires-Dist: pylint==4.0.
|
|
67
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.
|
|
68
|
-
Requires-Dist: pytest-mock==3.
|
|
67
|
+
Requires-Dist: pylint==4.0.9; extra == "dev-pytest"
|
|
68
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.19.0; extra == "dev-pytest"
|
|
69
|
+
Requires-Dist: pytest-mock==3.16.0; extra == "dev-pytest"
|
|
69
70
|
Requires-Dist: pytest-asyncio==1.4.0; extra == "dev-pytest"
|
|
70
71
|
Requires-Dist: async-solipsism==0.9; extra == "dev-pytest"
|
|
71
72
|
Requires-Dist: frequenz-gridpool[render-graph]; extra == "dev-pytest"
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/SOURCES.txt
RENAMED
|
@@ -14,6 +14,7 @@ src/frequenz/gridpool/cli/_patch_config.py
|
|
|
14
14
|
src/frequenz/gridpool/cli/_render_graph.py
|
|
15
15
|
src/frequenz/gridpool/config/__init__.py
|
|
16
16
|
src/frequenz/gridpool/config/_assets.py
|
|
17
|
+
src/frequenz/gridpool/config/_fields.py
|
|
17
18
|
src/frequenz/gridpool/config/_gridpool.py
|
|
18
19
|
src/frequenz/gridpool/config/_load.py
|
|
19
20
|
src/frequenz/gridpool/config/_microgrid.py
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/requires.txt
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
marshmallow<5,>=3.18.0
|
|
1
2
|
marshmallow-dataclass<9,>=8.7.1
|
|
2
3
|
typing-extensions<5,>=4.14.1
|
|
3
|
-
frequenz-microgrid-component-graph<0.
|
|
4
|
+
frequenz-microgrid-component-graph<0.7,>=0.6.0
|
|
4
5
|
frequenz-client-assets<0.6,>=0.5.0
|
|
5
6
|
|
|
6
7
|
[cli]
|
|
@@ -11,8 +12,8 @@ tomlkit<1,>=0.13.0
|
|
|
11
12
|
frequenz-gridpool[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest,render-graph]
|
|
12
13
|
|
|
13
14
|
[dev-flake8]
|
|
14
|
-
flake8==7.
|
|
15
|
-
flake8-datetimez==
|
|
15
|
+
flake8==7.4.1
|
|
16
|
+
flake8-datetimez==26.8.1
|
|
16
17
|
flake8-docstrings==1.7.0
|
|
17
18
|
flake8-pyproject==1.2.4
|
|
18
19
|
pydoclint==0.9.1
|
|
@@ -23,35 +24,35 @@ black==26.5.1
|
|
|
23
24
|
isort==8.0.1
|
|
24
25
|
|
|
25
26
|
[dev-mkdocs]
|
|
26
|
-
Markdown==3.
|
|
27
|
+
Markdown==3.11
|
|
27
28
|
black==26.5.1
|
|
28
29
|
mike==2.2.0
|
|
29
30
|
mkdocs-gen-files==0.6.1
|
|
30
31
|
mkdocs-literate-nav==0.6.3
|
|
31
32
|
mkdocs-macros-plugin==1.5.0
|
|
32
33
|
mkdocs-material==9.7.7
|
|
33
|
-
mkdocstrings[python]==1.0.
|
|
34
|
-
mkdocstrings-python==2.0.
|
|
35
|
-
frequenz-repo-config[lib]==0.
|
|
34
|
+
mkdocstrings[python]==1.0.7
|
|
35
|
+
mkdocstrings-python==2.0.9
|
|
36
|
+
frequenz-repo-config[lib]==0.19.0
|
|
36
37
|
|
|
37
38
|
[dev-mypy]
|
|
38
|
-
mypy==2.3.
|
|
39
|
+
mypy==2.3.1
|
|
39
40
|
types-networkx>=3.6.1.20251220
|
|
40
41
|
types-Markdown==3.10.2.20260712
|
|
41
42
|
frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
42
43
|
|
|
43
44
|
[dev-noxfile]
|
|
44
|
-
nox==2026.
|
|
45
|
-
frequenz-repo-config[lib]==0.
|
|
45
|
+
nox==2026.8.17
|
|
46
|
+
frequenz-repo-config[lib]==0.19.0
|
|
46
47
|
|
|
47
48
|
[dev-pylint]
|
|
48
49
|
frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
49
50
|
|
|
50
51
|
[dev-pytest]
|
|
51
52
|
pytest==9.1.1
|
|
52
|
-
pylint==4.0.
|
|
53
|
-
frequenz-repo-config[extra-lint-examples]==0.
|
|
54
|
-
pytest-mock==3.
|
|
53
|
+
pylint==4.0.9
|
|
54
|
+
frequenz-repo-config[extra-lint-examples]==0.19.0
|
|
55
|
+
pytest-mock==3.16.0
|
|
55
56
|
pytest-asyncio==1.4.0
|
|
56
57
|
async-solipsism==0.9
|
|
57
58
|
frequenz-gridpool[render-graph]
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
".github/workflows/black-migration.yaml",
|
|
14
14
|
".github/workflows/ci-pr.yaml",
|
|
15
15
|
".github/workflows/ci.yaml",
|
|
16
|
-
".github/workflows/dco-merge-queue.yml",
|
|
17
16
|
".github/workflows/isort-migration.yaml",
|
|
18
17
|
".github/workflows/labeler.yml",
|
|
19
18
|
".github/workflows/release-notes-check.yml",
|
|
@@ -45,6 +44,7 @@
|
|
|
45
44
|
"src/frequenz/gridpool/cli/_render_graph.py",
|
|
46
45
|
"src/frequenz/gridpool/config/__init__.py",
|
|
47
46
|
"src/frequenz/gridpool/config/_assets.py",
|
|
47
|
+
"src/frequenz/gridpool/config/_fields.py",
|
|
48
48
|
"src/frequenz/gridpool/config/_gridpool.py",
|
|
49
49
|
"src/frequenz/gridpool/config/_load.py",
|
|
50
50
|
"src/frequenz/gridpool/config/_microgrid.py",
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Frequenz Gridpool Library Release Notes
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
This release rounds out `gridpool-cli` for the `assets` config layout and adds
|
|
6
|
-
gridpool enterprise ownership to the config model. The CLI can now generate,
|
|
7
|
-
validate, patch, and query merged config files.
|
|
8
|
-
|
|
9
|
-
## Upgrading
|
|
10
|
-
|
|
11
|
-
- `generate-config` now writes the `assets.microgrids` layout and stamps
|
|
12
|
-
`assets.version`, instead of the legacy layout. Regenerate configs to get the
|
|
13
|
-
current format; generated files then load back without a migration.
|
|
14
|
-
|
|
15
|
-
- `generate-config --inplace` refuses legacy-layout files (top-level or
|
|
16
|
-
`meta`-nested), which it would duplicate rather than edit. Rebuild those from
|
|
17
|
-
scratch.
|
|
18
|
-
|
|
19
|
-
## New Features
|
|
20
|
-
|
|
21
|
-
- `gridpool-cli validate <files>` checks config files offline and exits
|
|
22
|
-
non-zero on the first error, to gate config-repo CI. Each file must be valid
|
|
23
|
-
on its own, so a record names its own key and required fields; the files are
|
|
24
|
-
then checked merged, for the cross-record checks.
|
|
25
|
-
|
|
26
|
-
- `gridpool-cli` accepts `FREQUENZ_API_KEY` and `FREQUENZ_API_SECRET` as a
|
|
27
|
-
fallback pair for `ASSETS_API_AUTH_KEY` and `ASSETS_API_SIGN_SECRET`.
|
|
28
|
-
|
|
29
|
-
- Gridpools are described under `assets.gridpools`, each entry naming the
|
|
30
|
-
enterprise that owns the gridpool. `AssetsConfig.find_enterprise(gridpool_id)`
|
|
31
|
-
returns the configured owner.
|
|
32
|
-
`gridpool-cli find-enterprise <gridpool_id> <files>` prints it from the config.
|
|
33
|
-
`AssetsConfig.check` enforces the one-enterprise-per-gridpool invariant: a
|
|
34
|
-
gridpool's microgrids may not disagree on it, and a declared enterprise must
|
|
35
|
-
match the inferred one.
|
|
36
|
-
|
|
37
|
-
- A config derived from the Assets API now carries each microgrid's
|
|
38
|
-
`enterprise_id`.
|
|
39
|
-
|
|
40
|
-
- `generate-config --inplace` refreshes managed values while preserving
|
|
41
|
-
formatting; `--fill-missing` only adds absent values. Generated patches are
|
|
42
|
-
validated before writing.
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
# License: MIT
|
|
2
|
-
# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
|
|
3
|
-
|
|
4
|
-
"""Format migrations for raw config documents.
|
|
5
|
-
|
|
6
|
-
Each step rewrites the raw `dict` a file parses to, before it is loaded into the
|
|
7
|
-
typed model. Steps are self-detecting and idempotent, so the `version` field
|
|
8
|
-
does not gate whether a step runs; it only records how old a file may be, which
|
|
9
|
-
bounds when a step may be dropped.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import logging
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
from typing import Any, Callable
|
|
15
|
-
|
|
16
|
-
_logger = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
_CURRENT_VERSION = 1
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def migrate(raw: dict[str, Any], source: Path) -> dict[str, Any]:
|
|
22
|
-
"""Migrate a raw config document to the current format.
|
|
23
|
-
|
|
24
|
-
Reads and validates `assets.version` (absent means the oldest, `0`), applies
|
|
25
|
-
every self-detecting step, and stamps the current version. The version sits
|
|
26
|
-
inside `assets` so it tracks the assets format alone, not the whole document.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
raw: The raw document as parsed from a file.
|
|
30
|
-
source: The file it was parsed from, for warnings and errors.
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
The document in the current format.
|
|
34
|
-
|
|
35
|
-
Raises:
|
|
36
|
-
TypeError: If `assets.version` is not an integer.
|
|
37
|
-
ValueError: If `assets.version` is outside the supported range.
|
|
38
|
-
"""
|
|
39
|
-
assets = raw.get("assets")
|
|
40
|
-
version = assets.get("version", 0) if isinstance(assets, dict) else 0
|
|
41
|
-
if not isinstance(version, int) or isinstance(version, bool):
|
|
42
|
-
raise TypeError(f"{source}: `assets.version` must be an integer")
|
|
43
|
-
if version < 0:
|
|
44
|
-
raise ValueError(f"{source}: `assets.version` must not be negative")
|
|
45
|
-
if version > _CURRENT_VERSION:
|
|
46
|
-
raise ValueError(
|
|
47
|
-
f"{source}: assets version {version} is newer than supported "
|
|
48
|
-
f"version {_CURRENT_VERSION}"
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
for _, step in _STEPS:
|
|
52
|
-
raw = step(raw, source)
|
|
53
|
-
assets = raw.get("assets")
|
|
54
|
-
if isinstance(assets, dict):
|
|
55
|
-
assets["version"] = _CURRENT_VERSION
|
|
56
|
-
return raw
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _nest_top_level_microgrids(raw: dict[str, Any], source: Path) -> dict[str, Any]:
|
|
60
|
-
"""Wrap deprecated top-level microgrid entries under `assets.microgrids`.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
raw: The raw document.
|
|
64
|
-
source: The file it was parsed from.
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
The document with its entries under `assets`.
|
|
68
|
-
|
|
69
|
-
Raises:
|
|
70
|
-
ValueError: If both layouts are present, a half-migrated file rather
|
|
71
|
-
than a merge.
|
|
72
|
-
"""
|
|
73
|
-
if "assets" in raw:
|
|
74
|
-
stray = sorted(k for k in raw if k != "assets")
|
|
75
|
-
if stray:
|
|
76
|
-
raise ValueError(
|
|
77
|
-
f"{source}: keys {stray} sit outside `assets` while the file "
|
|
78
|
-
"already has an `assets` table; move them under `assets.microgrids`."
|
|
79
|
-
)
|
|
80
|
-
return raw
|
|
81
|
-
|
|
82
|
-
_logger.warning(
|
|
83
|
-
"%s: top-level microgrid IDs are deprecated, "
|
|
84
|
-
"nest the entries under `assets.microgrids` instead.",
|
|
85
|
-
source,
|
|
86
|
-
)
|
|
87
|
-
return {"assets": {"microgrids": dict(raw)}}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _lift_microgrid_meta(raw: dict[str, Any], source: Path) -> dict[str, Any]:
|
|
91
|
-
"""Merge a deprecated nested `meta` table onto its microgrid entry.
|
|
92
|
-
|
|
93
|
-
Earlier files nested a microgrid's fields under `meta`; they now sit
|
|
94
|
-
directly on the entry.
|
|
95
|
-
|
|
96
|
-
Args:
|
|
97
|
-
raw: The raw document.
|
|
98
|
-
source: The file it was parsed from.
|
|
99
|
-
|
|
100
|
-
Returns:
|
|
101
|
-
The document with microgrid fields directly on each entry.
|
|
102
|
-
|
|
103
|
-
Raises:
|
|
104
|
-
TypeError: If a `meta` value is not a table.
|
|
105
|
-
ValueError: If a field is set both under `meta` and on the entry.
|
|
106
|
-
"""
|
|
107
|
-
assets = raw.get("assets")
|
|
108
|
-
if not isinstance(assets, dict):
|
|
109
|
-
return raw
|
|
110
|
-
microgrids = assets.get("microgrids")
|
|
111
|
-
if not isinstance(microgrids, dict):
|
|
112
|
-
return raw
|
|
113
|
-
for mid, entry in microgrids.items():
|
|
114
|
-
if not isinstance(entry, dict) or "meta" not in entry:
|
|
115
|
-
continue
|
|
116
|
-
meta = entry.pop("meta")
|
|
117
|
-
if not isinstance(meta, dict):
|
|
118
|
-
raise TypeError(f"{source}: microgrid {mid} `meta` must be a table")
|
|
119
|
-
_logger.warning(
|
|
120
|
-
"%s: microgrid %s nests fields under `meta`; that is deprecated, "
|
|
121
|
-
"set them directly on the entry instead.",
|
|
122
|
-
source,
|
|
123
|
-
mid,
|
|
124
|
-
)
|
|
125
|
-
for key, value in meta.items():
|
|
126
|
-
if key in entry:
|
|
127
|
-
raise ValueError(
|
|
128
|
-
f"{source}: microgrid {mid} sets `{key}` both under `meta` "
|
|
129
|
-
"and directly; keep only the direct one"
|
|
130
|
-
)
|
|
131
|
-
entry[key] = value
|
|
132
|
-
return raw
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
_STEPS: list[tuple[int, Callable[[dict[str, Any], Path], dict[str, Any]]]] = [
|
|
136
|
-
(0, _nest_top_level_microgrids),
|
|
137
|
-
(0, _lift_microgrid_meta),
|
|
138
|
-
]
|
|
139
|
-
"""Migration steps as `(from_version, step)`, oldest first."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/_graph_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/cli/_render_graph.py
RENAMED
|
File without changes
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/__init__.py
RENAMED
|
File without changes
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz/gridpool/config/_gridpool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{frequenz_gridpool-0.7.1 → frequenz_gridpool-0.9.0}/src/frequenz_gridpool.egg-info/top_level.txt
RENAMED
|
File without changes
|