diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml
index b39c963..8839cd6 100644
--- a/.github/workflows/ci-pr.yaml
+++ b/.github/workflows/ci-pr.yaml
@@ -3,10 +3,6 @@ name: Test PR
on:
pull_request:
-permissions:
- # Read repository contents for checkout and dependency resolution only.
- contents: read
-
env:
# Please make sure this version is included in the `matrix`, as the
# `matrix` section can't use `env`, so it must be entered manually
@@ -21,7 +17,7 @@ jobs:
steps:
- name: Run nox
- uses: frequenz-floss/gh-action-nox@80a9845a59ffc71d27b9c41099eb6cb55bc7b671 # v1.1.1
+ uses: frequenz-floss/gh-action-nox@v1.0.1
with:
python-version: "3.11"
nox-session: ci_checks_max
@@ -31,15 +27,15 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Setup Git
- uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0
+ uses: frequenz-floss/gh-action-setup-git@v1.0.0
- name: Fetch sources
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@v5
with:
submodules: true
- name: Setup Python
- uses: frequenz-floss/gh-action-setup-python-with-deps@e4d0b2ef8f5a1612d7827f3abaef17c931d2b946 # v1.0.2
+ uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
dependencies: .[dev-mkdocs]
@@ -48,14 +44,11 @@ jobs:
env:
MIKE_VERSION: gh-${{ github.job }}
run: |
- # mike is installed as a console script, not a runnable module.
- # Run the installed script under isolated mode to avoid importing from
- # the workspace when building docs from checked-out code.
- python -I "$(command -v mike)" deploy "$MIKE_VERSION"
- python -I "$(command -v mike)" set-default "$MIKE_VERSION"
+ mike deploy $MIKE_VERSION
+ mike set-default $MIKE_VERSION
- name: Upload site
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ uses: actions/upload-artifact@v5
with:
name: docs-site
path: site/
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 1f67e9a..96c86b0 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -15,10 +15,6 @@ on:
- "dependabot/**"
workflow_dispatch:
-permissions:
- # Read repository contents for checkout and dependency resolution only.
- contents: read
-
env:
# Please make sure this version is included in the `matrix`, as the
# `matrix` section can't use `env`, so it must be entered manually
@@ -32,22 +28,27 @@ jobs:
strategy:
fail-fast: false
matrix:
- platform:
+ target:
- ubuntu-24.04
- ubuntu-24.04-arm
+ - windows-latest
+ - macos-15-intel
+ - macos-15
python:
- "3.11"
- "3.12"
+ - "3.13"
+ - "3.14"
nox-session:
# To speed things up a bit we use the special ci_checks_max session
# that uses the same venv to run multiple linting sessions
- "ci_checks_max"
- "pytest_min"
- runs-on: ${{ matrix.platform }}
+ runs-on: ${{ matrix.target }}
steps:
- name: Run nox
- uses: frequenz-floss/gh-action-nox@80a9845a59ffc71d27b9c41099eb6cb55bc7b671 # v1.1.1
+ uses: frequenz-floss/gh-action-nox@e1351cf45e05e85afc1c79ab883e06322892d34c # v1.1.0
with:
python-version: ${{ matrix.python }}
nox-session: ${{ matrix.nox-session }}
@@ -62,76 +63,193 @@ jobs:
needs: ["nox"]
# We skip this job only if nox was also skipped
if: always() && needs.nox.result != 'skipped'
- runs-on: ubuntu-slim
- # Drop token permissions: this job only checks matrix status from `needs`.
- permissions: {}
+ runs-on: ubuntu-24.04
env:
DEPS_RESULT: ${{ needs.nox.result }}
steps:
- name: Check matrix job result
run: test "$DEPS_RESULT" = "success"
- build:
- name: Build distribution packages
- # Since this is a pure Python package, we only need to build it once. If it
- # had any architecture specific code, we would need to build it for each
- # architecture.
- runs-on: ubuntu-24.04
-
+ build-linux:
+ runs-on: ${{ matrix.platform.runner }}
+ strategy:
+ matrix:
+ platform:
+ - runner: ubuntu-22.04
+ target: x86_64
+ - runner: ubuntu-22.04
+ target: aarch64
steps:
- - name: Setup Git
- uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0
+ - uses: actions/checkout@v5
+ - uses: actions/setup-python@v6
+ with:
+ python-version: 3.x
+ - name: Build wheels
+ uses: PyO3/maturin-action@v1
+ with:
+ target: ${{ matrix.platform.target }}
+ args: --release --out dist --find-interpreter
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ manylinux: auto
+ - name: Upload wheels
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-linux-${{ matrix.platform.target }}
+ path: dist
- - name: Fetch sources
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ build-musllinux:
+ runs-on: ${{ matrix.platform.runner }}
+ strategy:
+ matrix:
+ platform:
+ - runner: ubuntu-22.04
+ target: x86_64
+ - runner: ubuntu-22.04
+ target: aarch64
+ steps:
+ - uses: actions/checkout@v5
+ - uses: actions/setup-python@v6
with:
- submodules: true
+ python-version: 3.x
+ - name: Build wheels
+ uses: PyO3/maturin-action@v1
+ with:
+ target: ${{ matrix.platform.target }}
+ args: --release --out dist --find-interpreter
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ manylinux: musllinux_1_2
+ - name: Upload wheels
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-musllinux-${{ matrix.platform.target }}
+ path: dist
- - name: Setup Python
- uses: frequenz-floss/gh-action-setup-python-with-deps@e4d0b2ef8f5a1612d7827f3abaef17c931d2b946 # v1.0.2
+ build-windows:
+ runs-on: ${{ matrix.platform.runner }}
+ strategy:
+ matrix:
+ platform:
+ - runner: windows-latest
+ target: x64
+ steps:
+ - uses: actions/checkout@v5
+ - uses: actions/setup-python@v6
with:
- python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
- dependencies: build
+ python-version: 3.x
+ architecture: ${{ matrix.platform.target }}
+ - name: Build wheels
+ uses: PyO3/maturin-action@v1
+ with:
+ target: ${{ matrix.platform.target }}
+ args: --release --out dist --find-interpreter
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ - name: Upload wheels
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-windows-${{ matrix.platform.target }}
+ path: dist
- - name: Build the source and binary distribution
- run: python -Im build
+ build-macos:
+ runs-on: ${{ matrix.platform.runner }}
+ strategy:
+ matrix:
+ platform:
+ - runner: macos-15-intel
+ target: x86_64
+ - runner: macos-15
+ target: aarch64
+ steps:
+ - uses: actions/checkout@v5
+ - uses: actions/setup-python@v6
+ with:
+ python-version: 3.x
+ - name: Build wheels
+ uses: PyO3/maturin-action@v1
+ with:
+ target: ${{ matrix.platform.target }}
+ args: --release --out dist --find-interpreter
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ - name: Upload wheels
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-macos-${{ matrix.platform.target }}
+ path: dist
- - name: Upload distribution files
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ build-sdist:
+ name: Build source distribution packages
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v5
+ - name: Build sdist
+ uses: PyO3/maturin-action@v1
with:
- name: dist-packages
- path: dist/
- if-no-files-found: error
+ command: sdist
+ args: --out dist
+ - name: Upload sdist
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-sdist
+ path: dist/*.tar.gz
test-installation:
name: Test package installation
- needs: ["build"]
+ needs:
+ - "build-linux"
+ - "build-musllinux"
+ - "build-windows"
+ - "build-macos"
+ - "build-sdist"
strategy:
fail-fast: false
matrix:
platform:
- - ubuntu-24.04
- - ubuntu-24.04-arm
+ - runner: ubuntu-24.04
+ image: linux
+ tag: manylinux
+ target: x86_64
+ - runner: ubuntu-24.04-arm
+ image: linux
+ tag: manylinux
+ target: aarch64
+ - runner: windows-latest
+ image: windows
+ tag: win
+ target: x64
+ - runner: macos-15-intel
+ image: macos
+ tag: macosx
+ target: x86_64
+ - runner: macos-15
+ image: macos
+ tag: macosx
+ target: aarch64
python:
- - "3.11"
- - "3.12"
- runs-on: ${{ matrix.platform }}
+ - semver: "3.11"
+ wheelver: cp311
+ - semver: "3.12"
+ wheelver: cp312
+ - semver: "3.13"
+ wheelver: cp313
+ - semver: "3.14"
+ wheelver: cp314
+ runs-on: ${{ matrix.platform.runner }}
steps:
- name: Setup Git
- uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0
+ uses: frequenz-floss/gh-action-setup-git@v1.0.0
- name: Print environment (debug)
run: env
- name: Download package
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ uses: actions/download-artifact@v6
with:
- name: dist-packages
+ name: wheels-${{ matrix.platform.image }}-${{ matrix.platform.target }}
path: dist
# This is necessary for the `pip` caching in the setup-python action to work
- name: Fetch the pyproject.toml file for this action hash
+ shell: bash
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
@@ -145,13 +263,13 @@ jobs:
> pyproject.toml
- name: Setup Python
- uses: frequenz-floss/gh-action-setup-python-with-deps@e4d0b2ef8f5a1612d7827f3abaef17c931d2b946 # v1.0.2
+ uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1
with:
- python-version: ${{ matrix.python }}
- dependencies: dist/*.whl
+ python-version: ${{ matrix.python.semver }}
+ dependencies: dist/*${{ matrix.python.wheelver }}-${{ matrix.platform.tag }}*.whl
- name: Print installed packages (debug)
- run: python -Im pip freeze
+ run: python -m pip freeze
# This job runs if all the `test-installation` matrix jobs ran and succeeded.
# It is only used to have a single job that we can require in branch
@@ -163,9 +281,7 @@ jobs:
needs: ["test-installation"]
# We skip this job only if test-installation was also skipped
if: always() && needs.test-installation.result != 'skipped'
- runs-on: ubuntu-slim
- # Drop token permissions: this job only checks matrix status from `needs`.
- permissions: {}
+ runs-on: ubuntu-24.04
env:
DEPS_RESULT: ${{ needs.test-installation.result }}
steps:
@@ -178,15 +294,15 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Setup Git
- uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0
+ uses: frequenz-floss/gh-action-setup-git@v1.0.0
- name: Fetch sources
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@v5
with:
submodules: true
- name: Setup Python
- uses: frequenz-floss/gh-action-setup-python-with-deps@e4d0b2ef8f5a1612d7827f3abaef17c931d2b946 # v1.0.2
+ uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
dependencies: .[dev-mkdocs]
@@ -195,14 +311,11 @@ jobs:
env:
MIKE_VERSION: gh-${{ github.job }}
run: |
- # mike is installed as a console script, not a runnable module.
- # Run the installed script under isolated mode to avoid importing from
- # the workspace when building docs from checked-out code.
- python -I "$(command -v mike)" deploy "$MIKE_VERSION"
- python -I "$(command -v mike)" set-default "$MIKE_VERSION"
+ mike deploy $MIKE_VERSION
+ mike set-default $MIKE_VERSION
- name: Upload site
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ uses: actions/upload-artifact@v5
with:
name: docs-site
path: site/
@@ -214,19 +327,18 @@ jobs:
if: github.event_name == 'push'
runs-on: ubuntu-24.04
permissions:
- # Push generated documentation updates to the `gh-pages` branch.
contents: write
steps:
- name: Setup Git
- uses: frequenz-floss/gh-action-setup-git@16952aac3ccc01d27412fe0dea3ea946530dcace # v1.0.0
+ uses: frequenz-floss/gh-action-setup-git@v1.0.0
- name: Fetch sources
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@v5
with:
submodules: true
- name: Setup Python
- uses: frequenz-floss/gh-action-setup-python-with-deps@e4d0b2ef8f5a1612d7827f3abaef17c931d2b946 # v1.0.2
+ uses: frequenz-floss/gh-action-setup-python-with-deps@v1.0.1
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
dependencies: .[dev-mkdocs]
@@ -239,7 +351,7 @@ jobs:
GIT_REF: ${{ github.ref }}
GIT_SHA: ${{ github.sha }}
run: |
- python -Im frequenz.repo.config.cli.version.mike.info
+ python -m frequenz.repo.config.cli.version.mike.info
- name: Fetch the gh-pages branch
if: steps.mike-version.outputs.version
@@ -260,23 +372,13 @@ jobs:
GIT_REF: ${{ github.ref }}
GIT_SHA: ${{ github.sha }}
run: |
- # Collect aliases into an array to avoid accidental (or malicious)
- # shell injection when passing them to mike.
- aliases=()
- if test -n "$ALIASES"; then
- read -r -a aliases <<<"$ALIASES"
- fi
- # mike is installed as a console script, not a runnable module.
- # Run the installed script under isolated mode to avoid importing from
- # the workspace when building docs from checked-out code.
- python -I "$(command -v mike)" \
- deploy --update-aliases --title "$TITLE" "$VERSION" "${aliases[@]}"
+ mike deploy --update-aliases --title "$TITLE" "$VERSION" $ALIASES
- name: Sort site versions
if: steps.mike-version.outputs.version
run: |
git checkout gh-pages
- python -Im frequenz.repo.config.cli.version.mike.sort versions.json
+ python -m frequenz.repo.config.cli.version.mike.sort versions.json
git commit -a -m "Sort versions.json"
- name: Publish site
@@ -290,14 +392,15 @@ jobs:
# Create a release only on tags creation
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
permissions:
- # Create GitHub releases and upload distribution artifacts.
+ # We need write permissions on contents to create GitHub releases and on
+ # discussions to create the release announcement in the discussion forums
contents: write
- runs-on: ubuntu-slim
+ discussions: write
+ runs-on: ubuntu-24.04
steps:
- name: Download distribution files
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ uses: actions/download-artifact@v6
with:
- name: dist-packages
path: dist
- name: Download RELEASE_NOTES.md
@@ -317,15 +420,15 @@ jobs:
- name: Create GitHub release
run: |
set -ux
- extra_opts=()
- if echo "$REF_NAME" | grep -- -; then extra_opts+=(--prerelease); fi
+ extra_opts=
+ if echo "$REF_NAME" | grep -- -; then extra_opts=" --prerelease"; fi
gh release create \
-R "$REPOSITORY" \
--notes-file RELEASE_NOTES.md \
--generate-notes \
- "${extra_opts[@]}" \
- "$REF_NAME" \
- dist/*
+ $extra_opts \
+ $REF_NAME \
+ dist/wheels-*/*.whl dist/wheels-sdist/*.tar.gz
env:
REF_NAME: ${{ github.ref_name }}
REPOSITORY: ${{ github.repository }}
@@ -341,10 +444,10 @@ jobs:
id-token: write
steps:
- name: Download distribution files
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ uses: actions/download-artifact@v6
with:
- name: dist-packages
path: dist
+ merge-multiple: true
- name: Publish the Python distribution to PyPI
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
+ uses: pypa/gh-action-pypi-publish@release/v1
diff --git a/.gitignore b/.gitignore
index 6997f69..6943cad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -148,3 +148,9 @@ cython_debug/
# Automatically generated documentation
docs/reference/
site/
+
+
+# Added by cargo
+
+/target
+uv.lock
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..59a69a0
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,417 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "bumpalo"
+version = "3.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
+
+[[package]]
+name = "cc"
+version = "1.2.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "chrono"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "frequenz-resampling"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ecb556ae0cab92c99b043fe4c8c3ecf86acb5848da910358a6c1c5a81a9cef"
+dependencies = [
+ "chrono",
+ "itertools",
+ "log",
+ "num-traits",
+]
+
+[[package]]
+name = "frequenz-resampling-python"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "frequenz-resampling",
+ "pyo3",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indoc"
+version = "2.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.185"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "portable-atomic"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "pyo3"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d"
+dependencies = [
+ "chrono",
+ "indoc",
+ "libc",
+ "memoffset",
+ "once_cell",
+ "portable-atomic",
+ "pyo3-build-config",
+ "pyo3-ffi",
+ "pyo3-macros",
+ "unindent",
+]
+
+[[package]]
+name = "pyo3-build-config"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6"
+dependencies = [
+ "target-lexicon",
+]
+
+[[package]]
+name = "pyo3-ffi"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089"
+dependencies = [
+ "libc",
+ "pyo3-build-config",
+]
+
+[[package]]
+name = "pyo3-macros"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02"
+dependencies = [
+ "proc-macro2",
+ "pyo3-macros-backend",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pyo3-macros-backend"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "pyo3-build-config",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.13.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unindent"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..79a49c0
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "frequenz-resampling-python"
+version = "0.1.0"
+edition = "2024"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+chrono = "0.4"
+frequenz-resampling = "0.3.0"
+pyo3 = { version = "0.27", features = [
+ "extension-module",
+ "chrono",
+ "abi3-py311",
+] }
diff --git a/README.md b/README.md
index 8fee338..fc91de6 100644
--- a/README.md
+++ b/README.md
@@ -6,19 +6,99 @@
## Introduction
-A library for resampling a stream of samples to a given interval.
+`frequenz-resampling` provides fast, typed resampling for timestamped numeric
+data in Python.
-TODO(cookiecutter): Improve the README file
+The library is backed by the Rust resampler
+[`frequenz-floss/frequenz-resampling-rs`](https://github.com/frequenz-floss/frequenz-resampling-rs)
+and exposed through a small Python API. It supports incremental stream
+resampling through a `Resampler` object as well as one-shot resampling with the
+`resample()` convenience function.
+
+Main capabilities:
+
+- fixed-interval resampling with timezone-aware `datetime` timestamps
+- configurable interval semantics via `Closed` and `Label`
+- multiple aggregation functions via `ResamplingFunction`:
+ `AVERAGE`, `SUM`, `MAX`, `MIN`, `FIRST`, `LAST`, `COUNT`, `COALESCE`
+- typed Python API (`py.typed`) and generated API docs
## Supported Platforms
The following platforms are officially supported (tested):
-- **Python:** 3.11
-- **Operating System:** Ubuntu Linux 20.04
+- **Python:** 3.11, 3.12, 3.13, 3.14
+- **Operating System:** Ubuntu Linux 24.04, Windows, macOS
- **Architectures:** amd64, arm64
+## Quick Start
+
+### Install
+
+```sh
+python -m pip install frequenz-resampling
+```
+
+### Stream resampling with `Resampler`
+
+```python
+import datetime as dt
+
+from frequenz.resampling import Closed, Label, Resampler, ResamplingFunction
+
+start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+step = dt.timedelta(seconds=1)
+
+resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+)
+
+for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=float(i + 1))
+
+result = resampler.resample(start + 10 * step)
+print(result)
+# [(1970-01-01 00:00:05+00:00, 3.0), (1970-01-01 00:00:10+00:00, 8.0)]
+```
+
+### One-shot resampling with `resample()`
+
+```python
+import datetime as dt
+
+from frequenz.resampling import Closed, Label, ResamplingFunction, resample
+
+start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+step = dt.timedelta(seconds=1)
+data = [(start + i * step, float(i + 1)) for i in range(10)]
+
+result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.SUM,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+)
+print(result)
+```
+
+## Documentation
+
+- User and API documentation:
+
+- Changelog / releases:
+
+
## Contributing
-If you want to know how to build this project and contribute to it, please
-check out the [Contributing Guide](CONTRIBUTING.md).
+Contributions are welcome. For development setup, testing, docs, and release
+workflow, see the [Contributing Guide](CONTRIBUTING.md).
+
+## License
+
+This project is licensed under the [MIT License](LICENSE).
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 257e738..8dbfe5e 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,16 +2,39 @@
## Summary
-
+This release introduces Rust-backed Python bindings for resampling and migrates
+the Python package to the `frequenz.resampling` namespace.
+
+The project now builds via `maturin`, includes a PyO3 extension module, and has
+expanded tests and CI to validate the compiled package workflow.
## Upgrading
-
+- Update imports from `resampling` to `frequenz.resampling`.
+ - Old: `from resampling import Resampler`
+ - New: `from frequenz.resampling import Resampler`
+- The implementation is now provided by a Rust extension module
+ (`frequenz.resampling._rust_backend`) built with `maturin`.
+- If you maintain tooling around docs/linting/test discovery, note that Python
+ source paths now target `frequenz/` and pytest collection targets `tests/`.
## New Features
-
+- Add Rust crate wrapper for `frequenz-resampling` using PyO3.
+- Expose Python API in `frequenz.resampling` with:
+ - `Resampler`
+ - `resample()`
+ - `ResamplingFunction`
+ - `Closed`
+ - `Label`
+- Add type stubs for the extension module (`_rust_backend.pyi`).
+- Add comprehensive Python binding tests.
+- Update CI packaging flow to better support compiled distribution builds across
+ environments.
+- Expand repository docs (`README.md`, `AGENTS.md`) for the new architecture.
## Bug Fixes
-
+- Fix docs and tooling source path assumptions from `src/` to `frequenz/` for
+ mkdocs/mkdocstrings and related generation scripts.
+- Apply formatting and cleanup updates around the new bindings and tests.
diff --git a/docs/_scripts/mkdocstrings_autoapi.py b/docs/_scripts/mkdocstrings_autoapi.py
index 2963a01..90fbf12 100644
--- a/docs/_scripts/mkdocstrings_autoapi.py
+++ b/docs/_scripts/mkdocstrings_autoapi.py
@@ -5,4 +5,4 @@
from frequenz.repo.config.mkdocs import api_pages
-api_pages.generate_python_api_pages("src", "reference")
+api_pages.generate_python_api_pages("frequenz", "reference")
diff --git a/frequenz/resampling/__init__.py b/frequenz/resampling/__init__.py
new file mode 100644
index 0000000..45dd86f
--- /dev/null
+++ b/frequenz/resampling/__init__.py
@@ -0,0 +1,14 @@
+# License: MIT
+# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
+
+"""Frequenz Resampling Python Bindings."""
+
+from ._rust_backend import ( # noqa: F401, F403 # pylint: disable=E0401
+ Closed,
+ Label,
+ Resampler,
+ ResamplingFunction,
+ resample,
+)
+
+__all__ = ["Closed", "Label", "Resampler", "ResamplingFunction", "resample"]
diff --git a/frequenz/resampling/_rust_backend.pyi b/frequenz/resampling/_rust_backend.pyi
new file mode 100644
index 0000000..53a402d
--- /dev/null
+++ b/frequenz/resampling/_rust_backend.pyi
@@ -0,0 +1,176 @@
+# License: MIT
+# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
+
+"""Type stubs for the Rust-backed Python extension module."""
+
+# Stub files mirror the exported Python API and signature-only declarations.
+# pylint: disable=too-many-arguments,unused-argument
+
+__all__ = "Closed", "Label", "Resampler", "ResamplingFunction", "resample"
+
+from datetime import datetime, timedelta
+from enum import Enum, unique
+from typing import Optional, Sequence
+
+@unique
+class ResamplingFunction(Enum):
+ """
+ The ResamplingFunction enum represents the different resampling functions
+ that can be used to resample a time series.
+ """
+
+ AVERAGE = 0
+ """Calculates the average of all samples in the time step (ignoring None values)"""
+ SUM = 1
+ """Calculates the sum of all samples in the time step (ignoring None values)"""
+ MAX = 2
+ """Calculates the maximum of all samples in the time step"""
+ MIN = 3
+ """Calculates the minimum of all samples in the time step"""
+ LAST = 4
+ """Returns the last sample in the time step"""
+ COUNT = 5
+ """Counts the number of samples in the time step"""
+ FIRST = 6
+ """Returns the first sample in the time step"""
+ COALESCE = 7
+ """Returns the first non-None sample in the time step"""
+
+ @staticmethod
+ def values() -> list[int]:
+ """
+ Returns a list of all values of the enum.
+
+ Returns:
+ A list of all values of the enum.
+ """
+
+ @staticmethod
+ def members() -> list[tuple[str, int]]:
+ """
+ Returns a list of all members of the enum.
+
+ Returns:
+ A list of all members of the enum.
+ """
+
+@unique
+class Closed(Enum):
+ """Controls which edge of an interval is closed for sample membership."""
+
+ LEFT = 0
+ """Left-closed, right-open intervals: `[start, end)`"""
+ RIGHT = 1
+ """Left-open, right-closed intervals: `(start, end]`"""
+
+ @staticmethod
+ def values() -> list[int]:
+ """Returns a list of all values of the enum."""
+
+ @staticmethod
+ def members() -> list[tuple[str, int]]:
+ """Returns a list of all members of the enum."""
+
+@unique
+class Label(Enum):
+ """Controls which edge of an interval is used as the output timestamp."""
+
+ LEFT = 0
+ """Use the interval start as the output timestamp"""
+ RIGHT = 1
+ """Use the interval end as the output timestamp"""
+
+ @staticmethod
+ def values() -> list[int]:
+ """Returns a list of all values of the enum."""
+
+ @staticmethod
+ def members() -> list[tuple[str, int]]:
+ """Returns a list of all members of the enum."""
+
+class Resampler:
+ """
+ The Resampler class is used to resample a time series of samples.
+
+ It stores the samples in a buffer and resamples the samples in the buffer when the
+ resample method is called.
+ A resampler can be configured with a resampling function and a resampling interval.
+ """
+
+ def __init__(
+ self,
+ interval: timedelta,
+ resampling_function: ResamplingFunction,
+ *,
+ max_age_in_intervals: int,
+ start: datetime,
+ closed: Closed,
+ label: Label,
+ ):
+ """
+ Initializes a new Resampler object.
+
+ Args:
+ interval: The resampling interval.
+ resampling_function: The resampling function.
+ max_age_in_intervals: The maximum age of a sample in intervals.
+ start: The start time of the resampling.
+ closed: Which interval edge is closed for sample membership. Use
+ `"left"` for left-closed, right-open intervals `[start, end)`
+ and `"right"` for right-closed, left-open intervals
+ `(start, end]`.
+ label: Which interval edge to use for output timestamps. Use
+ `"left"` for the left bin edge and `"right"` for the right
+ bin edge.
+ """
+
+ def push_sample(self, *, timestamp: datetime, value: Optional[float]) -> None:
+ """
+ Pushes a new sample into the resampler buffer.
+
+ Args:
+ timestamp: The timestamp of the sample.
+ value: The value of the sample.
+ """
+
+ def resample(
+ self, end: datetime | None = None
+ ) -> list[tuple[datetime, Optional[float]]]:
+ """
+ Resamples the samples in the buffer until the given end time.
+
+ Args:
+ end: The end time of the resampling. If `None` the samples in the buffer will be
+ resampled until the current date/time.
+
+ Returns:
+ A list of tuples with the resampled samples.
+ """
+
+def resample(
+ data: Sequence[tuple[datetime, Optional[float]]],
+ interval: timedelta,
+ method: ResamplingFunction,
+ *,
+ closed: Closed,
+ label: Label,
+) -> list[tuple[datetime, Optional[float]]]:
+ """
+ Resamples a list of timestamp/value pairs in a single call.
+
+ This is a convenience function for one-shot resampling without needing to
+ manage a `Resampler` instance.
+
+ Args:
+ data: A list of (timestamp, value) tuples to resample. Must be sorted by timestamp.
+ interval: The resampling interval.
+ method: The resampling function to use for aggregating values within each interval.
+ closed: Which interval edge is closed for sample membership. Use
+ `"left"` for left-closed, right-open intervals `[start, end)`
+ and `"right"` for right-closed, left-open intervals `(start, end]`.
+ label: Which interval edge to use for output timestamps. Use `"left"`
+ for the left bin edge and `"right"` for the right bin edge.
+
+ Returns:
+ A list of (timestamp, value) tuples representing the resampled data.
+ """
diff --git a/resampling/conftest.py b/frequenz/resampling/conftest.py
similarity index 100%
rename from resampling/conftest.py
rename to frequenz/resampling/conftest.py
diff --git a/resampling/py.typed b/frequenz/resampling/py.typed
similarity index 100%
rename from resampling/py.typed
rename to frequenz/resampling/py.typed
diff --git a/mkdocs.yml b/mkdocs.yml
index 0794203..75537bb 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -98,8 +98,8 @@ plugins:
- mkdocstrings:
default_handler: python
handlers:
- paths: ["src"]
python:
+ paths: ["frequenz"]
options:
docstring_section_style: spacy
inherited_members: true
@@ -125,6 +125,6 @@ plugins:
# Preview controls
watch:
- - "src"
+ - "frequenz"
- README.md
- CONTRIBUTING.md
diff --git a/pyproject.toml b/pyproject.toml
index 69f1219..9b0ef28 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,11 +3,10 @@
[build-system]
requires = [
- "setuptools == 82.0.1",
- "setuptools_scm[toml] == 10.0.5",
+ "maturin>=1.7,<2.0",
"frequenz-repo-config[lib] == 0.17.0",
]
-build-backend = "setuptools.build_meta"
+build-backend = "maturin"
[project]
name = "frequenz-resampling"
@@ -34,6 +33,11 @@ dynamic = ["version"]
name = "Frequenz Energy-as-a-Service GmbH"
email = "floss@frequenz.com"
+[tool.maturin]
+module-name = "frequenz.resampling._rust_backend"
+profile = "release"
+bindings = "pyo3"
+
[project.optional-dependencies]
dev-flake8 = [
"flake8 == 7.3.0",
@@ -74,6 +78,8 @@ dev-pylint = [
dev-pytest = [
"pytest == 9.0.3",
"pylint == 4.0.5", # We need this to check for the examples
+ "pandas == 2.3.3",
+ "pandas-stubs == 2.3.2.250926",
"frequenz-repo-config[extra-lint-examples] == 0.17.0",
"pytest-mock == 3.15.1",
"pytest-asyncio == 1.3.0",
@@ -98,7 +104,7 @@ include = '\.pyi?$'
[tool.isort]
profile = "black"
line_length = 88
-src_paths = ["benchmarks", "examples", "src", "tests"]
+src_paths = ["benchmarks", "examples", "frequenz", "tests"]
[tool.flake8]
# We give some flexibility to go over 88, there are cases like long URLs or
@@ -159,7 +165,7 @@ filterwarnings = [
# characters as this is a regex.
'ignore:Protobuf gencode version .*exactly one major version older.*:UserWarning',
]
-testpaths = ["tests", "src"]
+testpaths = ["tests"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
required_plugins = ["pytest-asyncio", "pytest-mock"]
diff --git a/resampling/__init__.py b/resampling/__init__.py
deleted file mode 100644
index 18ed845..0000000
--- a/resampling/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# License: MIT
-# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
-
-"""A library for resampling a stream of samples to a given interval."""
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..520ccb3
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,460 @@
+// License: MIT
+// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
+
+//! This module defines the Python bindings for the `resampling` library.
+
+use chrono::{DateTime, TimeDelta, Utc};
+use frequenz_resampling::{Closed, Label, Resampler, ResamplingFunction, Sample, SimpleSample};
+use pyo3::{exceptions::PyValueError, prelude::*};
+use std::fmt::Display;
+
+#[derive(Default, Clone, Debug, Copy)]
+struct PythonSample {
+ timestamp: DateTime,
+ value: Option,
+}
+
+impl PythonSample {
+ fn to_tuple(self) -> (DateTime, Option) {
+ (self.timestamp, self.value)
+ }
+}
+
+impl Sample for PythonSample {
+ type Value = f32;
+
+ fn new(timestamp: DateTime, value: Option) -> Self {
+ Self { timestamp, value }
+ }
+
+ fn timestamp(&self) -> DateTime {
+ self.timestamp
+ }
+
+ fn value(&self) -> Option {
+ self.value
+ }
+}
+
+#[pyclass(eq, eq_int, name = "ResamplingFunction")]
+#[derive(Clone, Debug, Copy, PartialEq)]
+enum ResamplingFunctionF32 {
+ #[pyo3(name = "AVERAGE")]
+ Average,
+ #[pyo3(name = "SUM")]
+ Sum,
+ #[pyo3(name = "MAX")]
+ Max,
+ #[pyo3(name = "MIN")]
+ Min,
+ #[pyo3(name = "LAST")]
+ Last,
+ #[pyo3(name = "COUNT")]
+ Count,
+ #[pyo3(name = "FIRST")]
+ First,
+ #[pyo3(name = "COALESCE")]
+ Coalesce,
+}
+
+#[pymethods]
+impl ResamplingFunctionF32 {
+ #[new]
+ fn new(value: i32) -> PyResult {
+ value.try_into()
+ }
+
+ #[staticmethod]
+ fn values() -> Vec {
+ vec![
+ Self::Average.value(),
+ Self::Sum.value(),
+ Self::Max.value(),
+ Self::Min.value(),
+ Self::Last.value(),
+ Self::Count.value(),
+ Self::First.value(),
+ Self::Coalesce.value(),
+ ]
+ }
+
+ #[staticmethod]
+ fn members() -> Vec<(String, i32)> {
+ vec![
+ (Self::Average.to_string(), Self::Average.value()),
+ (Self::Sum.to_string(), Self::Sum.value()),
+ (Self::Max.to_string(), Self::Max.value()),
+ (Self::Min.to_string(), Self::Min.value()),
+ (Self::Last.to_string(), Self::Last.value()),
+ (Self::Count.to_string(), Self::Count.value()),
+ (Self::First.to_string(), Self::First.value()),
+ (Self::Coalesce.to_string(), Self::Coalesce.value()),
+ ]
+ }
+
+ fn __str__(&self) -> String {
+ format!("{}.{}", "ResamplingFunction", self)
+ }
+
+ fn __repr__(&self) -> String {
+ format!("<{}: {}>", self.__str__(), self.value())
+ }
+
+ #[getter]
+ fn name(&self) -> String {
+ self.to_string()
+ }
+
+ #[getter]
+ fn value(&self) -> i32 {
+ match self {
+ Self::Average => 0,
+ Self::Sum => 1,
+ Self::Max => 2,
+ Self::Min => 3,
+ Self::Last => 4,
+ Self::Count => 5,
+ Self::First => 6,
+ Self::Coalesce => 7,
+ }
+ }
+}
+
+impl TryFrom for ResamplingFunctionF32 {
+ type Error = PyErr;
+
+ fn try_from(value: i32) -> Result {
+ match value {
+ 0 => Ok(Self::Average),
+ 1 => Ok(Self::Sum),
+ 2 => Ok(Self::Max),
+ 3 => Ok(Self::Min),
+ 4 => Ok(Self::Last),
+ 5 => Ok(Self::Count),
+ 6 => Ok(Self::First),
+ 7 => Ok(Self::Coalesce),
+ _ => Err(PyValueError::new_err("Invalid resampling function")),
+ }
+ }
+}
+
+impl Display for ResamplingFunctionF32 {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ ResamplingFunctionF32::Average => "AVERAGE",
+ ResamplingFunctionF32::Sum => "SUM",
+ ResamplingFunctionF32::Max => "MAX",
+ ResamplingFunctionF32::Min => "MIN",
+ ResamplingFunctionF32::First => "FIRST",
+ ResamplingFunctionF32::Last => "LAST",
+ ResamplingFunctionF32::Coalesce => "COALESCE",
+ ResamplingFunctionF32::Count => "COUNT",
+ }
+ )
+ }
+}
+
+impl From for ResamplingFunction {
+ fn from(resampling_function: ResamplingFunctionF32) -> Self {
+ match resampling_function {
+ ResamplingFunctionF32::Average => ResamplingFunction::Average,
+ ResamplingFunctionF32::Sum => ResamplingFunction::Sum,
+ ResamplingFunctionF32::Max => ResamplingFunction::Max,
+ ResamplingFunctionF32::Min => ResamplingFunction::Min,
+ ResamplingFunctionF32::First => ResamplingFunction::First,
+ ResamplingFunctionF32::Last => ResamplingFunction::Last,
+ ResamplingFunctionF32::Coalesce => ResamplingFunction::Coalesce,
+ ResamplingFunctionF32::Count => ResamplingFunction::Count,
+ }
+ }
+}
+
+impl From for ResamplingFunction {
+ fn from(resampling_function: ResamplingFunctionF32) -> Self {
+ match resampling_function {
+ ResamplingFunctionF32::Average => ResamplingFunction::Average,
+ ResamplingFunctionF32::Sum => ResamplingFunction::Sum,
+ ResamplingFunctionF32::Max => ResamplingFunction::Max,
+ ResamplingFunctionF32::Min => ResamplingFunction::Min,
+ ResamplingFunctionF32::First => ResamplingFunction::First,
+ ResamplingFunctionF32::Last => ResamplingFunction::Last,
+ ResamplingFunctionF32::Coalesce => ResamplingFunction::Coalesce,
+ ResamplingFunctionF32::Count => ResamplingFunction::Count,
+ }
+ }
+}
+
+#[pyclass(eq, eq_int, name = "Closed")]
+#[derive(Clone, Debug, Copy, PartialEq)]
+enum ClosedPy {
+ #[pyo3(name = "LEFT")]
+ Left,
+ #[pyo3(name = "RIGHT")]
+ Right,
+}
+
+#[pymethods]
+impl ClosedPy {
+ #[new]
+ fn new(value: i32) -> PyResult {
+ value.try_into()
+ }
+
+ #[staticmethod]
+ fn values() -> Vec {
+ vec![Self::Left.value(), Self::Right.value()]
+ }
+
+ #[staticmethod]
+ fn members() -> Vec<(String, i32)> {
+ vec![
+ (Self::Left.to_string(), Self::Left.value()),
+ (Self::Right.to_string(), Self::Right.value()),
+ ]
+ }
+
+ fn __str__(&self) -> String {
+ format!("{}.{}", "Closed", self)
+ }
+
+ fn __repr__(&self) -> String {
+ format!("<{}: {}>", self.__str__(), self.value())
+ }
+
+ #[getter]
+ fn name(&self) -> String {
+ self.to_string()
+ }
+
+ #[getter]
+ fn value(&self) -> i32 {
+ match self {
+ Self::Left => 0,
+ Self::Right => 1,
+ }
+ }
+}
+
+impl TryFrom for ClosedPy {
+ type Error = PyErr;
+
+ fn try_from(value: i32) -> Result {
+ match value {
+ 0 => Ok(Self::Left),
+ 1 => Ok(Self::Right),
+ _ => Err(PyValueError::new_err("Invalid closed value")),
+ }
+ }
+}
+
+impl Display for ClosedPy {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ ClosedPy::Left => "LEFT",
+ ClosedPy::Right => "RIGHT",
+ }
+ )
+ }
+}
+
+impl From for Closed {
+ fn from(closed: ClosedPy) -> Self {
+ match closed {
+ ClosedPy::Left => Closed::Left,
+ ClosedPy::Right => Closed::Right,
+ }
+ }
+}
+
+#[pyclass(eq, eq_int, name = "Label")]
+#[derive(Clone, Debug, Copy, PartialEq)]
+enum LabelPy {
+ #[pyo3(name = "LEFT")]
+ Left,
+ #[pyo3(name = "RIGHT")]
+ Right,
+}
+
+#[pymethods]
+impl LabelPy {
+ #[new]
+ fn new(value: i32) -> PyResult {
+ value.try_into()
+ }
+
+ #[staticmethod]
+ fn values() -> Vec {
+ vec![Self::Left.value(), Self::Right.value()]
+ }
+
+ #[staticmethod]
+ fn members() -> Vec<(String, i32)> {
+ vec![
+ (Self::Left.to_string(), Self::Left.value()),
+ (Self::Right.to_string(), Self::Right.value()),
+ ]
+ }
+
+ fn __str__(&self) -> String {
+ format!("{}.{}", "Label", self)
+ }
+
+ fn __repr__(&self) -> String {
+ format!("<{}: {}>", self.__str__(), self.value())
+ }
+
+ #[getter]
+ fn name(&self) -> String {
+ self.to_string()
+ }
+
+ #[getter]
+ fn value(&self) -> i32 {
+ match self {
+ Self::Left => 0,
+ Self::Right => 1,
+ }
+ }
+}
+
+impl TryFrom for LabelPy {
+ type Error = PyErr;
+
+ fn try_from(value: i32) -> Result {
+ match value {
+ 0 => Ok(Self::Left),
+ 1 => Ok(Self::Right),
+ _ => Err(PyValueError::new_err("Invalid label")),
+ }
+ }
+}
+
+impl Display for LabelPy {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ LabelPy::Left => "LEFT",
+ LabelPy::Right => "RIGHT",
+ }
+ )
+ }
+}
+
+impl From for Label {
+ fn from(label: LabelPy) -> Self {
+ match label {
+ LabelPy::Left => Label::Left,
+ LabelPy::Right => Label::Right,
+ }
+ }
+}
+
+/// Resamples a list of timestamp/value pairs in a single call.
+///
+/// This is a convenience function for one-shot resampling without needing to
+/// manage a `Resampler` instance.
+///
+/// Args:
+/// data: A list of (timestamp, value) tuples to resample. Must be sorted by timestamp.
+/// interval: The resampling interval.
+/// method: The resampling function to use for aggregating values within each interval.
+/// closed: Which interval edge is closed for sample membership.
+/// label: Which interval edge to use for output timestamps.
+///
+/// Returns:
+/// A list of (timestamp, value) tuples representing the resampled data.
+#[pyfunction]
+#[pyo3(signature = (data, interval, method, *, closed, label))]
+fn resample(
+ data: Vec<(DateTime, Option)>,
+ interval: TimeDelta,
+ method: ResamplingFunctionF32,
+ closed: ClosedPy,
+ label: LabelPy,
+) -> PyResult, Option)>> {
+ // Convert f32 to f64 for the Rust implementation
+ let data_f64: Vec<(DateTime, Option)> = data
+ .into_iter()
+ .map(|(ts, val)| (ts, val.map(|v| v as f64)))
+ .collect();
+
+ // Call the Rust implementation
+ let result = frequenz_resampling::resample(
+ &data_f64,
+ interval,
+ method.into(),
+ closed.into(),
+ label.into(),
+ );
+
+ // Convert f64 back to f32
+ Ok(result
+ .into_iter()
+ .map(|(ts, val)| (ts, val.map(|v| v as f32)))
+ .collect())
+}
+
+/// The Resampler class for f32 values.
+#[pyclass(name = "Resampler")]
+struct ResamplerF32 {
+ inner: Resampler,
+}
+
+#[pymethods]
+impl ResamplerF32 {
+ #[new]
+ #[pyo3(signature = (interval, resampling_function, *, max_age_in_intervals, start, closed, label))]
+ fn new(
+ interval: TimeDelta,
+ resampling_function: ResamplingFunctionF32,
+ max_age_in_intervals: i32,
+ start: DateTime,
+ closed: ClosedPy,
+ label: LabelPy,
+ ) -> PyResult {
+ Ok(Self {
+ inner: Resampler::new(
+ interval,
+ resampling_function.into(),
+ max_age_in_intervals,
+ start,
+ closed.into(),
+ label.into(),
+ ),
+ })
+ }
+
+ #[pyo3(signature = (*, timestamp, value))]
+ fn push_sample(&mut self, timestamp: DateTime, value: Option) {
+ self.inner.push(PythonSample::new(timestamp, value));
+ }
+
+ #[pyo3(signature = (end=None))]
+ fn resample(&mut self, end: Option>) -> Vec<(DateTime, Option)> {
+ match end {
+ Some(end) => self.inner.resample(end),
+ None => self.inner.resample_now(),
+ }
+ .into_iter()
+ .map(PythonSample::to_tuple)
+ .collect()
+ }
+}
+
+#[pymodule]
+fn _rust_backend(m: &Bound<'_, PyModule>) -> PyResult<()> {
+ m.add_class::()?;
+ m.add_class::()?;
+ m.add_class::()?;
+ m.add_class::()?;
+ m.add_function(wrap_pyfunction!(resample, m)?)?;
+ Ok(())
+}
diff --git a/tests/test_frequenz_resampling.py b/tests/test_frequenz_resampling.py
index eab17d9..9977d76 100644
--- a/tests/test_frequenz_resampling.py
+++ b/tests/test_frequenz_resampling.py
@@ -1,6 +1,699 @@
# License: MIT
# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
-"""Tests for the frequenz.resampling package."""
+"""Tests to verify that the resampler can be used successfully from Python."""
+import datetime as dt
+from typing import Literal
+
+import pandas as pd
import pytest
+
+from frequenz.resampling import Closed, Label, Resampler, ResamplingFunction, resample
+
+
+def test_resampler_resampling_function_average() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → avg = 3.0
+ # Interval [5, 10): t=5,6,7,8,9 with values 6,7,8,9,10 → avg = 8.0
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 3.0),
+ (start + 10 * step, 8.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_resampling_function_sum() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.SUM,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → sum = 15.0
+ # Interval [5, 10): t=5,6,7,8,9 with values 6,7,8,9,10 → sum = 40.0
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 15.0),
+ (start + 10 * step, 40.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_resampling_function_max() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.MAX,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → max = 5.0
+ # Interval [5, 10): t=5,6,7,8,9 with values 6,7,8,9,10 → max = 10.0
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 5.0),
+ (start + 10 * step, 10.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_resampling_function_min() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.MIN,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → min = 1.0
+ # Interval [5, 10): t=5,6,7,8,9 with values 6,7,8,9,10 → min = 6.0
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 1.0),
+ (start + 10 * step, 6.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_resampling_function_first() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.FIRST,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → first = 1.0
+ # Interval [5, 10): t=5,6,7,8,9 with values 6,7,8,9,10 → first = 6.0
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 1.0),
+ (start + 10 * step, 6.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_resampling_function_last() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.LAST,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → last = 5.0
+ # Interval [5, 10): t=5,6,7,8,9 with values 6,7,8,9,10 → last = 10.0
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 5.0),
+ (start + 10 * step, 10.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_resampling_function_coalesce() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.COALESCE,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10, but t=5 is None
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → coalesce = 1.0
+ # Interval [5, 10): t=5,6,7,8,9 with values None,7,8,9,10 → coalesce = 7.0
+ for i in range(10):
+ if i == 5:
+ resampler.push_sample(timestamp=start + i * step, value=None)
+ else:
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 1.0),
+ (start + 10 * step, 7.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_resampling_function_count() -> None:
+ """Test the resampler."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.COUNT,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 → count = 5.0
+ # Interval [5, 10): t=5,6,7,8,9 → count = 5.0
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 5 * step, 5.0),
+ (start + 10 * step, 5.0),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_resampling_none() -> None:
+ """Test resampling with None values."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # All values are None
+ for i in range(10):
+ resampler.push_sample(timestamp=start + i * step, value=None)
+
+ expected = [
+ (start + 5 * step, None),
+ (start + 10 * step, None),
+ ]
+
+ resampled = resampler.resample(start + 10 * step)
+
+ assert resampled == expected
+
+
+def test_enum_values() -> None:
+ """Test the ResamplingFunction enum."""
+ assert ResamplingFunction.values() == [0, 1, 2, 3, 4, 5, 6, 7]
+
+
+def test_enum_members() -> None:
+ """Test the ResamplingFunction enum."""
+ assert ResamplingFunction.members() == [
+ ("AVERAGE", 0),
+ ("SUM", 1),
+ ("MAX", 2),
+ ("MIN", 3),
+ ("LAST", 4),
+ ("COUNT", 5),
+ ("FIRST", 6),
+ ("COALESCE", 7),
+ ]
+
+
+def test_enum_str_repr() -> None:
+ """Test the ResamplingFunction enum."""
+ assert str(ResamplingFunction.AVERAGE) == "ResamplingFunction.AVERAGE"
+ assert repr(ResamplingFunction.AVERAGE) == ""
+ assert str(ResamplingFunction.SUM) == "ResamplingFunction.SUM"
+ assert repr(ResamplingFunction.SUM) == ""
+ assert str(ResamplingFunction.MAX) == "ResamplingFunction.MAX"
+ assert repr(ResamplingFunction.MAX) == ""
+ assert str(ResamplingFunction.MIN) == "ResamplingFunction.MIN"
+ assert repr(ResamplingFunction.MIN) == ""
+ assert str(ResamplingFunction.LAST) == "ResamplingFunction.LAST"
+ assert repr(ResamplingFunction.LAST) == ""
+ assert str(ResamplingFunction.COUNT) == "ResamplingFunction.COUNT"
+ assert repr(ResamplingFunction.COUNT) == ""
+ assert str(ResamplingFunction.FIRST) == "ResamplingFunction.FIRST"
+ assert repr(ResamplingFunction.FIRST) == ""
+ assert str(ResamplingFunction.COALESCE) == "ResamplingFunction.COALESCE"
+ assert repr(ResamplingFunction.COALESCE) == ""
+
+
+def test_resampling_function_name_value() -> None:
+ """Test the ResamplingFunction name and value interface."""
+ assert ResamplingFunction.AVERAGE.name == "AVERAGE"
+ assert ResamplingFunction.AVERAGE.value == 0
+ assert ResamplingFunction.SUM.name == "SUM"
+ assert ResamplingFunction.SUM.value == 1
+ assert ResamplingFunction.MAX.name == "MAX"
+ assert ResamplingFunction.MAX.value == 2
+ assert ResamplingFunction.MIN.name == "MIN"
+ assert ResamplingFunction.MIN.value == 3
+ assert ResamplingFunction.LAST.name == "LAST"
+ assert ResamplingFunction.LAST.value == 4
+ assert ResamplingFunction.COUNT.name == "COUNT"
+ assert ResamplingFunction.COUNT.value == 5
+ assert ResamplingFunction.FIRST.name == "FIRST"
+ assert ResamplingFunction.FIRST.value == 6
+ assert ResamplingFunction.COALESCE.name == "COALESCE"
+ assert ResamplingFunction.COALESCE.value == 7
+
+
+def test_resampling_function_init() -> None:
+ """Test the ResamplingFunction init."""
+ assert ResamplingFunction(0) == ResamplingFunction.AVERAGE
+ assert ResamplingFunction(1) == ResamplingFunction.SUM
+ assert ResamplingFunction(2) == ResamplingFunction.MAX
+ assert ResamplingFunction(3) == ResamplingFunction.MIN
+ assert ResamplingFunction(4) == ResamplingFunction.LAST
+ assert ResamplingFunction(5) == ResamplingFunction.COUNT
+ assert ResamplingFunction(6) == ResamplingFunction.FIRST
+ assert ResamplingFunction(7) == ResamplingFunction.COALESCE
+
+
+def test_resampler_label_left() -> None:
+ """Test the resampler with the left interval label."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=0.5)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ for i in range(0, 20):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 0 * step, 5.5),
+ (start + 10 * step, 15.5),
+ ]
+
+ resampled = resampler.resample(start + 20 * step)
+
+ assert resampled == expected
+
+
+def test_resampler_last_timestamp() -> None:
+ """Test the resampler with the right interval label."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=0.5)
+ resampler = Resampler(
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ max_age_in_intervals=1,
+ start=start,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ # Data starts at t=0, step=0.5s, 20 samples
+ # Interval [0, 5): t=0,0.5,1,1.5,2,2.5,3,3.5,4,4.5 → values 1-10 → avg = 5.5
+ # Interval [5, 10): t=5,5.5,6,6.5,7,7.5,8,8.5,9,9.5 → values 11-20 → avg = 15.5
+ for i in range(20):
+ resampler.push_sample(timestamp=start + i * step, value=i + 1)
+
+ expected = [
+ (start + 10 * step, 5.5),
+ (start + 20 * step, 15.5),
+ ]
+
+ resampled = resampler.resample(start + 20 * step)
+
+ assert resampled == expected
+
+
+# Tests for the one-shot resample function
+
+
+def test_resample_function_basic() -> None:
+ """Test the resample function with basic usage."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+
+ # Data: t=0,1,2,3,4,5,6,7,8,9 with values 1-10
+ # Interval [0, 5): t=0,1,2,3,4 with values 1,2,3,4,5 → avg = 3.0
+ # Interval [5, 10): t=5,6,7,8,9 with values 6,7,8,9,10 → avg = 8.0
+ data = [(start + i * step, float(i + 1)) for i in range(10)]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ assert len(result) == 2
+ assert result[0] == (start, 3.0)
+ assert result[1] == (start + 5 * step, 8.0)
+
+
+def test_resample_function_label_right() -> None:
+ """Test the resample function with label='right'."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+
+ data = [(start + i * step, float(i + 1)) for i in range(10)]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ closed=Closed.LEFT,
+ label=Label.RIGHT,
+ )
+
+ assert len(result) == 2
+ # With label='right', timestamps are at end of interval
+ assert result[0] == (start + 5 * step, 3.0)
+ assert result[1] == (start + 10 * step, 8.0)
+
+
+def test_resample_function_closed_right() -> None:
+ """Test the resample function with right-closed intervals."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ data = [
+ (start, 10.0),
+ (start + 5 * step, 20.0),
+ ]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.SUM,
+ closed=Closed.RIGHT,
+ label=Label.RIGHT,
+ )
+
+ assert result == [
+ (start, 10.0),
+ (start + 5 * step, 20.0),
+ ]
+
+
+@pytest.mark.parametrize(
+ ("closed", "label", "pandas_closed", "pandas_label"),
+ [
+ (Closed.LEFT, Label.LEFT, "left", "left"),
+ (Closed.LEFT, Label.RIGHT, "left", "right"),
+ (Closed.RIGHT, Label.LEFT, "right", "left"),
+ (Closed.RIGHT, Label.RIGHT, "right", "right"),
+ ],
+)
+def test_resample_function_matches_pandas(
+ closed: Closed,
+ label: Label,
+ pandas_closed: Literal["left", "right"],
+ pandas_label: Literal["left", "right"],
+) -> None:
+ """Test the one-shot API against pandas with matching closed/label settings."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+ data = [(start + i * step, float(i + 1)) for i in range(10)]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ closed=closed,
+ label=label,
+ )
+
+ pandas_series = pd.Series(
+ [value for _, value in data],
+ index=pd.DatetimeIndex([timestamp for timestamp, _ in data]),
+ dtype="float64",
+ )
+ pandas_result = pandas_series.resample(
+ "5s", closed=pandas_closed, label=pandas_label
+ ).mean()
+ expected = [
+ (timestamp.to_pydatetime(), float(value))
+ for timestamp, value in zip(
+ pandas_result.index, pandas_result.to_list(), strict=True
+ )
+ ]
+
+ assert result == expected
+
+
+def test_label_init_invalid_value() -> None:
+ """Test the Label enum rejects invalid values."""
+ with pytest.raises(ValueError, match="Invalid label"):
+ Label(99)
+
+
+def test_closed_init_invalid_value() -> None:
+ """Test the Closed enum rejects invalid values."""
+ with pytest.raises(ValueError, match="Invalid closed"):
+ Closed(99)
+
+
+def test_label_values_members() -> None:
+ """Test the Label enum helpers."""
+ assert Label.values() == [0, 1]
+ assert Label.members() == [("LEFT", 0), ("RIGHT", 1)]
+
+
+def test_closed_values_members() -> None:
+ """Test the Closed enum helpers."""
+ assert Closed.values() == [0, 1]
+ assert Closed.members() == [("LEFT", 0), ("RIGHT", 1)]
+
+
+def test_resample_function_empty_data() -> None:
+ """Test the resample function with empty data."""
+ data: list[tuple[dt.datetime, float | None]] = []
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ assert result == []
+
+
+def test_resample_function_with_none_values() -> None:
+ """Test the resample function with None values."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+
+ # First value in each interval is None
+ data: list[tuple[dt.datetime, float | None]] = [
+ (start + i * step, None if i in (0, 5) else float(i + 1)) for i in range(10)
+ ]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ assert len(result) == 2
+ # Interval [0, 5): values 2,3,4,5 → avg = 3.5
+ # Interval [5, 10): values 7,8,9,10 → avg = 8.5
+ assert result[0] == (start, 3.5)
+ assert result[1] == (start + 5 * step, 8.5)
+
+
+def test_resample_function_interval_with_only_none() -> None:
+ """Test a bucket that contains only None values."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+
+ data: list[tuple[dt.datetime, float | None]] = [
+ (start + i * step, None if i < 5 else float(i + 1)) for i in range(10)
+ ]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ assert len(result) == 2
+ assert result[0] == (start, None)
+ assert result[1] == (start + 5 * step, 8.0)
+
+
+def test_resample_function_sum() -> None:
+ """Test the resample function with Sum method."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+
+ data = [(start + i * step, float(i + 1)) for i in range(10)]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.SUM,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ assert len(result) == 2
+ # Interval [0, 5): sum(1,2,3,4,5) = 15.0
+ # Interval [5, 10): sum(6,7,8,9,10) = 40.0
+ assert result[0] == (start, 15.0)
+ assert result[1] == (start + 5 * step, 40.0)
+
+
+def test_resample_function_min_max() -> None:
+ """Test the resample function with Min and Max methods."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+
+ data = [(start + i * step, float(i + 1)) for i in range(10)]
+
+ min_result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.MIN,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+ max_result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.MAX,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ assert min_result[0] == (start, 1.0)
+ assert min_result[1] == (start + 5 * step, 6.0)
+ assert max_result[0] == (start, 5.0)
+ assert max_result[1] == (start + 5 * step, 10.0)
+
+
+def test_resample_function_single_sample() -> None:
+ """Test the resample function with a single sample."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+
+ data = [(start, 42.0)]
+
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ ResamplingFunction.AVERAGE,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+
+ assert len(result) == 1
+ assert result[0] == (start, 42.0)
+
+
+def test_resample_function_all_methods() -> None:
+ """Test the resample function with all resampling methods."""
+ start = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
+ step = dt.timedelta(seconds=1)
+
+ data = [(start + i * step, float(i + 1)) for i in range(5)]
+
+ # Test all methods work without errors
+ for method in [
+ ResamplingFunction.AVERAGE,
+ ResamplingFunction.SUM,
+ ResamplingFunction.MIN,
+ ResamplingFunction.MAX,
+ ResamplingFunction.FIRST,
+ ResamplingFunction.LAST,
+ ResamplingFunction.COUNT,
+ ResamplingFunction.COALESCE,
+ ]:
+ result = resample(
+ data,
+ dt.timedelta(seconds=5),
+ method,
+ closed=Closed.LEFT,
+ label=Label.LEFT,
+ )
+ assert len(result) == 1
+ assert result[0][0] == start
diff --git a/uv.lock b/uv.lock
deleted file mode 100644
index 499cd9e..0000000
--- a/uv.lock
+++ /dev/null
@@ -1,1387 +0,0 @@
-version = 1
-requires-python = ">=3.11, <4"
-resolution-markers = [
- "python_full_version < '3.12'",
- "python_full_version >= '3.12'",
-]
-
-[[package]]
-name = "argcomplete"
-version = "3.6.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846 },
-]
-
-[[package]]
-name = "astroid"
-version = "3.3.11"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/18/74/dfb75f9ccd592bbedb175d4a32fc643cf569d7c218508bfbd6ea7ef9c091/astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce", size = 400439 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/af/0f/3b8fdc946b4d9cc8cc1e8af42c4e409468c84441b933d037e101b3d72d86/astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec", size = 275612 },
-]
-
-[[package]]
-name = "async-solipsism"
-version = "0.8"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/68/39/4323ae4e8506cda09a3eadf19e7bc37880d1034754e5b897c6ef7d0b5e89/async_solipsism-0.8.tar.gz", hash = "sha256:c724c359ea2173868ef725f55df95b264647307f05b29bf9cb3a3b940dd6ea5b", size = 30165 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d3/fb/b6b1e5364e9eb8b80113b121723d7d7004e775226eef7c61810f5448890c/async_solipsism-0.8-py3-none-any.whl", hash = "sha256:e8e31375b2791fcab3e9196c66531116d6e291f6a49d94f599a19cbd7fa78b1f", size = 26092 },
-]
-
-[[package]]
-name = "attrs"
-version = "26.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548 },
-]
-
-[[package]]
-name = "babel"
-version = "2.18.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845 },
-]
-
-[[package]]
-name = "backrefs"
-version = "5.9"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/eb/a7/312f673df6a79003279e1f55619abbe7daebbb87c17c976ddc0345c04c7b/backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59", size = 5765857 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/19/4d/798dc1f30468134906575156c089c492cf79b5a5fd373f07fe26c4d046bf/backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f", size = 380267 },
- { url = "https://files.pythonhosted.org/packages/55/07/f0b3375bf0d06014e9787797e6b7cc02b38ac9ff9726ccfe834d94e9991e/backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf", size = 392072 },
- { url = "https://files.pythonhosted.org/packages/9d/12/4f345407259dd60a0997107758ba3f221cf89a9b5a0f8ed5b961aef97253/backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa", size = 397947 },
- { url = "https://files.pythonhosted.org/packages/10/bf/fa31834dc27a7f05e5290eae47c82690edc3a7b37d58f7fb35a1bdbf355b/backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b", size = 399843 },
- { url = "https://files.pythonhosted.org/packages/fc/24/b29af34b2c9c41645a9f4ff117bae860291780d73880f449e0b5d948c070/backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9", size = 411762 },
- { url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60", size = 380265 },
-]
-
-[[package]]
-name = "black"
-version = "25.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "mypy-extensions" },
- { name = "packaging" },
- { name = "pathspec" },
- { name = "platformdirs" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372 },
- { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865 },
- { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699 },
- { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028 },
- { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 },
- { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 },
- { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 },
- { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 },
- { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 },
- { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 },
- { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 },
- { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 },
- { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 },
-]
-
-[[package]]
-name = "certifi"
-version = "2026.4.22"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707 },
-]
-
-[[package]]
-name = "charset-normalizer"
-version = "3.4.7"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705 },
- { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419 },
- { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901 },
- { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742 },
- { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061 },
- { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239 },
- { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173 },
- { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841 },
- { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304 },
- { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455 },
- { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036 },
- { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739 },
- { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277 },
- { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819 },
- { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281 },
- { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843 },
- { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328 },
- { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061 },
- { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031 },
- { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239 },
- { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589 },
- { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733 },
- { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652 },
- { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229 },
- { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552 },
- { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806 },
- { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316 },
- { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274 },
- { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468 },
- { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460 },
- { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330 },
- { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828 },
- { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627 },
- { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008 },
- { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303 },
- { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282 },
- { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595 },
- { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986 },
- { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711 },
- { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036 },
- { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998 },
- { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056 },
- { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537 },
- { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176 },
- { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723 },
- { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085 },
- { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819 },
- { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915 },
- { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234 },
- { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042 },
- { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706 },
- { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727 },
- { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882 },
- { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860 },
- { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564 },
- { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276 },
- { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238 },
- { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189 },
- { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352 },
- { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024 },
- { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869 },
- { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541 },
- { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634 },
- { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384 },
- { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133 },
- { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257 },
- { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851 },
- { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393 },
- { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251 },
- { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609 },
- { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014 },
- { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979 },
- { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238 },
- { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110 },
- { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824 },
- { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103 },
- { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194 },
- { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827 },
- { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168 },
- { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018 },
- { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958 },
-]
-
-[[package]]
-name = "click"
-version = "8.2.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "platform_system == 'Windows'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215 },
-]
-
-[[package]]
-name = "colorama"
-version = "0.4.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
-]
-
-[[package]]
-name = "colorlog"
-version = "6.10.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743 },
-]
-
-[[package]]
-name = "dependency-groups"
-version = "1.3.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "packaging" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/62/55/f054de99871e7beb81935dea8a10b90cd5ce42122b1c3081d5282fdb3621/dependency_groups-1.3.1.tar.gz", hash = "sha256:78078301090517fd938c19f64a53ce98c32834dfe0dee6b88004a569a6adfefd", size = 10093 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/99/c7/d1ec24fb280caa5a79b6b950db565dab30210a66259d17d5bb2b3a9f878d/dependency_groups-1.3.1-py3-none-any.whl", hash = "sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030", size = 8664 },
-]
-
-[[package]]
-name = "dill"
-version = "0.4.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019 },
-]
-
-[[package]]
-name = "distlib"
-version = "0.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 },
-]
-
-[[package]]
-name = "docstring-parser-fork"
-version = "0.0.14"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/66/bf/27f9cab2f0cd1d17a4420572088bbc19f36d726fbcf165edf226a8926dbc/docstring_parser_fork-0.0.14.tar.gz", hash = "sha256:a2743a63d8d36c09650594f7b4ab5b2758fee8629dcf794d1b221b23179baa5c", size = 34551 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl", hash = "sha256:4c544f234ef2cc2749a3df32b70c437d77888b1099143a1ad5454452c574b9af", size = 43063 },
-]
-
-[[package]]
-name = "filelock"
-version = "3.29.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812 },
-]
-
-[[package]]
-name = "flake8"
-version = "7.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mccabe" },
- { name = "pycodestyle" },
- { name = "pyflakes" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9b/af/fbfe3c4b5a657d79e5c47a2827a362f9e1b763336a52f926126aa6dc7123/flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872", size = 48326 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9f/56/13ab06b4f93ca7cac71078fbe37fcea175d3216f31f85c3168a6bbd0bb9a/flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e", size = 57922 },
-]
-
-[[package]]
-name = "flake8-datetimez"
-version = "20.10.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "flake8" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/04/94/72744acab2d9460c092ad8252a13da8fc64685cfb84f3422c21c378da928/flake8-datetimez-20.10.0.tar.gz", hash = "sha256:78939f3bcbe2b7fe48235998545c869c27cdfac3f45685099a3f7366c1ffebc6", size = 4178 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0f/94/c1e0448d04e84b6a3d49675c2cc301d91549f540dc5b27739e1d1c0ed171/flake8_datetimez-20.10.0-py3-none-any.whl", hash = "sha256:57aa2f55eb88797e2d8c06bd536ff8049b9f1ba877d81dc06ff8d9bdc195c1fc", size = 5701 },
-]
-
-[[package]]
-name = "flake8-docstrings"
-version = "1.7.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "flake8" },
- { name = "pydocstyle" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/93/24/f839e3a06e18f4643ccb81370909a497297909f15106e6af2fecdef46894/flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af", size = 5995 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3f/7d/76a278fa43250441ed9300c344f889c7fb1817080c8fb8996b840bf421c2/flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75", size = 4994 },
-]
-
-[[package]]
-name = "flake8-pyproject"
-version = "1.2.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "flake8" },
-]
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5f/1d/635e86f9f3a96b7ea9e9f19b5efe17a987e765c39ca496e4a893bb999112/flake8_pyproject-1.2.3-py3-none-any.whl", hash = "sha256:6249fe53545205af5e76837644dc80b4c10037e73a0e5db87ff562d75fb5bd4a", size = 4756 },
-]
-
-[[package]]
-name = "frequenz-repo-config"
-version = "0.17.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "github-action-utils" },
- { name = "mkdocs-gen-files" },
- { name = "mkdocstrings-python" },
- { name = "nox" },
- { name = "semver" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/66/05/d9954aafd000dfdb16cf6eed4e1ca8393644f1411f627bae99c7d9449108/frequenz_repo_config-0.17.0.tar.gz", hash = "sha256:31fc06d1fbc1af07b29f7a45c3e5e1b0803bcab5e852f2381faebfafc6404b38", size = 43448 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/ad/2688bda844861c1dfe9d688185a699e45a00503eb3abfc4f8e7bc0cdcf6a/frequenz_repo_config-0.17.0-py3-none-any.whl", hash = "sha256:01b58bfc797612816783d89a1456f0ebf9711d5b0c39887b783755a57bf410e9", size = 44627 },
-]
-
-[package.optional-dependencies]
-extra-lint-examples = [
- { name = "pylint" },
- { name = "pytest" },
- { name = "sybil" },
-]
-
-[[package]]
-name = "frequenz-resampling"
-version = "0.0.post0+d20260422"
-source = { editable = "." }
-dependencies = [
- { name = "typing-extensions" },
-]
-
-[package.optional-dependencies]
-dev = [
- { name = "async-solipsism" },
- { name = "black" },
- { name = "flake8" },
- { name = "flake8-datetimez" },
- { name = "flake8-docstrings" },
- { name = "flake8-pyproject" },
- { name = "frequenz-repo-config", extra = ["extra-lint-examples"] },
- { name = "isort" },
- { name = "markdown" },
- { name = "mike" },
- { name = "mkdocs-gen-files" },
- { name = "mkdocs-literate-nav" },
- { name = "mkdocs-macros-plugin" },
- { name = "mkdocs-material" },
- { name = "mkdocstrings", extra = ["python"] },
- { name = "mkdocstrings-python" },
- { name = "mypy" },
- { name = "nox" },
- { name = "pydoclint" },
- { name = "pydocstyle" },
- { name = "pylint" },
- { name = "pytest" },
- { name = "pytest-asyncio" },
- { name = "pytest-mock" },
- { name = "types-markdown" },
-]
-dev-flake8 = [
- { name = "flake8" },
- { name = "flake8-datetimez" },
- { name = "flake8-docstrings" },
- { name = "flake8-pyproject" },
- { name = "pydoclint" },
- { name = "pydocstyle" },
-]
-dev-formatting = [
- { name = "black" },
- { name = "isort" },
-]
-dev-mkdocs = [
- { name = "black" },
- { name = "frequenz-repo-config" },
- { name = "markdown" },
- { name = "mike" },
- { name = "mkdocs-gen-files" },
- { name = "mkdocs-literate-nav" },
- { name = "mkdocs-macros-plugin" },
- { name = "mkdocs-material" },
- { name = "mkdocstrings", extra = ["python"] },
- { name = "mkdocstrings-python" },
-]
-dev-mypy = [
- { name = "async-solipsism" },
- { name = "black" },
- { name = "frequenz-repo-config", extra = ["extra-lint-examples"] },
- { name = "markdown" },
- { name = "mike" },
- { name = "mkdocs-gen-files" },
- { name = "mkdocs-literate-nav" },
- { name = "mkdocs-macros-plugin" },
- { name = "mkdocs-material" },
- { name = "mkdocstrings", extra = ["python"] },
- { name = "mkdocstrings-python" },
- { name = "mypy" },
- { name = "nox" },
- { name = "pylint" },
- { name = "pytest" },
- { name = "pytest-asyncio" },
- { name = "pytest-mock" },
- { name = "types-markdown" },
-]
-dev-noxfile = [
- { name = "frequenz-repo-config" },
- { name = "nox" },
-]
-dev-pylint = [
- { name = "async-solipsism" },
- { name = "black" },
- { name = "frequenz-repo-config", extra = ["extra-lint-examples"] },
- { name = "markdown" },
- { name = "mike" },
- { name = "mkdocs-gen-files" },
- { name = "mkdocs-literate-nav" },
- { name = "mkdocs-macros-plugin" },
- { name = "mkdocs-material" },
- { name = "mkdocstrings", extra = ["python"] },
- { name = "mkdocstrings-python" },
- { name = "nox" },
- { name = "pylint" },
- { name = "pytest" },
- { name = "pytest-asyncio" },
- { name = "pytest-mock" },
-]
-dev-pytest = [
- { name = "async-solipsism" },
- { name = "frequenz-repo-config", extra = ["extra-lint-examples"] },
- { name = "pylint" },
- { name = "pytest" },
- { name = "pytest-asyncio" },
- { name = "pytest-mock" },
-]
-
-[package.metadata]
-requires-dist = [
- { name = "async-solipsism", marker = "extra == 'dev-pytest'", specifier = "==0.8" },
- { name = "black", marker = "extra == 'dev-formatting'", specifier = "==25.1.0" },
- { name = "black", marker = "extra == 'dev-mkdocs'", specifier = "==25.1.0" },
- { name = "flake8", marker = "extra == 'dev-flake8'", specifier = "==7.3.0" },
- { name = "flake8-datetimez", marker = "extra == 'dev-flake8'", specifier = "==20.10.0" },
- { name = "flake8-docstrings", marker = "extra == 'dev-flake8'", specifier = "==1.7.0" },
- { name = "flake8-pyproject", marker = "extra == 'dev-flake8'", specifier = "==1.2.3" },
- { name = "frequenz-repo-config", extras = ["extra-lint-examples"], marker = "extra == 'dev-pytest'", specifier = "==0.17.0" },
- { name = "frequenz-repo-config", extras = ["lib"], marker = "extra == 'dev-mkdocs'", specifier = "==0.17.0" },
- { name = "frequenz-repo-config", extras = ["lib"], marker = "extra == 'dev-noxfile'", specifier = "==0.17.0" },
- { name = "frequenz-resampling", extras = ["dev-flake8", "dev-formatting", "dev-mkdocs", "dev-mypy", "dev-noxfile", "dev-pylint", "dev-pytest"], marker = "extra == 'dev'" },
- { name = "frequenz-resampling", extras = ["dev-mkdocs", "dev-noxfile", "dev-pytest"], marker = "extra == 'dev-mypy'" },
- { name = "frequenz-resampling", extras = ["dev-mkdocs", "dev-noxfile", "dev-pytest"], marker = "extra == 'dev-pylint'" },
- { name = "isort", marker = "extra == 'dev-formatting'", specifier = "==6.0.0" },
- { name = "markdown", marker = "extra == 'dev-mkdocs'", specifier = "==3.8.2" },
- { name = "mike", marker = "extra == 'dev-mkdocs'", specifier = "==2.1.3" },
- { name = "mkdocs-gen-files", marker = "extra == 'dev-mkdocs'", specifier = "==0.5.0" },
- { name = "mkdocs-literate-nav", marker = "extra == 'dev-mkdocs'", specifier = "==0.6.2" },
- { name = "mkdocs-macros-plugin", marker = "extra == 'dev-mkdocs'", specifier = "==1.3.9" },
- { name = "mkdocs-material", marker = "extra == 'dev-mkdocs'", specifier = "==9.6.18" },
- { name = "mkdocstrings", extras = ["python"], marker = "extra == 'dev-mkdocs'", specifier = "==1.0.0" },
- { name = "mkdocstrings-python", marker = "extra == 'dev-mkdocs'", specifier = "==2.0.1" },
- { name = "mypy", marker = "extra == 'dev-mypy'", specifier = "==1.9.0" },
- { name = "nox", marker = "extra == 'dev-noxfile'", specifier = "==2025.5.1" },
- { name = "pydoclint", marker = "extra == 'dev-flake8'", specifier = "==0.6.10" },
- { name = "pydocstyle", marker = "extra == 'dev-flake8'", specifier = "==6.3.0" },
- { name = "pylint", marker = "extra == 'dev-pytest'", specifier = "==3.3.8" },
- { name = "pytest", marker = "extra == 'dev-pytest'", specifier = "==8.4.1" },
- { name = "pytest-asyncio", marker = "extra == 'dev-pytest'", specifier = "==1.1.0" },
- { name = "pytest-mock", marker = "extra == 'dev-pytest'", specifier = "==3.14.0" },
- { name = "types-markdown", marker = "extra == 'dev-mypy'", specifier = "==3.8.0.20250809" },
- { name = "typing-extensions", specifier = ">=4.14.1,<5" },
-]
-
-[[package]]
-name = "ghp-import"
-version = "2.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "python-dateutil" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 },
-]
-
-[[package]]
-name = "github-action-utils"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/41/81/b70e3be8686c2011ec03eddf5a695496c9935818b18cde6a5f9553127a4e/github-action-utils-1.1.0.tar.gz", hash = "sha256:8aa40d90b89d814004160bb7e90b42cc07b55f41f66e4a4a32766d26c9ca3d61", size = 8860 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/c8/d2a1c4ac43c9194316b8802b08502712ef242e6f325435409688e760b03a/github_action_utils-1.1.0-py2.py3-none-any.whl", hash = "sha256:bc84bac22e8a25ebe86370b08ff2c174960e468e899ffd313cb09d19629acefb", size = 8893 },
-]
-
-[[package]]
-name = "griffe"
-version = "2.0.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "griffecli" },
- { name = "griffelib" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/4a/49/eb6d2935e27883af92c930ed40cc4c69bcd32c402be43b8ca4ab20510f67/griffe-2.0.2.tar.gz", hash = "sha256:c5d56326d159f274492e9bf93a9895cec101155d944caa66d0fc4e0c13751b92", size = 293757 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/94/c0/2bb018eecf9a83c68db9cd9fffd9dab25f102ad30ed869451046e46d1187/griffe-2.0.2-py3-none-any.whl", hash = "sha256:2b31816460aee1996af26050a1fc6927a2e5936486856707f55508e4c9b5960b", size = 5141 },
-]
-
-[[package]]
-name = "griffecli"
-version = "2.0.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama" },
- { name = "griffelib" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/79/e0/6a7d661d71bb043656a109b91d84a42b5342752542074ec83b16a6eb97f0/griffecli-2.0.2.tar.gz", hash = "sha256:40a1ad4181fc39685d025e119ae2c5b669acdc1f19b705fb9bf971f4e6f6dffb", size = 56281 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2e/e8/90d93356c88ac34c20cb5edffca68138df55ca9bbd1a06eccfbcec8fdbe5/griffecli-2.0.2-py3-none-any.whl", hash = "sha256:0d44d39e59afa81e288a3e1c3bf352cc4fa537483326ac06b8bb6a51fd8303a0", size = 9500 },
-]
-
-[[package]]
-name = "griffelib"
-version = "2.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357 },
-]
-
-[[package]]
-name = "hjson"
-version = "3.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/82/e5/0b56d723a76ca67abadbf7fb71609fb0ea7e6926e94fcca6c65a85b36a0e/hjson-3.1.0.tar.gz", hash = "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75", size = 40541 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/7f/13cd798d180af4bf4c0ceddeefba2b864a63c71645abc0308b768d67bb81/hjson-3.1.0-py3-none-any.whl", hash = "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89", size = 54018 },
-]
-
-[[package]]
-name = "idna"
-version = "3.12"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/22/12/2948fbe5513d062169bd91f7d7b1cd97bc8894f32946b71fa39f6e63ca0c/idna-3.12.tar.gz", hash = "sha256:724e9952cc9e2bd7550ea784adb098d837ab5267ef67a1ab9cf7846bdbdd8254", size = 194350 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/53/b2/acc33950394b3becb2b664741a0c0889c7ef9f9ffbfa8d47eddb53a50abd/idna-3.12-py3-none-any.whl", hash = "sha256:60ffaa1858fac94c9c124728c24fcde8160f3fb4a7f79aa8cdd33a9d1af60a67", size = 68634 },
-]
-
-[[package]]
-name = "importlib-metadata"
-version = "9.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "zipp" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789 },
-]
-
-[[package]]
-name = "importlib-resources"
-version = "7.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e4/06/b56dfa750b44e86157093bc8fca0ab81dccbf5260510de4eaf1cb69b5b99/importlib_resources-7.1.0.tar.gz", hash = "sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708", size = 44985 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl", hash = "sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1", size = 37232 },
-]
-
-[[package]]
-name = "iniconfig"
-version = "2.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 },
-]
-
-[[package]]
-name = "isort"
-version = "6.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/1c/28/b382d1656ac0ee4cef4bf579b13f9c6c813bff8a5cb5996669592c8c75fa/isort-6.0.0.tar.gz", hash = "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1", size = 828356 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/76/c7/d6017f09ae5b1206fbe531f7af3b6dac1f67aedcbd2e79f3b386c27955d6/isort-6.0.0-py3-none-any.whl", hash = "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892", size = 94053 },
-]
-
-[[package]]
-name = "jinja2"
-version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 },
-]
-
-[[package]]
-name = "markdown"
-version = "3.8.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827 },
-]
-
-[[package]]
-name = "markupsafe"
-version = "3.0.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 },
- { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 },
- { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 },
- { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 },
- { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 },
- { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 },
- { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 },
- { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 },
- { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 },
- { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 },
- { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 },
- { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 },
- { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 },
- { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 },
- { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 },
- { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 },
- { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 },
- { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 },
- { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 },
- { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 },
- { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 },
- { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 },
- { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 },
- { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 },
- { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 },
- { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 },
- { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 },
- { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 },
- { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 },
- { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 },
- { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 },
- { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 },
- { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 },
- { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 },
- { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 },
- { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 },
- { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 },
- { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 },
- { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 },
- { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 },
- { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 },
- { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 },
- { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 },
- { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 },
- { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 },
- { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 },
- { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 },
- { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 },
- { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 },
- { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 },
- { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 },
- { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 },
- { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 },
- { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 },
- { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 },
- { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 },
- { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 },
- { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 },
- { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 },
- { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 },
- { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 },
- { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 },
- { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 },
- { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 },
- { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 },
- { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 },
-]
-
-[[package]]
-name = "mccabe"
-version = "0.7.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 },
-]
-
-[[package]]
-name = "mergedeep"
-version = "1.3.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 },
-]
-
-[[package]]
-name = "mike"
-version = "2.1.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "importlib-metadata" },
- { name = "importlib-resources" },
- { name = "jinja2" },
- { name = "mkdocs" },
- { name = "pyparsing" },
- { name = "pyyaml" },
- { name = "pyyaml-env-tag" },
- { name = "verspec" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ab/f7/2933f1a1fb0e0f077d5d6a92c6c7f8a54e6128241f116dff4df8b6050bbf/mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810", size = 38119 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a", size = 33754 },
-]
-
-[[package]]
-name = "mkdocs"
-version = "1.6.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "colorama", marker = "platform_system == 'Windows'" },
- { name = "ghp-import" },
- { name = "jinja2" },
- { name = "markdown" },
- { name = "markupsafe" },
- { name = "mergedeep" },
- { name = "mkdocs-get-deps" },
- { name = "packaging" },
- { name = "pathspec" },
- { name = "pyyaml" },
- { name = "pyyaml-env-tag" },
- { name = "watchdog" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 },
-]
-
-[[package]]
-name = "mkdocs-autorefs"
-version = "1.4.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markdown" },
- { name = "markupsafe" },
- { name = "mkdocs" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530 },
-]
-
-[[package]]
-name = "mkdocs-gen-files"
-version = "0.5.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mkdocs" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/48/85/2d634462fd59136197d3126ca431ffb666f412e3db38fd5ce3a60566303e/mkdocs_gen_files-0.5.0.tar.gz", hash = "sha256:4c7cf256b5d67062a788f6b1d035e157fc1a9498c2399be9af5257d4ff4d19bc", size = 7539 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e7/0f/1e55b3fd490ad2cecb6e7b31892d27cb9fc4218ec1dab780440ba8579e74/mkdocs_gen_files-0.5.0-py3-none-any.whl", hash = "sha256:7ac060096f3f40bd19039e7277dd3050be9a453c8ac578645844d4d91d7978ea", size = 8380 },
-]
-
-[[package]]
-name = "mkdocs-get-deps"
-version = "0.2.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mergedeep" },
- { name = "platformdirs" },
- { name = "pyyaml" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555 },
-]
-
-[[package]]
-name = "mkdocs-literate-nav"
-version = "0.6.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mkdocs" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f6/5f/99aa379b305cd1c2084d42db3d26f6de0ea9bf2cc1d10ed17f61aff35b9a/mkdocs_literate_nav-0.6.2.tar.gz", hash = "sha256:760e1708aa4be86af81a2b56e82c739d5a8388a0eab1517ecfd8e5aa40810a75", size = 17419 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8a/84/b5b14d2745e4dd1a90115186284e9ee1b4d0863104011ab46abb7355a1c3/mkdocs_literate_nav-0.6.2-py3-none-any.whl", hash = "sha256:0a6489a26ec7598477b56fa112056a5e3a6c15729f0214bea8a4dbc55bd5f630", size = 13261 },
-]
-
-[[package]]
-name = "mkdocs-macros-plugin"
-version = "1.3.9"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "hjson" },
- { name = "jinja2" },
- { name = "mkdocs" },
- { name = "packaging" },
- { name = "pathspec" },
- { name = "python-dateutil" },
- { name = "pyyaml" },
- { name = "super-collections" },
- { name = "termcolor" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f0/4f/c1c0b258d39739d25d9259d7379fdd016a893e4370734b1843cef6c90f2d/mkdocs_macros_plugin-1.3.9.tar.gz", hash = "sha256:01b6003fbe9b55fdc97c0abb66f811d65abfd291dcf70f277990165553faa99a", size = 33990 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/75/d6/18f19261f44813be207c46fb80da9ca13cb397aae5944b2b30c9eb1ebe3f/mkdocs_macros_plugin-1.3.9-py3-none-any.whl", hash = "sha256:c52351295efdbdbb37a9f0ea639719055ddb64a00115457289940e85696a81d9", size = 38219 },
-]
-
-[[package]]
-name = "mkdocs-material"
-version = "9.6.18"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "babel" },
- { name = "backrefs" },
- { name = "click" },
- { name = "colorama" },
- { name = "jinja2" },
- { name = "markdown" },
- { name = "mkdocs" },
- { name = "mkdocs-material-extensions" },
- { name = "paginate" },
- { name = "pygments" },
- { name = "pymdown-extensions" },
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e6/46/db0d78add5aac29dfcd0a593bcc6049c86c77ba8a25b3a5b681c190d5e99/mkdocs_material-9.6.18.tar.gz", hash = "sha256:a2eb253bcc8b66f8c6eaf8379c10ed6e9644090c2e2e9d0971c7722dc7211c05", size = 4034856 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/22/0b/545a4f8d4f9057e77f1d99640eb09aaae40c4f9034707f25636caf716ff9/mkdocs_material-9.6.18-py3-none-any.whl", hash = "sha256:dbc1e146a0ecce951a4d84f97b816a54936cdc9e1edd1667fc6868878ac06701", size = 9232642 },
-]
-
-[[package]]
-name = "mkdocs-material-extensions"
-version = "1.3.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 },
-]
-
-[[package]]
-name = "mkdocstrings"
-version = "1.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "markdown" },
- { name = "markupsafe" },
- { name = "mkdocs" },
- { name = "mkdocs-autorefs" },
- { name = "pymdown-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e5/13/10bbf9d56565fd91b91e6f5a8cd9b9d8a2b101c4e8ad6eeafa35a706301d/mkdocstrings-1.0.0.tar.gz", hash = "sha256:351a006dbb27aefce241ade110d3cd040c1145b7a3eb5fd5ac23f03ed67f401a", size = 101086 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ec/fc/80aa31b79133634721cf7855d37b76ea49773599214896f2ff10be03de2a/mkdocstrings-1.0.0-py3-none-any.whl", hash = "sha256:4c50eb960bff6e05dfc631f6bc00dfabffbcb29c5ff25f676d64daae05ed82fa", size = 35135 },
-]
-
-[package.optional-dependencies]
-python = [
- { name = "mkdocstrings-python" },
-]
-
-[[package]]
-name = "mkdocstrings-python"
-version = "2.0.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "griffe" },
- { name = "mkdocs-autorefs" },
- { name = "mkdocstrings" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/24/75/d30af27a2906f00eb90143470272376d728521997800f5dce5b340ba35bc/mkdocstrings_python-2.0.1.tar.gz", hash = "sha256:843a562221e6a471fefdd4b45cc6c22d2607ccbad632879234fa9692e9cf7732", size = 199345 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/81/06/c5f8deba7d2cbdfa7967a716ae801aa9ca5f734b8f54fd473ef77a088dbe/mkdocstrings_python-2.0.1-py3-none-any.whl", hash = "sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90", size = 105055 },
-]
-
-[[package]]
-name = "mypy"
-version = "1.9.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mypy-extensions" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/72/1e/a587a862c766a755a58b62d8c00aed11b74a15dc415c1bf5da7b607b0efd/mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974", size = 2995901 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6d/ce/c62c0c0d83b8a936ad6d5e0294e956e881acc5d680deb4929ea259fb50f6/mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3", size = 10605548 },
- { url = "https://files.pythonhosted.org/packages/da/e2/1864612774cf8a445f6d42ce73ce0f1492a37ed2af1c908e989f1ec7d349/mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc", size = 9767399 },
- { url = "https://files.pythonhosted.org/packages/a1/81/97e8539d6cdcfb3a8ae7eb1438c6983a9fc434ef9664572bfa7fd285cab9/mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129", size = 12421347 },
- { url = "https://files.pythonhosted.org/packages/ef/cf/43c1e29b9d3b2bf6c75e32d021d7db4631c516e4c0bd72b75bc8836680d8/mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612", size = 12490944 },
- { url = "https://files.pythonhosted.org/packages/59/56/a33d610a9cf692669690a89b54a6a920fd7c7ebcca00da2c36c9d975de8e/mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3", size = 9226854 },
- { url = "https://files.pythonhosted.org/packages/6e/96/40f0f605b1d4e2ad1fb11d21988ce3a3e205886c0fcbd35c9789a214de9a/mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd", size = 10725390 },
- { url = "https://files.pythonhosted.org/packages/d7/d2/072e40384b53051106b4fcf03537fb88e2a6ad0757d2ab7f6c8c2f188a69/mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6", size = 9731292 },
- { url = "https://files.pythonhosted.org/packages/85/a5/b7dc7eb69eda899fd07e71403b51b598a1f4df0f452d1da5844374082bcd/mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185", size = 12455450 },
- { url = "https://files.pythonhosted.org/packages/1c/1b/3e962a201d2f0f57c9fa1990e0dd6076f4f2f94954ab56e4a701ec3cc070/mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913", size = 12530368 },
- { url = "https://files.pythonhosted.org/packages/72/1f/8b214b69d08cc5e4bd8c3769ac55a43318f3529362ea55e5957774b69924/mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6", size = 9319112 },
- { url = "https://files.pythonhosted.org/packages/60/db/0ba2eaedca52bf5276275e8489951c26206030b3d31bf06f00875ae75d5d/mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e", size = 2555887 },
-]
-
-[[package]]
-name = "mypy-extensions"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 },
-]
-
-[[package]]
-name = "nox"
-version = "2025.5.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "argcomplete" },
- { name = "attrs" },
- { name = "colorlog" },
- { name = "dependency-groups" },
- { name = "packaging" },
- { name = "virtualenv" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b4/80/47712208c410defec169992e57c179f0f4d92f5dd17ba8daca50a8077e23/nox-2025.5.1.tar.gz", hash = "sha256:2a571dfa7a58acc726521ac3cd8184455ebcdcbf26401c7b737b5bc6701427b2", size = 4023334 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a6/be/7b423b02b09eb856beffe76fe8c4121c99852db74dd12a422dcb72d1134e/nox-2025.5.1-py3-none-any.whl", hash = "sha256:56abd55cf37ff523c254fcec4d152ed51e5fe80e2ab8317221d8b828ac970a31", size = 71753 },
-]
-
-[[package]]
-name = "packaging"
-version = "26.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831 },
-]
-
-[[package]]
-name = "paginate"
-version = "0.5.7"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 },
-]
-
-[[package]]
-name = "pathspec"
-version = "1.0.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206 },
-]
-
-[[package]]
-name = "platformdirs"
-version = "4.9.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348 },
-]
-
-[[package]]
-name = "pluggy"
-version = "1.6.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 },
-]
-
-[[package]]
-name = "pycodestyle"
-version = "2.14.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594 },
-]
-
-[[package]]
-name = "pydoclint"
-version = "0.6.10"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "docstring-parser-fork" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2b/ef/3e00c307c3bd0c2722fbd89bd05c200eace307b5a16b755c441a0e0d59fe/pydoclint-0.6.10.tar.gz", hash = "sha256:171c24f273c274715f4aa8754933cad11f2013878583e02146e416e732aae8b7", size = 159388 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6a/71/8d7de922c9ce149f78cea4fb23a8c1b3a84a9142d0b36c7feb987ee12e64/pydoclint-0.6.10-py3-none-any.whl", hash = "sha256:91e3d69be30e2d9201c1d34f63f01cb9d11aa6d8cbf63156bb886f6b15fb67ba", size = 68156 },
-]
-
-[[package]]
-name = "pydocstyle"
-version = "6.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "snowballstemmer" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/d5385ca59fd065e3c6a5fe19f9bc9d5ea7f2509fa8c9c22fb6b2031dd953/pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1", size = 36796 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/36/ea/99ddefac41971acad68f14114f38261c1f27dac0b3ec529824ebc739bdaa/pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019", size = 38038 },
-]
-
-[[package]]
-name = "pyflakes"
-version = "3.4.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551 },
-]
-
-[[package]]
-name = "pygments"
-version = "2.20.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 },
-]
-
-[[package]]
-name = "pylint"
-version = "3.3.8"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "astroid" },
- { name = "colorama", marker = "sys_platform == 'win32'" },
- { name = "dill" },
- { name = "isort" },
- { name = "mccabe" },
- { name = "platformdirs" },
- { name = "tomlkit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9d/58/1f614a84d3295c542e9f6e2c764533eea3f318f4592dc1ea06c797114767/pylint-3.3.8.tar.gz", hash = "sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05", size = 1523947 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2d/1a/711e93a7ab6c392e349428ea56e794a3902bb4e0284c1997cff2d7efdbc1/pylint-3.3.8-py3-none-any.whl", hash = "sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83", size = 523153 },
-]
-
-[[package]]
-name = "pymdown-extensions"
-version = "10.21.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markdown" },
- { name = "pyyaml" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901 },
-]
-
-[[package]]
-name = "pyparsing"
-version = "3.3.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781 },
-]
-
-[[package]]
-name = "pytest"
-version = "8.4.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
- { name = "iniconfig" },
- { name = "packaging" },
- { name = "pluggy" },
- { name = "pygments" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474 },
-]
-
-[[package]]
-name = "pytest-asyncio"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pytest" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157 },
-]
-
-[[package]]
-name = "pytest-mock"
-version = "3.14.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pytest" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 },
-]
-
-[[package]]
-name = "python-dateutil"
-version = "2.9.0.post0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "six" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
-]
-
-[[package]]
-name = "python-discovery"
-version = "1.2.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "filelock" },
- { name = "platformdirs" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894 },
-]
-
-[[package]]
-name = "pyyaml"
-version = "6.0.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826 },
- { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577 },
- { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556 },
- { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114 },
- { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638 },
- { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463 },
- { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986 },
- { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543 },
- { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763 },
- { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 },
- { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 },
- { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 },
- { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 },
- { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 },
- { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 },
- { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 },
- { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 },
- { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 },
- { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 },
- { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 },
- { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 },
- { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 },
- { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 },
- { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 },
- { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 },
- { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 },
- { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 },
- { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 },
- { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 },
- { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 },
- { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 },
- { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 },
- { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 },
- { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 },
- { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 },
- { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 },
- { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 },
- { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 },
- { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 },
- { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 },
- { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 },
- { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 },
- { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 },
- { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 },
- { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 },
- { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 },
- { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 },
-]
-
-[[package]]
-name = "pyyaml-env-tag"
-version = "1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pyyaml" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722 },
-]
-
-[[package]]
-name = "requests"
-version = "2.33.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "certifi" },
- { name = "charset-normalizer" },
- { name = "idna" },
- { name = "urllib3" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947 },
-]
-
-[[package]]
-name = "semver"
-version = "3.0.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912 },
-]
-
-[[package]]
-name = "six"
-version = "1.17.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
-]
-
-[[package]]
-name = "snowballstemmer"
-version = "3.0.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274 },
-]
-
-[[package]]
-name = "super-collections"
-version = "0.6.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "hjson" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e0/de/a0c3d1244912c260638f0f925e190e493ccea37ecaea9bbad7c14413b803/super_collections-0.6.2.tar.gz", hash = "sha256:0c8d8abacd9fad2c7c1c715f036c29f5db213f8cac65f24d45ecba12b4da187a", size = 31315 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/17/43/47c7cf84b3bd74a8631b02d47db356656bb8dff6f2e61a4c749963814d0d/super_collections-0.6.2-py3-none-any.whl", hash = "sha256:291b74d26299e9051d69ad9d89e61b07b6646f86a57a2f5ab3063d206eee9c56", size = 16173 },
-]
-
-[[package]]
-name = "sybil"
-version = "10.0.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/de/2b/5ee5ef413b87f215c03cd08462dc341903b86707e517e20715823c984893/sybil-10.0.1.tar.gz", hash = "sha256:319eed013ebe848f8c57ce79c1ed526e506d952c778693979bc509513ae72a68", size = 80120 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/41/f3/4f2a8069d212a33bccc88be13b5bdad61f810054f6c21c6da3aa25fb38d0/sybil-10.0.1-py3-none-any.whl", hash = "sha256:0030d8583d5ce97969397d303db1f17054f61b23f18469c0ac61f9f42735571c", size = 40316 },
-]
-
-[[package]]
-name = "termcolor"
-version = "3.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734 },
-]
-
-[[package]]
-name = "tomlkit"
-version = "0.14.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310 },
-]
-
-[[package]]
-name = "types-markdown"
-version = "3.8.0.20250809"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/04/65/6abc45be9bfead21329da7a1c69f6aa6212f60917b24a93c9efc0fb00ec6/types_markdown-3.8.0.20250809.tar.gz", hash = "sha256:fa619e735878a244332a4bbe16bcfc44e49ff6264c2696056278f0642cdfa223", size = 19310 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/2f/77ecd81e0d1bf54c576b84f076b9fa2a5ba4aef8a4b56e7fb50b9fa52f3b/types_markdown-3.8.0.20250809-py3-none-any.whl", hash = "sha256:3f34a38c2259a3158e90ab0cb058cd8f4fdd3d75e2a0b335cb57f25dc2bc77d3", size = 25753 },
-]
-
-[[package]]
-name = "typing-extensions"
-version = "4.15.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 },
-]
-
-[[package]]
-name = "urllib3"
-version = "2.6.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584 },
-]
-
-[[package]]
-name = "verspec"
-version = "0.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e7/44/8126f9f0c44319b2efc65feaad589cadef4d77ece200ae3c9133d58464d0/verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e", size = 27123 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31", size = 19640 },
-]
-
-[[package]]
-name = "virtualenv"
-version = "21.2.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "distlib" },
- { name = "filelock" },
- { name = "platformdirs" },
- { name = "python-discovery" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/0c/98/3a7e644e19cb26133488caff231be390579860bbbb3da35913c49a1d0a46/virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada", size = 5850742 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/27/8d/edd0bd910ff803c308ee9a6b7778621af0d10252219ad9f19ef4d4982a61/virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac", size = 5831232 },
-]
-
-[[package]]
-name = "watchdog"
-version = "6.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393 },
- { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392 },
- { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019 },
- { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 },
- { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 },
- { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 },
- { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 },
- { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 },
- { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 },
- { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 },
- { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 },
- { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 },
- { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 },
- { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 },
- { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 },
- { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 },
- { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 },
- { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 },
- { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 },
-]
-
-[[package]]
-name = "zipp"
-version = "3.23.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378 },
-]