frequenz-gridpool 0.0.1__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.0.1/LICENSE +21 -0
- frequenz_gridpool-0.0.1/MANIFEST.in +13 -0
- frequenz_gridpool-0.0.1/PKG-INFO +88 -0
- frequenz_gridpool-0.0.1/README.md +24 -0
- frequenz_gridpool-0.0.1/RELEASE_NOTES.md +17 -0
- frequenz_gridpool-0.0.1/pyproject.toml +176 -0
- frequenz_gridpool-0.0.1/setup.cfg +4 -0
- frequenz_gridpool-0.0.1/src/frequenz/gridpool/__init__.py +8 -0
- frequenz_gridpool-0.0.1/src/frequenz/gridpool/_microgrid_config.py +390 -0
- frequenz_gridpool-0.0.1/src/frequenz/gridpool/conftest.py +13 -0
- frequenz_gridpool-0.0.1/src/frequenz/gridpool/py.typed +0 -0
- frequenz_gridpool-0.0.1/src/frequenz_gridpool.egg-info/PKG-INFO +88 -0
- frequenz_gridpool-0.0.1/src/frequenz_gridpool.egg-info/SOURCES.txt +14 -0
- frequenz_gridpool-0.0.1/src/frequenz_gridpool.egg-info/dependency_links.txt +1 -0
- frequenz_gridpool-0.0.1/src/frequenz_gridpool.egg-info/requires.txt +48 -0
- frequenz_gridpool-0.0.1/src/frequenz_gridpool.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright © 2025 Frequenz Energy-as-a-Service GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
exclude .cookiecutter-replay.json
|
|
2
|
+
exclude .editorconfig
|
|
3
|
+
exclude .gitignore
|
|
4
|
+
exclude CODEOWNERS
|
|
5
|
+
exclude CONTRIBUTING.md
|
|
6
|
+
exclude mkdocs.yml
|
|
7
|
+
exclude noxfile.py
|
|
8
|
+
exclude src/conftest.py
|
|
9
|
+
recursive-exclude .github *
|
|
10
|
+
recursive-exclude benchmarks *
|
|
11
|
+
recursive-exclude docs *
|
|
12
|
+
recursive-exclude tests *
|
|
13
|
+
recursive-include py *.pyi
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frequenz-gridpool
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: High-level interface to grid pools for the Frequenz platform.
|
|
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-gridpool-python/
|
|
8
|
+
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-gridpool-python/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-gridpool-python/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-gridpool-python
|
|
11
|
+
Project-URL: Support, https://github.com/frequenz-floss/frequenz-gridpool-python/discussions/categories/support
|
|
12
|
+
Keywords: frequenz,python,lib,library,gridpool
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
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: marshmallow-dataclass<9,>=8.7.1
|
|
24
|
+
Requires-Dist: typing-extensions<5,>=4.14.1
|
|
25
|
+
Provides-Extra: dev-flake8
|
|
26
|
+
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
27
|
+
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
28
|
+
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
29
|
+
Requires-Dist: pydoclint==0.8.1; extra == "dev-flake8"
|
|
30
|
+
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
31
|
+
Provides-Extra: dev-formatting
|
|
32
|
+
Requires-Dist: black==25.11.0; extra == "dev-formatting"
|
|
33
|
+
Requires-Dist: isort==6.0.0; extra == "dev-formatting"
|
|
34
|
+
Provides-Extra: dev-mkdocs
|
|
35
|
+
Requires-Dist: Markdown==3.10; extra == "dev-mkdocs"
|
|
36
|
+
Requires-Dist: black==25.11.0; extra == "dev-mkdocs"
|
|
37
|
+
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
38
|
+
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocs-material==9.7.0; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocstrings[python]==0.30.1; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: mkdocstrings-python==1.19.0; extra == "dev-mkdocs"
|
|
44
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.6; extra == "dev-mkdocs"
|
|
45
|
+
Provides-Extra: dev-mypy
|
|
46
|
+
Requires-Dist: mypy==1.18.2; extra == "dev-mypy"
|
|
47
|
+
Requires-Dist: types-Markdown==3.10.0.20251106; extra == "dev-mypy"
|
|
48
|
+
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
49
|
+
Provides-Extra: dev-noxfile
|
|
50
|
+
Requires-Dist: nox==2025.11.12; extra == "dev-noxfile"
|
|
51
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.6; extra == "dev-noxfile"
|
|
52
|
+
Provides-Extra: dev-pylint
|
|
53
|
+
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
54
|
+
Provides-Extra: dev-pytest
|
|
55
|
+
Requires-Dist: pytest==8.4.1; extra == "dev-pytest"
|
|
56
|
+
Requires-Dist: pylint==4.0.3; extra == "dev-pytest"
|
|
57
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.6; extra == "dev-pytest"
|
|
58
|
+
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
59
|
+
Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
|
|
60
|
+
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
61
|
+
Provides-Extra: dev
|
|
62
|
+
Requires-Dist: frequenz-gridpool[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
|
+
Dynamic: license-file
|
|
64
|
+
|
|
65
|
+
# Frequenz Gridpool Library
|
|
66
|
+
|
|
67
|
+
[](https://github.com/frequenz-floss/frequenz-gridpool-python/actions/workflows/ci.yaml)
|
|
68
|
+
[](https://pypi.org/project/frequenz-gridpool/)
|
|
69
|
+
[](https://frequenz-floss.github.io/frequenz-gridpool-python/)
|
|
70
|
+
|
|
71
|
+
## Introduction
|
|
72
|
+
|
|
73
|
+
High-level interface to grid pools for the Frequenz platform.
|
|
74
|
+
|
|
75
|
+
TODO(cookiecutter): Improve the README file
|
|
76
|
+
|
|
77
|
+
## Supported Platforms
|
|
78
|
+
|
|
79
|
+
The following platforms are officially supported (tested):
|
|
80
|
+
|
|
81
|
+
- **Python:** 3.11
|
|
82
|
+
- **Operating System:** Ubuntu Linux 20.04
|
|
83
|
+
- **Architectures:** amd64, arm64
|
|
84
|
+
|
|
85
|
+
## Contributing
|
|
86
|
+
|
|
87
|
+
If you want to know how to build this project and contribute to it, please
|
|
88
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Frequenz Gridpool Library
|
|
2
|
+
|
|
3
|
+
[](https://github.com/frequenz-floss/frequenz-gridpool-python/actions/workflows/ci.yaml)
|
|
4
|
+
[](https://pypi.org/project/frequenz-gridpool/)
|
|
5
|
+
[](https://frequenz-floss.github.io/frequenz-gridpool-python/)
|
|
6
|
+
|
|
7
|
+
## Introduction
|
|
8
|
+
|
|
9
|
+
High-level interface to grid pools for the Frequenz platform.
|
|
10
|
+
|
|
11
|
+
TODO(cookiecutter): Improve the README file
|
|
12
|
+
|
|
13
|
+
## Supported Platforms
|
|
14
|
+
|
|
15
|
+
The following platforms are officially supported (tested):
|
|
16
|
+
|
|
17
|
+
- **Python:** 3.11
|
|
18
|
+
- **Operating System:** Ubuntu Linux 20.04
|
|
19
|
+
- **Architectures:** amd64, arm64
|
|
20
|
+
|
|
21
|
+
## Contributing
|
|
22
|
+
|
|
23
|
+
If you want to know how to build this project and contribute to it, please
|
|
24
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
<!-- 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
|
+
* Adds a configuration class for microgrids copied from `frequenz-floss/lib-notebooks`.
|
|
14
|
+
|
|
15
|
+
## Bug Fixes
|
|
16
|
+
|
|
17
|
+
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
[build-system]
|
|
5
|
+
requires = [
|
|
6
|
+
"setuptools == 80.9.0",
|
|
7
|
+
"setuptools_scm[toml] == 9.2.2",
|
|
8
|
+
"frequenz-repo-config[lib] == 0.13.6",
|
|
9
|
+
]
|
|
10
|
+
build-backend = "setuptools.build_meta"
|
|
11
|
+
|
|
12
|
+
[project]
|
|
13
|
+
name = "frequenz-gridpool"
|
|
14
|
+
description = "High-level interface to grid pools for the Frequenz platform."
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
license = { text = "MIT" }
|
|
17
|
+
keywords = ["frequenz", "python", "lib", "library", "gridpool"]
|
|
18
|
+
# TODO(cookiecutter): Remove and add more classifiers if appropriate
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Topic :: Software Development :: Libraries",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
requires-python = ">= 3.11, < 4"
|
|
29
|
+
# TODO(cookiecutter): Remove and add more dependencies if appropriate
|
|
30
|
+
dependencies = [
|
|
31
|
+
"marshmallow-dataclass>=8.7.1,<9",
|
|
32
|
+
"typing-extensions >= 4.14.1, < 5",
|
|
33
|
+
]
|
|
34
|
+
dynamic = ["version"]
|
|
35
|
+
|
|
36
|
+
[[project.authors]]
|
|
37
|
+
name = "Frequenz Energy-as-a-Service GmbH"
|
|
38
|
+
email = "floss@frequenz.com"
|
|
39
|
+
|
|
40
|
+
# TODO(cookiecutter): Remove and add more optional dependencies if appropriate
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
dev-flake8 = [
|
|
43
|
+
"flake8 == 7.3.0",
|
|
44
|
+
"flake8-docstrings == 1.7.0",
|
|
45
|
+
"flake8-pyproject == 1.2.3", # For reading the flake8 config from pyproject.toml
|
|
46
|
+
"pydoclint == 0.8.1",
|
|
47
|
+
"pydocstyle == 6.3.0",
|
|
48
|
+
]
|
|
49
|
+
dev-formatting = ["black == 25.11.0", "isort == 6.0.0"]
|
|
50
|
+
dev-mkdocs = [
|
|
51
|
+
"Markdown == 3.10",
|
|
52
|
+
"black == 25.11.0",
|
|
53
|
+
"mike == 2.1.3",
|
|
54
|
+
"mkdocs-gen-files == 0.5.0",
|
|
55
|
+
"mkdocs-literate-nav == 0.6.2",
|
|
56
|
+
"mkdocs-macros-plugin == 1.5.0",
|
|
57
|
+
"mkdocs-material == 9.7.0",
|
|
58
|
+
"mkdocstrings[python] == 0.30.1",
|
|
59
|
+
"mkdocstrings-python == 1.19.0",
|
|
60
|
+
"frequenz-repo-config[lib] == 0.13.6",
|
|
61
|
+
]
|
|
62
|
+
dev-mypy = [
|
|
63
|
+
"mypy == 1.18.2",
|
|
64
|
+
"types-Markdown == 3.10.0.20251106",
|
|
65
|
+
# For checking the noxfile, docs/ script, and tests
|
|
66
|
+
"frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
67
|
+
]
|
|
68
|
+
dev-noxfile = [
|
|
69
|
+
"nox == 2025.11.12",
|
|
70
|
+
"frequenz-repo-config[lib] == 0.13.6",
|
|
71
|
+
]
|
|
72
|
+
dev-pylint = [
|
|
73
|
+
# dev-pytest already defines a dependency to pylint because of the examples
|
|
74
|
+
# For checking the noxfile, docs/ script, and tests
|
|
75
|
+
"frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
76
|
+
]
|
|
77
|
+
dev-pytest = [
|
|
78
|
+
"pytest == 8.4.1",
|
|
79
|
+
"pylint == 4.0.3", # We need this to check for the examples
|
|
80
|
+
"frequenz-repo-config[extra-lint-examples] == 0.13.6",
|
|
81
|
+
"pytest-mock == 3.15.1",
|
|
82
|
+
"pytest-asyncio == 1.3.0",
|
|
83
|
+
"async-solipsism == 0.8",
|
|
84
|
+
]
|
|
85
|
+
dev = [
|
|
86
|
+
"frequenz-gridpool[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[project.urls]
|
|
90
|
+
Documentation = "https://frequenz-floss.github.io/frequenz-gridpool-python/"
|
|
91
|
+
Changelog = "https://github.com/frequenz-floss/frequenz-gridpool-python/releases"
|
|
92
|
+
Issues = "https://github.com/frequenz-floss/frequenz-gridpool-python/issues"
|
|
93
|
+
Repository = "https://github.com/frequenz-floss/frequenz-gridpool-python"
|
|
94
|
+
Support = "https://github.com/frequenz-floss/frequenz-gridpool-python/discussions/categories/support"
|
|
95
|
+
|
|
96
|
+
[tool.black]
|
|
97
|
+
line-length = 88
|
|
98
|
+
target-version = ['py311']
|
|
99
|
+
include = '\.pyi?$'
|
|
100
|
+
|
|
101
|
+
[tool.isort]
|
|
102
|
+
profile = "black"
|
|
103
|
+
line_length = 88
|
|
104
|
+
src_paths = ["benchmarks", "examples", "src", "tests"]
|
|
105
|
+
|
|
106
|
+
[tool.flake8]
|
|
107
|
+
# We give some flexibility to go over 88, there are cases like long URLs or
|
|
108
|
+
# code in documenation that have extra indentation. Black will still take care
|
|
109
|
+
# of making everything that can be 88 wide, 88 wide.
|
|
110
|
+
max-line-length = 100
|
|
111
|
+
extend-ignore = [
|
|
112
|
+
"E203", # Whitespace before ':' (conflicts with black)
|
|
113
|
+
"W503", # Line break before binary operator (conflicts with black)
|
|
114
|
+
]
|
|
115
|
+
# pydoclint options
|
|
116
|
+
style = "google"
|
|
117
|
+
check-return-types = false
|
|
118
|
+
check-yield-types = false
|
|
119
|
+
arg-type-hints-in-docstring = false
|
|
120
|
+
arg-type-hints-in-signature = true
|
|
121
|
+
allow-init-docstring = true
|
|
122
|
+
check-class-attributes = false
|
|
123
|
+
|
|
124
|
+
[tool.pylint.similarities]
|
|
125
|
+
ignore-comments = ['yes']
|
|
126
|
+
ignore-docstrings = ['yes']
|
|
127
|
+
ignore-imports = ['no']
|
|
128
|
+
min-similarity-lines = 40
|
|
129
|
+
|
|
130
|
+
[tool.pylint.messages_control]
|
|
131
|
+
disable = [
|
|
132
|
+
"too-few-public-methods",
|
|
133
|
+
"too-many-return-statements",
|
|
134
|
+
# disabled because it conflicts with isort
|
|
135
|
+
"wrong-import-order",
|
|
136
|
+
"ungrouped-imports",
|
|
137
|
+
# Checked by mypy (and pylint is very flaky checking these)
|
|
138
|
+
"unsubscriptable-object",
|
|
139
|
+
"no-member",
|
|
140
|
+
"no-name-in-module",
|
|
141
|
+
"possibly-used-before-assignment",
|
|
142
|
+
# Checked by flake8
|
|
143
|
+
"f-string-without-interpolation",
|
|
144
|
+
"line-too-long",
|
|
145
|
+
"missing-function-docstring",
|
|
146
|
+
"redefined-outer-name",
|
|
147
|
+
"unnecessary-lambda-assignment",
|
|
148
|
+
"unused-import",
|
|
149
|
+
"unused-variable",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
[tool.pytest.ini_options]
|
|
153
|
+
addopts = "-W=all -Werror -Wdefault::DeprecationWarning -Wdefault::PendingDeprecationWarning -vv"
|
|
154
|
+
testpaths = ["tests", "src"]
|
|
155
|
+
asyncio_mode = "auto"
|
|
156
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
157
|
+
required_plugins = ["pytest-asyncio", "pytest-mock"]
|
|
158
|
+
|
|
159
|
+
[tool.mypy]
|
|
160
|
+
explicit_package_bases = true
|
|
161
|
+
namespace_packages = true
|
|
162
|
+
# This option disables mypy cache, and it is sometimes useful to enable it if
|
|
163
|
+
# you are getting weird intermittent error, or error in the CI but not locally
|
|
164
|
+
# (or vice versa). In particular errors saying that type: ignore is not
|
|
165
|
+
# used but getting the original ignored error when removing the type: ignore.
|
|
166
|
+
# See for example: https://github.com/python/mypy/issues/2960
|
|
167
|
+
#no_incremental = true
|
|
168
|
+
packages = ["frequenz.gridpool"]
|
|
169
|
+
strict = true
|
|
170
|
+
|
|
171
|
+
[[tool.mypy.overrides]]
|
|
172
|
+
module = ["mkdocs_macros.*", "sybil", "sybil.*"]
|
|
173
|
+
ignore_missing_imports = true
|
|
174
|
+
|
|
175
|
+
[tool.setuptools_scm]
|
|
176
|
+
version_scheme = "post-release"
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Configuration for microgrids."""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import re
|
|
8
|
+
import tomllib
|
|
9
|
+
from dataclasses import field
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, ClassVar, Literal, Self, Type, cast, get_args
|
|
12
|
+
|
|
13
|
+
from marshmallow import Schema
|
|
14
|
+
from marshmallow_dataclass import dataclass
|
|
15
|
+
|
|
16
|
+
_logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
ComponentType = Literal["grid", "pv", "battery", "consumption", "chp", "ev"]
|
|
19
|
+
"""Valid component types."""
|
|
20
|
+
|
|
21
|
+
ComponentCategory = Literal["meter", "inverter", "component"]
|
|
22
|
+
"""Valid component categories."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class ComponentTypeConfig:
|
|
27
|
+
"""Configuration of a microgrid component type."""
|
|
28
|
+
|
|
29
|
+
meter: list[int] | None = None
|
|
30
|
+
"""List of meter IDs for this component."""
|
|
31
|
+
|
|
32
|
+
inverter: list[int] | None = None
|
|
33
|
+
"""List of inverter IDs for this component."""
|
|
34
|
+
|
|
35
|
+
component: list[int] | None = None
|
|
36
|
+
"""List of component IDs for this component."""
|
|
37
|
+
|
|
38
|
+
formula: dict[str, str] | None = None
|
|
39
|
+
"""Formula to calculate the power of this component."""
|
|
40
|
+
|
|
41
|
+
def __post_init__(self) -> None:
|
|
42
|
+
"""Set the default formula if none is provided."""
|
|
43
|
+
self.formula = self.formula or {}
|
|
44
|
+
if "AC_ACTIVE_POWER" not in self.formula:
|
|
45
|
+
self.formula["AC_ACTIVE_POWER"] = "+".join(
|
|
46
|
+
[f"#{cid}" for cid in self._default_cids()]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def cids(self, metric: str = "") -> list[int]:
|
|
50
|
+
"""Get component IDs for this component.
|
|
51
|
+
|
|
52
|
+
By default, the meter IDs are returned if available, otherwise the inverter IDs.
|
|
53
|
+
For components without meters or inverters, the component IDs are returned.
|
|
54
|
+
|
|
55
|
+
If a metric is provided, the component IDs are extracted from the formula.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
metric: Metric name of the formula.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
List of component IDs for this component.
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValueError: If the metric is not supported or improperly set.
|
|
65
|
+
"""
|
|
66
|
+
if metric:
|
|
67
|
+
if not isinstance(self.formula, dict):
|
|
68
|
+
raise ValueError("Formula must be a dictionary.")
|
|
69
|
+
formula = self.formula.get(metric)
|
|
70
|
+
if not formula:
|
|
71
|
+
raise ValueError(f"{metric} does not have a formula")
|
|
72
|
+
# Extract component IDs from the formula which are given as e.g. #123
|
|
73
|
+
pattern = r"#(\d+)"
|
|
74
|
+
return [int(e) for e in re.findall(pattern, self.formula[metric])]
|
|
75
|
+
|
|
76
|
+
return self._default_cids()
|
|
77
|
+
|
|
78
|
+
def _default_cids(self) -> list[int]:
|
|
79
|
+
"""Get the default component IDs for this component.
|
|
80
|
+
|
|
81
|
+
If available, the meter IDs are returned, otherwise the inverter IDs.
|
|
82
|
+
For components without meters or inverters, the component IDs are returned.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
List of component IDs for this component.
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
ValueError: If no IDs are available.
|
|
89
|
+
"""
|
|
90
|
+
if self.meter:
|
|
91
|
+
return self.meter
|
|
92
|
+
if self.inverter:
|
|
93
|
+
return self.inverter
|
|
94
|
+
if self.component:
|
|
95
|
+
return self.component
|
|
96
|
+
|
|
97
|
+
raise ValueError("No IDs available")
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def is_valid_type(cls, ctype: str) -> bool:
|
|
101
|
+
"""Check if `ctype` is a valid enum value."""
|
|
102
|
+
return ctype in get_args(ComponentType)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class PVConfig:
|
|
107
|
+
"""Configuration of a PV system in a microgrid."""
|
|
108
|
+
|
|
109
|
+
peak_power: float | None = None
|
|
110
|
+
"""Peak power of the PV system in Watt."""
|
|
111
|
+
|
|
112
|
+
rated_power: float | None = None
|
|
113
|
+
"""Rated power of the inverters in Watt."""
|
|
114
|
+
|
|
115
|
+
curtailable: bool = False
|
|
116
|
+
"""Flag to indicate if PV system can be curtailed."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass(frozen=True)
|
|
120
|
+
class WindConfig:
|
|
121
|
+
"""Configuration of a wind turbine in a microgrid."""
|
|
122
|
+
|
|
123
|
+
turbine_model: str | None = None
|
|
124
|
+
"""Model name of the wind turbine."""
|
|
125
|
+
|
|
126
|
+
rated_power: float | None = None
|
|
127
|
+
"""Rated power of the wind turbine in Watt."""
|
|
128
|
+
|
|
129
|
+
turbine_height: float | None = None
|
|
130
|
+
"""Height of the wind turbine in meters."""
|
|
131
|
+
|
|
132
|
+
number_of_turbines: int = 1
|
|
133
|
+
"""Number of wind turbines."""
|
|
134
|
+
|
|
135
|
+
hellmann_exponent: float | None = None
|
|
136
|
+
"""Hellmann exponent for wind speed extrapolation. See: https://w.wiki/FMw9"""
|
|
137
|
+
|
|
138
|
+
longitude: float | None = None
|
|
139
|
+
"""Geographic longitude of the wind turbine."""
|
|
140
|
+
|
|
141
|
+
latitude: float | None = None
|
|
142
|
+
"""Geographic latitude of the wind turbine."""
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=True)
|
|
146
|
+
class BatteryConfig:
|
|
147
|
+
"""Configuration of a battery in a microgrid."""
|
|
148
|
+
|
|
149
|
+
capacity: float | None = None
|
|
150
|
+
"""Capacity of the battery in Wh."""
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# pylint: disable=too-many-instance-attributes
|
|
154
|
+
@dataclass(frozen=True)
|
|
155
|
+
class Metadata:
|
|
156
|
+
"""Metadata for a microgrid."""
|
|
157
|
+
|
|
158
|
+
microgrid_id: int
|
|
159
|
+
"""ID of the microgrid."""
|
|
160
|
+
|
|
161
|
+
name: str | None = None
|
|
162
|
+
"""Name of the microgrid."""
|
|
163
|
+
|
|
164
|
+
enterprise_id: int | None = None
|
|
165
|
+
"""Enterprise ID of the microgrid."""
|
|
166
|
+
|
|
167
|
+
gid: int | None = None
|
|
168
|
+
"""Gridpool ID of the microgrid."""
|
|
169
|
+
|
|
170
|
+
delivery_area: str | None = None
|
|
171
|
+
"""Delivery area of the microgrid."""
|
|
172
|
+
|
|
173
|
+
latitude: float | None = None
|
|
174
|
+
"""Geographic latitude of the microgrid."""
|
|
175
|
+
|
|
176
|
+
longitude: float | None = None
|
|
177
|
+
"""Geographic longitude of the microgrid."""
|
|
178
|
+
|
|
179
|
+
altitude: float | None = None
|
|
180
|
+
"""Geographic altitude of the microgrid."""
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass
|
|
184
|
+
class MicrogridConfig:
|
|
185
|
+
"""Configuration of a microgrid."""
|
|
186
|
+
|
|
187
|
+
meta: Metadata
|
|
188
|
+
"""Metadata of the microgrid."""
|
|
189
|
+
|
|
190
|
+
pv: dict[str, PVConfig] | None = None
|
|
191
|
+
"""Configuration of the PV system."""
|
|
192
|
+
|
|
193
|
+
wind: dict[str, WindConfig] | None = None
|
|
194
|
+
"""Configuration of the wind turbines."""
|
|
195
|
+
|
|
196
|
+
battery: dict[str, BatteryConfig] | None = None
|
|
197
|
+
"""Configuration of the batteries."""
|
|
198
|
+
|
|
199
|
+
ctype: dict[str, ComponentTypeConfig] = field(default_factory=dict)
|
|
200
|
+
"""Mapping of component category types to ac power component config."""
|
|
201
|
+
|
|
202
|
+
def component_types(self) -> list[str]:
|
|
203
|
+
"""Get a list of all component types in the configuration."""
|
|
204
|
+
return list(self.ctype.keys())
|
|
205
|
+
|
|
206
|
+
def component_type_ids(
|
|
207
|
+
self,
|
|
208
|
+
component_type: str,
|
|
209
|
+
component_category: str | None = None,
|
|
210
|
+
metric: str = "",
|
|
211
|
+
) -> list[int]:
|
|
212
|
+
"""Get a list of all component IDs for a component type.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
component_type: Component type to be aggregated.
|
|
216
|
+
component_category: Specific category of component IDs to retrieve
|
|
217
|
+
(e.g., "meter", "inverter", or "component"). If not provided,
|
|
218
|
+
the default logic is used.
|
|
219
|
+
metric: Metric name of the formula if CIDs should be extracted from the formula.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
List of component IDs for this component type.
|
|
223
|
+
|
|
224
|
+
Raises:
|
|
225
|
+
ValueError: If the component type is unknown.
|
|
226
|
+
KeyError: If `component_category` is invalid.
|
|
227
|
+
"""
|
|
228
|
+
cfg = self.ctype.get(component_type)
|
|
229
|
+
if not cfg:
|
|
230
|
+
raise ValueError(f"{component_type} not found in config.")
|
|
231
|
+
|
|
232
|
+
if component_category:
|
|
233
|
+
valid_categories = get_args(ComponentCategory)
|
|
234
|
+
if component_category not in valid_categories:
|
|
235
|
+
raise KeyError(
|
|
236
|
+
f"Invalid component category: {component_category}. "
|
|
237
|
+
f"Valid categories are {valid_categories}"
|
|
238
|
+
)
|
|
239
|
+
category_ids = cast(list[int], getattr(cfg, component_category, []))
|
|
240
|
+
return category_ids
|
|
241
|
+
|
|
242
|
+
return cfg.cids(metric)
|
|
243
|
+
|
|
244
|
+
def formula(self, component_type: str, metric: str) -> str:
|
|
245
|
+
"""Get the formula for a component type.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
component_type: Component type to be aggregated.
|
|
249
|
+
metric: Metric to be aggregated.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Formula to be used for this aggregated component as string.
|
|
253
|
+
|
|
254
|
+
Raises:
|
|
255
|
+
ValueError: If the component type is unknown or formula is missing.
|
|
256
|
+
"""
|
|
257
|
+
cfg = self.ctype.get(component_type)
|
|
258
|
+
if not cfg:
|
|
259
|
+
raise ValueError(f"{component_type} not found in config.")
|
|
260
|
+
if cfg.formula is None:
|
|
261
|
+
raise ValueError(f"No formula set for {component_type}")
|
|
262
|
+
formula = cfg.formula.get(metric)
|
|
263
|
+
if not formula:
|
|
264
|
+
raise ValueError(f"{component_type} is missing formula for {metric}")
|
|
265
|
+
|
|
266
|
+
return formula
|
|
267
|
+
|
|
268
|
+
Schema: ClassVar[Type[Schema]] = Schema
|
|
269
|
+
|
|
270
|
+
@classmethod
|
|
271
|
+
def _load_table_entries(cls, data: dict[str, Any]) -> dict[str, Self]:
|
|
272
|
+
"""Load microgrid configurations from table entries.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
data: The loaded TOML data.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
A dict mapping microgrid IDs to MicrogridConfig instances.
|
|
279
|
+
|
|
280
|
+
Raises:
|
|
281
|
+
ValueError: If top-level keys are not numeric microgrid IDs
|
|
282
|
+
or if there is a microgrid ID mismatch.
|
|
283
|
+
TypeError: If microgrid data is not a dict.
|
|
284
|
+
"""
|
|
285
|
+
if not all(str(k).isdigit() for k in data.keys()):
|
|
286
|
+
raise ValueError("All top-level keys must be numeric microgrid IDs.")
|
|
287
|
+
|
|
288
|
+
mgrids = {}
|
|
289
|
+
for mid, entry in data.items():
|
|
290
|
+
if not mid.isdigit():
|
|
291
|
+
raise ValueError(
|
|
292
|
+
f"Table reader: Microgrid ID key must be numeric, got {mid}"
|
|
293
|
+
)
|
|
294
|
+
if not isinstance(entry, dict):
|
|
295
|
+
raise TypeError("Table reader: Each microgrid entry must be a dict")
|
|
296
|
+
|
|
297
|
+
mgrid = cls.Schema().load(entry)
|
|
298
|
+
if mgrid.meta is None or mgrid.meta.microgrid_id is None:
|
|
299
|
+
raise ValueError(
|
|
300
|
+
"Table reader: Each microgrid entry must have a meta.microgrid_id"
|
|
301
|
+
)
|
|
302
|
+
if int(mgrid.meta.microgrid_id) != int(mid):
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Table reader: Microgrid ID mismatch: key {mid} != {mgrid.meta.microgrid_id}"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
mgrids[mid] = mgrid
|
|
308
|
+
|
|
309
|
+
return mgrids
|
|
310
|
+
|
|
311
|
+
@classmethod
|
|
312
|
+
def load_from_file(cls, config_path: Path) -> dict[str, Self]:
|
|
313
|
+
"""
|
|
314
|
+
Load and validate configuration settings from a TOML file.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
config_path: the path to the TOML configuration file.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
A dict mapping microgrid IDs to MicrogridConfig instances.
|
|
321
|
+
"""
|
|
322
|
+
with config_path.open("rb") as f:
|
|
323
|
+
data = tomllib.load(f)
|
|
324
|
+
|
|
325
|
+
assert isinstance(data, dict)
|
|
326
|
+
|
|
327
|
+
return cls._load_table_entries(data)
|
|
328
|
+
|
|
329
|
+
@staticmethod
|
|
330
|
+
def load_configs(
|
|
331
|
+
microgrid_config_files: str | Path | list[str | Path] | None = None,
|
|
332
|
+
microgrid_config_dir: str | Path | None = None,
|
|
333
|
+
) -> dict[str, "MicrogridConfig"]:
|
|
334
|
+
"""Load multiple microgrid configurations from a file.
|
|
335
|
+
|
|
336
|
+
Configs for a single microgrid are expected to be in a single file.
|
|
337
|
+
Later files with the same microgrid ID will overwrite the previous configs.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
microgrid_config_files: Path to a single microgrid config file or list of paths.
|
|
341
|
+
microgrid_config_dir: Directory containing multiple microgrid config files.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
Dictionary of single microgrid formula configs with microgrid IDs as keys.
|
|
345
|
+
|
|
346
|
+
Raises:
|
|
347
|
+
ValueError: If no config files or dir is provided, or if no config files are found.
|
|
348
|
+
"""
|
|
349
|
+
if microgrid_config_files is None and microgrid_config_dir is None:
|
|
350
|
+
raise ValueError(
|
|
351
|
+
"No microgrid config path or directory provided. "
|
|
352
|
+
"Please provide at least one."
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
config_files: list[Path] = []
|
|
356
|
+
|
|
357
|
+
if microgrid_config_files:
|
|
358
|
+
if isinstance(microgrid_config_files, str):
|
|
359
|
+
config_files = [Path(microgrid_config_files)]
|
|
360
|
+
elif isinstance(microgrid_config_files, Path):
|
|
361
|
+
config_files = [microgrid_config_files]
|
|
362
|
+
elif isinstance(microgrid_config_files, list):
|
|
363
|
+
config_files = [Path(f) for f in microgrid_config_files]
|
|
364
|
+
|
|
365
|
+
if microgrid_config_dir:
|
|
366
|
+
if Path(microgrid_config_dir).is_dir():
|
|
367
|
+
config_files += list(Path(microgrid_config_dir).glob("*.toml"))
|
|
368
|
+
else:
|
|
369
|
+
raise ValueError(
|
|
370
|
+
f"Microgrid config directory {microgrid_config_dir} "
|
|
371
|
+
"is not a directory"
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
if len(config_files) == 0:
|
|
375
|
+
raise ValueError(
|
|
376
|
+
"No microgrid config files found. "
|
|
377
|
+
"Please provide at least one valid config file."
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
microgrid_configs: dict[str, "MicrogridConfig"] = {}
|
|
381
|
+
|
|
382
|
+
for config_path in config_files:
|
|
383
|
+
if not config_path.is_file():
|
|
384
|
+
_logger.warning("Config path %s is not a file, skipping.", config_path)
|
|
385
|
+
continue
|
|
386
|
+
|
|
387
|
+
mcfgs = MicrogridConfig.load_from_file(config_path)
|
|
388
|
+
microgrid_configs.update({str(key): value for key, value in mcfgs.items()})
|
|
389
|
+
|
|
390
|
+
return microgrid_configs
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Validate docstring code examples.
|
|
5
|
+
|
|
6
|
+
Code examples are often wrapped in triple backticks (```) within docstrings.
|
|
7
|
+
This plugin extracts these code examples and validates them using pylint.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from frequenz.repo.config.pytest import examples
|
|
11
|
+
from sybil import Sybil
|
|
12
|
+
|
|
13
|
+
pytest_collect_file = Sybil(**examples.get_sybil_arguments()).pytest()
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frequenz-gridpool
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: High-level interface to grid pools for the Frequenz platform.
|
|
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-gridpool-python/
|
|
8
|
+
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-gridpool-python/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-gridpool-python/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-gridpool-python
|
|
11
|
+
Project-URL: Support, https://github.com/frequenz-floss/frequenz-gridpool-python/discussions/categories/support
|
|
12
|
+
Keywords: frequenz,python,lib,library,gridpool
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
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: marshmallow-dataclass<9,>=8.7.1
|
|
24
|
+
Requires-Dist: typing-extensions<5,>=4.14.1
|
|
25
|
+
Provides-Extra: dev-flake8
|
|
26
|
+
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
27
|
+
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
28
|
+
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
29
|
+
Requires-Dist: pydoclint==0.8.1; extra == "dev-flake8"
|
|
30
|
+
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
31
|
+
Provides-Extra: dev-formatting
|
|
32
|
+
Requires-Dist: black==25.11.0; extra == "dev-formatting"
|
|
33
|
+
Requires-Dist: isort==6.0.0; extra == "dev-formatting"
|
|
34
|
+
Provides-Extra: dev-mkdocs
|
|
35
|
+
Requires-Dist: Markdown==3.10; extra == "dev-mkdocs"
|
|
36
|
+
Requires-Dist: black==25.11.0; extra == "dev-mkdocs"
|
|
37
|
+
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
38
|
+
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocs-material==9.7.0; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocstrings[python]==0.30.1; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: mkdocstrings-python==1.19.0; extra == "dev-mkdocs"
|
|
44
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.6; extra == "dev-mkdocs"
|
|
45
|
+
Provides-Extra: dev-mypy
|
|
46
|
+
Requires-Dist: mypy==1.18.2; extra == "dev-mypy"
|
|
47
|
+
Requires-Dist: types-Markdown==3.10.0.20251106; extra == "dev-mypy"
|
|
48
|
+
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
49
|
+
Provides-Extra: dev-noxfile
|
|
50
|
+
Requires-Dist: nox==2025.11.12; extra == "dev-noxfile"
|
|
51
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.6; extra == "dev-noxfile"
|
|
52
|
+
Provides-Extra: dev-pylint
|
|
53
|
+
Requires-Dist: frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
54
|
+
Provides-Extra: dev-pytest
|
|
55
|
+
Requires-Dist: pytest==8.4.1; extra == "dev-pytest"
|
|
56
|
+
Requires-Dist: pylint==4.0.3; extra == "dev-pytest"
|
|
57
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.6; extra == "dev-pytest"
|
|
58
|
+
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
59
|
+
Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
|
|
60
|
+
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
61
|
+
Provides-Extra: dev
|
|
62
|
+
Requires-Dist: frequenz-gridpool[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
63
|
+
Dynamic: license-file
|
|
64
|
+
|
|
65
|
+
# Frequenz Gridpool Library
|
|
66
|
+
|
|
67
|
+
[](https://github.com/frequenz-floss/frequenz-gridpool-python/actions/workflows/ci.yaml)
|
|
68
|
+
[](https://pypi.org/project/frequenz-gridpool/)
|
|
69
|
+
[](https://frequenz-floss.github.io/frequenz-gridpool-python/)
|
|
70
|
+
|
|
71
|
+
## Introduction
|
|
72
|
+
|
|
73
|
+
High-level interface to grid pools for the Frequenz platform.
|
|
74
|
+
|
|
75
|
+
TODO(cookiecutter): Improve the README file
|
|
76
|
+
|
|
77
|
+
## Supported Platforms
|
|
78
|
+
|
|
79
|
+
The following platforms are officially supported (tested):
|
|
80
|
+
|
|
81
|
+
- **Python:** 3.11
|
|
82
|
+
- **Operating System:** Ubuntu Linux 20.04
|
|
83
|
+
- **Architectures:** amd64, arm64
|
|
84
|
+
|
|
85
|
+
## Contributing
|
|
86
|
+
|
|
87
|
+
If you want to know how to build this project and contribute to it, please
|
|
88
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
RELEASE_NOTES.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
src/frequenz/gridpool/__init__.py
|
|
7
|
+
src/frequenz/gridpool/_microgrid_config.py
|
|
8
|
+
src/frequenz/gridpool/conftest.py
|
|
9
|
+
src/frequenz/gridpool/py.typed
|
|
10
|
+
src/frequenz_gridpool.egg-info/PKG-INFO
|
|
11
|
+
src/frequenz_gridpool.egg-info/SOURCES.txt
|
|
12
|
+
src/frequenz_gridpool.egg-info/dependency_links.txt
|
|
13
|
+
src/frequenz_gridpool.egg-info/requires.txt
|
|
14
|
+
src/frequenz_gridpool.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
marshmallow-dataclass<9,>=8.7.1
|
|
2
|
+
typing-extensions<5,>=4.14.1
|
|
3
|
+
|
|
4
|
+
[dev]
|
|
5
|
+
frequenz-gridpool[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]
|
|
6
|
+
|
|
7
|
+
[dev-flake8]
|
|
8
|
+
flake8==7.3.0
|
|
9
|
+
flake8-docstrings==1.7.0
|
|
10
|
+
flake8-pyproject==1.2.3
|
|
11
|
+
pydoclint==0.8.1
|
|
12
|
+
pydocstyle==6.3.0
|
|
13
|
+
|
|
14
|
+
[dev-formatting]
|
|
15
|
+
black==25.11.0
|
|
16
|
+
isort==6.0.0
|
|
17
|
+
|
|
18
|
+
[dev-mkdocs]
|
|
19
|
+
Markdown==3.10
|
|
20
|
+
black==25.11.0
|
|
21
|
+
mike==2.1.3
|
|
22
|
+
mkdocs-gen-files==0.5.0
|
|
23
|
+
mkdocs-literate-nav==0.6.2
|
|
24
|
+
mkdocs-macros-plugin==1.5.0
|
|
25
|
+
mkdocs-material==9.7.0
|
|
26
|
+
mkdocstrings[python]==0.30.1
|
|
27
|
+
mkdocstrings-python==1.19.0
|
|
28
|
+
frequenz-repo-config[lib]==0.13.6
|
|
29
|
+
|
|
30
|
+
[dev-mypy]
|
|
31
|
+
mypy==1.18.2
|
|
32
|
+
types-Markdown==3.10.0.20251106
|
|
33
|
+
frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
34
|
+
|
|
35
|
+
[dev-noxfile]
|
|
36
|
+
nox==2025.11.12
|
|
37
|
+
frequenz-repo-config[lib]==0.13.6
|
|
38
|
+
|
|
39
|
+
[dev-pylint]
|
|
40
|
+
frequenz-gridpool[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
41
|
+
|
|
42
|
+
[dev-pytest]
|
|
43
|
+
pytest==8.4.1
|
|
44
|
+
pylint==4.0.3
|
|
45
|
+
frequenz-repo-config[extra-lint-examples]==0.13.6
|
|
46
|
+
pytest-mock==3.15.1
|
|
47
|
+
pytest-asyncio==1.3.0
|
|
48
|
+
async-solipsism==0.8
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
frequenz
|