diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index f2e140bb..7122dd74 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -12,7 +12,7 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
- "postCreateCommand": "uv sync && uv run pre-commit install",
+ "postCreateCommand": "uv sync --all-extras --dev && uv run prek install",
// Configure tool-specific properties.
"customizations": {
"vscode": {
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000..799f3697
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,56 @@
+name: docs
+
+on:
+ release:
+ types:
+ - created
+ pull_request:
+ paths:
+ - docs/**
+ - mkdocs.yml
+ - pyproject.toml
+ - README.md
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+
+ - name: Set up uv
+ uses: astral-sh/setup-uv@v3
+
+ - name: Install docs dependencies
+ run: uv sync --all-extras --dev
+
+ - name: Build site
+ run: uv run mkdocs build
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: site
+
+ deploy:
+ if: github.event_name == 'release'
+ needs: build
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 4bc7be1c..d9bf6cb4 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+ python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
@@ -32,7 +32,7 @@ jobs:
- name: Install the project
run: uv sync --all-extras --dev
- - name: Run pre-commit
+ - name: Run prek (pre-commit checks)
env:
SKIP: ${{ github.ref == 'refs/heads/main' && 'no-commit-to-branch' || '' }}
- run: uv run pre-commit run --show-diff-on-failure --color=always --all-files
+ run: uv run prek run --show-diff-on-failure --color=always --all-files
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c1a7c00d..1583ea3b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+ python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ac60a34d..0bad6d73 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -16,7 +16,7 @@ repos:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-json
- exclude: .devcontainer
+ exclude: .devcontainer|.vscode
- id: check-yaml
- id: check-added-large-files
- id: no-commit-to-branch
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..fcb02265
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,17 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Docs: Serve (development)",
+ "type": "shell",
+ "command": "uv run mkdocs serve"
+ },
+ {
+ "label": "Docs: Build",
+ "type": "shell",
+ "command": "uv run mkdocs build --clean"
+ },
+ ]
+}
diff --git a/AGENTS.md b/AGENTS.md
index fc9d0842..2cf0ced4 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -29,10 +29,10 @@ Always use full type annotations, generics, and other modern practices.
# Install all dependencies:
uv sync
- # Run linting (with ruff), pre-commit checks and type checking (with mypy).
+ # Run linting (with ruff), prek (pre-commit alternative) checks and type checking (with mypy).
# Note when you run this, ruff will auto-format and sort imports, resolving any
# linter warnings about import ordering:
- uv run pre-commit run --all-files
+ uv run prek run --all-files
# Run tests:
uv run pytest
diff --git a/README.md b/README.md
index b9b9b7c4..3bcaa109 100644
--- a/README.md
+++ b/README.md
@@ -37,9 +37,10 @@ pip install pyoverkiz
import asyncio
import time
-from pyoverkiz.const import SUPPORTED_SERVERS
+from pyoverkiz.auth.credentials import UsernamePasswordCredentials
from pyoverkiz.client import OverkizClient
-from pyoverkiz.enums import Server
+from pyoverkiz.models import Action
+from pyoverkiz.enums import Server, OverkizCommand
USERNAME = ""
PASSWORD = ""
@@ -47,7 +48,8 @@ PASSWORD = ""
async def main() -> None:
async with OverkizClient(
- USERNAME, PASSWORD, server=SUPPORTED_SERVERS[Server.SOMFY_EUROPE]
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials(USERNAME, PASSWORD),
) as client:
try:
await client.login()
@@ -61,6 +63,19 @@ async def main() -> None:
print(f"{device.label} ({device.id}) - {device.controllable_name}")
print(f"{device.widget} - {device.ui_class}")
+ await client.execute_action_group(
+ actions=[
+ Action(
+ device_url="io://1234-5678-1234/12345678",
+ commands=[
+ Command(name=OverkizCommand.SET_CLOSURE, parameters=[100])
+ ]
+ )
+ ],
+ label="Execution via Python",
+ # mode=CommandMode.HIGH_PRIORITY
+ )
+
while True:
events = await client.fetch_events()
print(events)
@@ -76,14 +91,11 @@ asyncio.run(main())
```python
import asyncio
import time
-import aiohttp
+from pyoverkiz.auth.credentials import LocalTokenCredentials
from pyoverkiz.client import OverkizClient
-from pyoverkiz.const import SUPPORTED_SERVERS, OverkizServer
-from pyoverkiz.enums import Server
+from pyoverkiz.utils import create_local_server_config
-USERNAME = ""
-PASSWORD = ""
LOCAL_GATEWAY = "gateway-xxxx-xxxx-xxxx.local" # or use the IP address of your gateway
VERIFY_SSL = True # set verify_ssl to False if you don't use the .local hostname
@@ -91,23 +103,10 @@ VERIFY_SSL = True # set verify_ssl to False if you don't use the .local hostnam
async def main() -> None:
token = "" # generate your token via the Somfy app and include it here
- # Local Connection
- session = aiohttp.ClientSession(
- connector=aiohttp.TCPConnector(verify_ssl=VERIFY_SSL)
- )
-
async with OverkizClient(
- username="",
- password="",
- token=token,
- session=session,
+ server=create_local_server_config(host=LOCAL_GATEWAY),
+ credentials=LocalTokenCredentials(token),
verify_ssl=VERIFY_SSL,
- server=OverkizServer(
- name="Somfy TaHoma (local)",
- endpoint=f"https://{LOCAL_GATEWAY}:8443/enduser-mobile-web/1/enduserAPI/",
- manufacturer="Somfy",
- configuration_url=None,
- ),
) as client:
await client.login()
diff --git a/docs/action-queue.md b/docs/action-queue.md
new file mode 100644
index 00000000..f14c02c7
--- /dev/null
+++ b/docs/action-queue.md
@@ -0,0 +1,147 @@
+# Action queue
+
+The action queue automatically groups rapid, consecutive calls to `execute_action_group()` into a single ActionGroup execution. This minimizes the number of API calls and helps prevent rate limiting issues, such as `TooManyRequestsException`, `TooManyConcurrentRequestsException`, `TooManyExecutionsException`, or `ExecutionQueueFullException` which can occur if actions are sent individually in quick succession.
+
+Important limitation:
+- Gateways only allow a single action per device in each action group. The queue
+ merges commands for the same `device_url` into a single action to keep the
+ batch valid and preserve command order for that device.
+- If you pass multiple actions for the same `device_url` in a single
+ `execute_action_group()` call, the queue will merge them for you.
+
+## Enable with defaults
+
+Set `action_queue=True` to enable batching with default settings:
+
+```python
+import asyncio
+
+from pyoverkiz.auth import UsernamePasswordCredentials
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.enums import OverkizCommand, Server
+from pyoverkiz.models import Action, Command
+
+client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("user@example.com", "password"),
+ action_queue=True, # uses defaults
+)
+
+action1 = Action(
+ device_url="io://1234-5678-1234/12345678",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+)
+action2 = Action(
+ device_url="io://1234-5678-1234/87654321",
+ commands=[Command(name=OverkizCommand.OPEN)],
+)
+
+task1 = asyncio.create_task(client.execute_action_group([action1]))
+task2 = asyncio.create_task(client.execute_action_group([action2]))
+exec_id1, exec_id2 = await asyncio.gather(task1, task2)
+
+print(exec_id1 == exec_id2)
+```
+
+Defaults:
+- `delay=0.5`
+- `max_actions=20`
+
+## Advanced settings
+
+If you need to tune batching behavior, pass `ActionQueueSettings`:
+
+```python
+import asyncio
+
+from pyoverkiz.action_queue import ActionQueueSettings
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.auth import UsernamePasswordCredentials
+from pyoverkiz.enums import OverkizCommand, Server
+from pyoverkiz.models import Action, Command
+
+client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("user@example.com", "password"),
+ action_queue=ActionQueueSettings(
+ delay=0.5, # seconds to wait before auto-flush
+ max_actions=20, # auto-flush when this count is reached
+ ),
+)
+```
+
+## `flush_action_queue()` (force immediate execution)
+
+Normally, queued actions are sent after the delay window or when `max_actions` is reached. Call `flush_action_queue()` to force the queue to execute immediately, which is useful when you want to send any pending actions without waiting for the delay timer to expire.
+
+```python
+from pyoverkiz.action_queue import ActionQueueSettings
+import asyncio
+
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.auth import UsernamePasswordCredentials
+from pyoverkiz.enums import OverkizCommand, Server
+from pyoverkiz.models import Action, Command
+
+client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("user@example.com", "password"),
+ action_queue=ActionQueueSettings(delay=10.0), # long delay
+)
+
+action = Action(
+ device_url="io://1234-5678-1234/12345678",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+)
+
+exec_task = asyncio.create_task(client.execute_action_group([action]))
+
+# Give it time to enter the queue
+await asyncio.sleep(0.05)
+
+# Force immediate execution instead of waiting 10 seconds
+await client.flush_action_queue()
+
+exec_id = await exec_task
+print(exec_id)
+```
+
+Why this matters:
+- It lets you keep a long delay for batching, but still force a quick execution when a user interaction demands it.
+- Useful before shutdown to avoid leaving actions waiting in the queue.
+
+## `get_pending_actions_count()` (best-effort count)
+
+`get_pending_actions_count()` returns a snapshot of how many actions are currently queued. Because the queue can change concurrently (and the method does not acquire the queue lock), the value is approximate. Use it for logging, diagnostics, or UI hints—not for critical control flow.
+
+```python
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.auth import UsernamePasswordCredentials
+from pyoverkiz.enums import OverkizCommand, Server
+from pyoverkiz.models import Action, Command
+
+client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("user@example.com", "password"),
+ action_queue=True,
+)
+
+action = Action(
+ device_url="io://1234-5678-1234/12345678",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+)
+
+exec_task = asyncio.create_task(client.execute_action_group([action]))
+await asyncio.sleep(0.01)
+
+pending = client.get_pending_actions_count()
+print(f"Pending actions (approx): {pending}")
+
+exec_id = await exec_task
+print(exec_id)
+```
+
+Why it’s best-effort:
+- Actions may flush automatically while you read the count.
+- New actions may be added concurrently by other tasks.
+- The count can be briefly stale, so avoid using it to decide whether you must flush or not.
diff --git a/docs/api-reference.md b/docs/api-reference.md
new file mode 100644
index 00000000..f10e1f8f
--- /dev/null
+++ b/docs/api-reference.md
@@ -0,0 +1,13 @@
+::: pyoverkiz.client.OverkizClient
+
+::: pyoverkiz.models
+ options:
+ show_source: false
+
+::: pyoverkiz.enums
+ options:
+ show_source: false
+
+::: pyoverkiz.exceptions
+ options:
+ show_source: false
diff --git a/docs/apiDocDyn.js b/docs/api/apiDocDyn.js
similarity index 100%
rename from docs/apiDocDyn.js
rename to docs/api/apiDocDyn.js
diff --git a/docs/apiDocStyle.css b/docs/api/apiDocStyle.css
similarity index 100%
rename from docs/apiDocStyle.css
rename to docs/api/apiDocStyle.css
diff --git a/docs/tahoma_api.html b/docs/api/index.html
similarity index 100%
rename from docs/tahoma_api.html
rename to docs/api/index.html
diff --git a/docs/jquery-1.10.2-ui.js b/docs/api/jquery-1.10.2-ui.js
similarity index 100%
rename from docs/jquery-1.10.2-ui.js
rename to docs/api/jquery-1.10.2-ui.js
diff --git a/docs/jquery-1.10.2.js b/docs/api/jquery-1.10.2.js
similarity index 100%
rename from docs/jquery-1.10.2.js
rename to docs/api/jquery-1.10.2.js
diff --git a/docs/callable_names.txt b/docs/callable_names.txt
deleted file mode 100644
index 8ee5ac35..00000000
--- a/docs/callable_names.txt
+++ /dev/null
@@ -1,639 +0,0 @@
-camera:DomisCameraComponent
-camera:GenericCameraComponent
-core:OccupancySensor
-core:SmokeSensor
-core:WeatherSystemSensor
-eliot:DimmerLightEliotComponent
-eliot:EliotElectricEnergyConsumptionSensor
-eliot:OnOffLightEliotComponent
-eliot:OnOffSwitchEliotComponent
-eliot:RemoteEliotComponent
-eliot:RollerShutterEliotComponent
-enocean:EnOcean0to1530LuxLightSensor
-enocean:EnOcean0to510LuxLightSensor
-enocean:EnOcean0to999LuxLightSensor
-enocean:EnOceanAirQualityBasedOnTemperatureHumiditySensor
-enocean:EnOceanAvidsenAMRCounter
-enocean:EnOceanAvidsenRollerShutterComponent
-enocean:EnOceanAvidsenRollerShutterComponentWithReCom
-enocean:EnOceanAvidsenTICCounter
-enocean:EnOceanCO2Sensor
-enocean:EnOceanCaleffiValveComponent
-enocean:EnOceanCardSwitchComponent
-enocean:EnOceanContactSensor
-enocean:EnOceanContactWithVoltageSupplySensor
-enocean:EnOceanDimplexVentilationComponent
-enocean:EnOceanElectricConsumptionCounter
-enocean:EnOceanElectricCurrentConsumptionSensor
-enocean:EnOceanElectricEnergyConsumptionSensor
-enocean:EnOceanEwattchAMRCounter
-enocean:EnOceanEwattchTICCounter
-enocean:EnOceanEwattchTICPowerConsumption
-enocean:EnOceanGenericAMRCounter
-enocean:EnOceanGenericActuator
-enocean:EnOceanGenericElectricCounter
-enocean:EnOceanHVACBatteryPoweredComponent
-enocean:EnOceanHVACTemperatureSensor
-enocean:EnOceanHeatingSystemConsumptionSensor
-enocean:EnOceanHumidityComfortSensor
-enocean:EnOceanHumiditySensor
-enocean:EnOceanInternalHumiditySensor
-enocean:EnOceanLightSensorType01
-enocean:EnOceanLightSensorType02
-enocean:EnOceanMicroPeltValveComponent
-enocean:EnOceanMicroPeltValveV2WithRemoteCommissioningComponent
-enocean:EnOceanMicroPeltValveWithRemoteCommissioningComponent
-enocean:EnOceanMultiChannelEnergyConsumptionSensor
-enocean:EnOceanNativeRepeaterComponent
-enocean:EnOceanNexelecHumiditySensor
-enocean:EnOceanNexelecSmokeSensor
-enocean:EnOceanNexelecTemperatureSensor
-enocean:EnOceanOccupancySensor
-enocean:EnOceanOnOffHeatingSystemComponent
-enocean:EnOceanOnOffHeatingSystemComponentWithReCom
-enocean:EnOceanOnOffLight
-enocean:EnOceanPMDMValveWithRemoteCommissioning
-enocean:EnOceanPilotWireComponent
-enocean:EnOceanPilotWireComponentWithReCom
-enocean:EnOceanPilotWireThermostatComponent
-enocean:EnOceanRainSensor
-enocean:EnOceanRepeaterComponent
-enocean:EnOceanRockerSwitchAutoManuUpDownComponent
-enocean:EnOceanRockerSwitchx1Component
-enocean:EnOceanRockerSwitchx2Component
-enocean:EnOceanRockerSwitchx4Component
-enocean:EnOceanRollerShutter
-enocean:EnOceanRoomOperatingPanelFanSpeed
-enocean:EnOceanRoomOperatingPanelOccupancyControl
-enocean:EnOceanRoomOperatingPanelTemperatureSensor
-enocean:EnOceanSmokeSensor
-enocean:EnOceanStatefulRollerShutter
-enocean:EnOceanSunIntensitySensor
-enocean:EnOceanSwitchOnOff
-enocean:EnOceanSwitchOnOffConsumptionSensorType6
-enocean:EnOceanSwitchOnOffConsumptionSensorType8
-enocean:EnOceanSwitchOnOffConsumptionSensorTypeB
-enocean:EnOceanSwitchOnOffConsumptionSensorTypeE
-enocean:EnOceanSwitchOnOffType1
-enocean:EnOceanSwitchOnOffType12
-enocean:EnOceanSwitchOnOffType12WithReCom
-enocean:EnOceanSwitchOnOffType12WithRemoteCommissioning
-enocean:EnOceanSwitchOnOffType6
-enocean:EnOceanSwitchOnOffType8
-enocean:EnOceanSwitchOnOffType8WithReCom
-enocean:EnOceanSwitchOnOffTypeA
-enocean:EnOceanSwitchOnOffTypeB
-enocean:EnOceanSwitchOnOffTypeE
-enocean:EnOceanSwitchOnOffTypeF
-enocean:EnOceanSwitchOnOffTypeFWithReCom
-enocean:EnOceanSwitchOnOffTypeFWithRemoteCommissioning
-enocean:EnOceanTemperatureSensor
-enocean:EnOceanTemperatureSensorScale0To51
-enocean:EnOceanTemperatureSensorScaleMinus30To50
-enocean:EnOceanTemperatureSensorScaleMinus40To80
-enocean:EnOceanTemperatureSensorType01
-enocean:EnOceanTemperatureSensorType05
-enocean:EnOceanTemperatureSensorType14
-enocean:EnOceanTemperatureSetPoint
-enocean:EnOceanThermostatComponent
-enocean:EnOceanThermostatController
-enocean:EnOceanVOCSensor
-enocean:EnOceanVenetianBlindController
-enocean:EnOceanVenetianBlindControllerWithRemoteCommissioning
-enocean:EnOceanWaterSensor
-enocean:EnOceanWindSpeedSensor
-enocean:EnOceanWindowHandle
-enocean:EnOceanWindowWithVibrationDetectionSensor
-enocean:EnOceanWizzConsoCounter
-enocean:EnOceanWizzPulseCounter
-enocean:EnoceanRollerShutterWithRemoteCommissioning
-enocean:RemoteControllerOneWayComponent
-enocean:TransceiverEnoceanComponent
-hue:BloomHUEComponent
-hue:BridgeHUEComponent
-hue:BridgeHUEV2Component
-hue:ColorLightModuleHUEComponent
-hue:ColorTemperatureLightBulbHUEComponent
-hue:ColorTemperatureLightCandleHUEComponent
-hue:ColorTemperatureLightSpotHUEComponent
-hue:ColorTemperatureModuleHUEComponent
-hue:ExtendedColorLightCandleHUEComponent
-hue:GenericColorLightHUEComponent
-hue:GenericColorTemperatureLightHUEComponent
-hue:GenericDimmableLightHUEComponent
-hue:GenericExtendedColorLightHUEComponent
-hue:GoHUEComponent
-hue:HueLampHUEComponent
-hue:HueLuxHUEComponent
-hue:HueSpotHUEComponent
-hue:LightStripsHUEComponent
-hue:LightStripsPlusHUEComponent
-hue:PhoenixHUEComponent
-internal:PodComponent
-internal:PodMiniComponent
-internal:PodV2Component
-internal:PodV3Component
-internal:TSKAlarmComponent
-internal:TSKDockComponent
-internal:UPodComponent
-internal:UPodNetWorkComponent
-internal:WifiComponent
-io:AdjustableSlatsRollerShutterIOComponent
-io:AirConditioningElectricalEnergyConsumptionSensor
-io:AlarmIOComponent
-io:AtlanticDimmableLightIOComponent
-io:AtlanticDomesticHotWaterProductionIOComponent
-io:AtlanticDomesticHotWaterProductionV2IOComponent
-io:AtlanticDomesticHotWaterProductionV2_AEX_IOComponent
-io:AtlanticDomesticHotWaterProductionV2_CETHI_V4_IOComponent
-io:AtlanticDomesticHotWaterProductionV2_CE_FLAT_C2_IOComponent
-io:AtlanticDomesticHotWaterProductionV2_CE_S4_IOComponent
-io:AtlanticDomesticHotWaterProductionV2_CV4E_IOComponent
-io:AtlanticDomesticHotWaterProductionV2_MURAL_IOComponent
-io:AtlanticDomesticHotWaterProductionV2_SPLIT_IOComponent
-io:AtlanticDomesticHotWaterProductionV2_TEC5_IOComponent
-io:AtlanticDomesticHotWaterProductionV3IOComponent
-io:AtlanticElectricalHeaterIOComponent
-io:AtlanticElectricalHeaterWithAdjustableTemperatureSetpointIOComponent
-io:AtlanticElectricalTowelDryerIOComponent
-io:AtlanticElectricalTowelDryer_IC3_IOComponent
-io:AtlanticHeatRecoveryVentilationIOComponent
-io:AtlanticPassAPCBoilerMainComponent
-io:AtlanticPassAPCDHWComponent
-io:AtlanticPassAPCECSFossilEnergyConsumptionSensor
-io:AtlanticPassAPCHeatPumpMainComponent
-io:AtlanticPassAPCHeatingAndCoolingZoneComponent
-io:AtlanticPassAPCHeatingFossilEnergyConsumptionSensor
-io:AtlanticPassAPCHeatingZoneComponent
-io:AtlanticPassAPCOutsideTemperatureSensor
-io:AtlanticPassAPCTotalFossilEnergyConsumptionSensor
-io:AtlanticPassAPCZoneControlMainComponent
-io:AtlanticPassAPCZoneControlZoneComponent
-io:AtlanticPassAPCZoneTemperatureSensor
-io:AwningReceiverUnoIOComponent
-io:AwningValanceIOComponent
-io:BeaconIOComponent
-io:BioclimaticPergolaIOComponent
-io:CO2IOSystemDeviceSensor
-io:CO2IOSystemSensor
-io:COIOSystemSensor
-io:CarButtonIODeviceSensor
-io:CarLockIOComponent
-io:ContactIOSystemDeviceSensor
-io:ContactIOSystemSensor
-io:CoolingRelatedElectricalEnergyConsumptionSensor
-io:CumulatedElectricalEnergyConsumptionIOSystemDeviceSensor
-io:CumulatedElectricalEnergyConsumptionIOSystemSensor
-io:CumulatedThermalEnergyConsumptionIOSystemSensor
-io:CumulatedWaterConsumptionIOSystemSensor
-io:CurtainTrackUnoIOComponent
-io:CyclicStandardReceiverIOComponent
-io:DHWCumulatedElectricalEnergyConsumptionIOSystemDeviceSensor
-io:DHWElectricalEnergyConsumptionSensor
-io:DHWRelatedElectricalEnergyConsumptionSensor
-io:DHWRelatedFossilEnergyConsumptionSensor
-io:DimmableColorTemperatureLightIOComponent
-io:DimmableLightIOComponent
-io:DimmableRGBLightIOComponent
-io:DiscreteExteriorHeatingIOComponent
-io:DiscreteGarageOpenerIOComponent
-io:DiscreteGarageOpenerWithPartialPositionIOComponent
-io:DiscreteGateOpenerIOComponent
-io:DomesticHotWaterTankComponent
-io:DoorLockIOComponent
-io:DropArmAwningIOComponent
-io:DualRollerShutterIOComponent
-io:ElectricalHeaterGenericIOComponent
-io:ElectricalHeaterWithAdjustableTemperatureSetpointGenericIOComponent
-io:ElectricityMeterComponent
-io:EnergyConsumptionSensorsConfigurationComponent
-io:EnergyConsumptionSensorsHeatPumpComponent
-io:ExteriorHeatingIOComponent
-io:ExteriorVenetianBlindIOComponent
-io:ExteriorVenetianBlindUnoIOComponent
-io:ExteriorVenetianBlindWithWP2IOComponent
-io:ExteriorVenetianBlindWithWPIOComponent
-io:GarageOpenerIOComponent
-io:GasDHWConsumptionSensor
-io:GasHeaterConsumptionSensor
-io:GateOpenerIOComponent
-io:GenericPulseSensor
-io:GradHermeticIOComponent
-io:HeatPumpIOComponent
-io:HeatingElectricalEnergyConsumptionSensor
-io:HeatingRelatedElectricalEnergyConsumptionSensor
-io:HeatingRelatedFossilEnergyConsumptionSensor
-io:HeatingTemperatureInterfaceIOComponent
-io:HeatingValveIOComponent
-io:HorizontalAwningIOComponent
-io:HorizontalAwningUnoIOComponent
-io:IORemoteController
-io:IzymoController
-io:KeygoController
-io:KizOThermBoilerTemperatureSensor
-io:KizOThermDHWTemperatureSensor
-io:KizOThermIOComponent
-io:KizOThermOutsideTemperatureSensor
-io:KizOThermRoomTemperatureSensor
-io:KizOThermV2IOComponent
-io:LightIOSystemDeviceSensor
-io:LightIOSystemSensor
-io:LightMicroModuleSomfyIOComponent
-io:LockUnlockDoorLockWithUnknownPositionIOComponent
-io:MicroModuleRollerShutterSomfyIOComponent
-io:OccupancyIOSystemDeviceSensor
-io:OccupancyIOSystemSensor
-io:OnOffIOComponent
-io:OnOffLightIOComponent
-io:OpenClosedTiltSensor
-io:OtherElectricalEnergyConsumptionSensor
-io:PergolaHorizontalUnoIOComponent
-io:PergolaRailGuidedAwningIOComponent
-io:PergolaScreenIOComponent
-io:PergolaSideScreenIOComponent
-io:PlugsElectricalEnergyConsumptionSensor
-io:PositionableAndLockableSlidingWindowComponent
-io:PositionableSlidingWindowComponent
-io:ProjectionRollerShutterIOComponent
-io:RSOverkizIOComponent
-io:RainIOSystemSensor
-io:RelativeHumidityIOSystemDeviceSensor
-io:RelativeHumidityIOSystemSensor
-io:RollerShutterGenericIOComponent
-io:RollerShutterUnoIOComponent
-io:RollerShutterVeluxIOComponent
-io:RollerShutterWithLowSpeedManagementIOComponent
-io:RollingDoorOpenerIOComponent
-io:ScreenReceiverUnoIOComponent
-io:SimpleBioclimaticPergolaIOComponent
-io:SimpleExteriorHeatingIOComponent
-io:SirenIOComponent
-io:SlidingDiscreteFullyPedestriableGateOpenerIOComponent
-io:SmokeIOSystemSensor
-io:SomfyBasicContactIOSystemSensor
-io:SomfyContactIOSystemSensor
-io:SomfyIndoorSimpleSirenIOComponent
-io:SomfyOccupancyIOSystemSensor
-io:SomfyRainIOSystemSensor
-io:SomfySmokeIOSystemSensor
-io:SunEnergyIOSystemSensor
-io:SwimmingPoolIOComponent
-io:SwitchMicroModuleIOComponent
-io:SwitchMicroModuleSomfyIOComponent
-io:TemperatureIOSystemSensor
-io:TemperatureInCelciusIOSystemDeviceSensor
-io:TotalElectricalEnergyConsumptionIOSystemSensor
-io:TotalElectricalEnergyConsumptionSensor
-io:TotalFossilEnergyConsumptionSensor
-io:UnknownIOComponent
-io:VRHollaIOComponent
-io:VenetianBlindIOComponent
-io:VentilationPointAirInletIOComponent
-io:VentilationPointAirOutletIOComponent
-io:VentilationPointAirTransferIOComponent
-io:VentilationPointIOComponent
-io:VerticalExteriorAwningIOComponent
-io:VerticalExteriorAwningUnoIOComponent
-io:VerticalInteriorBlindGenericIOComponent
-io:VerticalInteriorBlindUnoIOComponent
-io:VerticalInteriorBlindVeluxIOComponent
-io:VibrationSensor
-io:WindIOSystemSensor
-io:WindowLockIOComponent
-io:WindowOpenerGenericIOComponent
-io:WindowOpenerUnoIOComponent
-io:WindowOpenerVeluxIOComponent
-jsw:CameraController
-modbus:DeDietrichGatewayComponent
-modbus:FlaktWoodsRS485Component
-modbus:YutakiDHWTComponent
-modbus:YutakiMainComponent
-modbus:YutakiRoomThermostatZone1Component
-modbus:YutakiRoomThermostatZone2Component
-modbus:YutakiSwimmingPoolComponent
-modbus:YutakiV2DHWElectricalEnergyConsumptionComponent
-modbus:YutakiV2DHWTComponent
-modbus:YutakiV2SpaceCoolingElectricalEnergyConsumptionComponent
-modbus:YutakiV2SpaceHeatingElectricalEnergyConsumptionComponent
-modbus:YutakiV2SwimmingPoolElectricalEnergyConsumptionComponent
-modbus:YutakiV2Zone1Component
-modbus:YutakiV2Zone2Component
-modbus:YutakiZone1Component
-modbus:YutakiZone2Component
-myfox:AlarmController
-myfox:CameraController
-myfox:HomeKeeperProAlarmController
-myfox:LightController
-myfox:SecurityCameraController
-myfox:SomfyOneCameraController
-myfox:SomfyOnePlusCameraController
-myfox:SomfyProtectAlarmController
-myfox:SomfyProtectSecurityCameraController
-opendoors:OpenDoorsSmartLockComponent
-ovp:BoilerControllerOVPComponent
-ovp:CoolingCumulatedElectricalEnergyConsumptionSensor
-ovp:CothermThermostatOVPComponent
-ovp:DHW1OnDHWCircuitOVPComponent
-ovp:DHW1TemperatureSensorOnDHWCircuitOVPComponent
-ovp:DHW2OnAuxiliaryCircuitOVPComponent
-ovp:DHW2OnCircuitAOVPComponent
-ovp:DHW2TemperatureSensorOnAuxiliaryCircuitOVPComponent
-ovp:DHW2TemperatureSensorOnCircuitAOVPComponent
-ovp:DHWCumulatedElectricalEnergyConsumptionSensor
-ovp:DeDietrichModbusGatewayOVPComponent
-ovp:ElectricalHeaterGenericOVPComponent
-ovp:HLinkMainController
-ovp:HeatingCircuitOnCircuitAOVPComponent
-ovp:HeatingCircuitOnCircuitBOVPComponent
-ovp:HeatingCircuitOnCircuitCOVPComponent
-ovp:HeatingCircuitTemperatureSensorOnCircuitAOVPComponent
-ovp:HeatingCircuitTemperatureSensorOnCircuitBOVPComponent
-ovp:HeatingCircuitTemperatureSensorOnCircuitCOVPComponent
-ovp:HeatingCumulatedElectricalEnergyConsumptionSensor
-ovp:HeatingTemperatureInterfaceTemperatureSensor
-ovp:ImhotepHeatingTemperatureInterfaceOVPComponent
-ovp:ImhotepHeatingTemperatureInterfaceTemperatureSensor
-ovp:ImhotepHeatingTemperatureRelayOVPComponent
-ovp:InsideTemperatureSensorOVPComponent
-ovp:KizOThermBoilerTemperatureSensor
-ovp:KizOThermDHWTemperatureSensor
-ovp:KizOThermOutsideTemperatureSensor
-ovp:KizOThermRoomTemperatureSensor
-ovp:KizOThermV2OVPComponent
-ovp:LPBMainController
-ovp:ModbusMainController
-ovp:OnOffOVPComponent
-ovp:OutsideTemperatureSensorOVPComponent
-ovp:SolarCircuitOVPComponent
-ovp:SomfyHeatingTemperatureInterfaceOVPComponent
-ovp:SomfyPilotWireElectricalHeaterOVPComponent
-ovp:SomfyPilotWireHeatingInterfaceOVPComponent
-ovp:SomfyPilotWireTemperatureSensorOVPComponent
-ovp:SwimmingPoolOnCircuitBOVPComponent
-ovp:SwimmingPoolOnCircuitCOVPComponent
-ovp:SwimmingPoolTemperatureSensorOnCircuitBOVPComponent
-ovp:SwimmingPoolTemperatureSensorOnCircuitCOVPComponent
-ovp:UnknownOVPComponent
-profalux868:Profalux868ExteriorScreen
-profalux868:Profalux868ExteriorScreenPlatinum
-profalux868:Profalux868ExteriorVenetianBlind
-profalux868:Profalux868ExteriorVenetianBlindPlatinum
-profalux868:Profalux868RollerShutter
-profalux868:Profalux868RollerShutterPlatinum
-ramses:EvoHomeController
-ramses:EvoHomeControllerSensor
-ramses:EvoHomeDHWSensor
-ramses:EvoHomeDHWSetPoint
-ramses:EvoHomeZoneSensor
-ramses:EvoHomeZoneSetPoint
-ramses:RamsesOpenThermGateway
-rtd:AlarmRTDComponent
-rtd:IndoorSirenRTDComponent
-rtd:MotionOrContactSensorRTDComponent
-rtd:OutdoorSirenRTDComponent
-rtd:UnknownRTDComponent
-rtds:RTDSCOSensor
-rtds:RTDSContactSensor
-rtds:RTDSGasSensor
-rtds:RTDSMotionSensor
-rtds:RTDSPowerFailure
-rtds:RTDSRemoteControllerComponent
-rtds:RTDSSmokeSensor
-rtds:RTDSTemperatureSensor
-rtds:RTDSWaterSensor
-rtds:SirenStatusRTDSComponent
-rtds:UnknownRTDSComponent
-rts:BlindRTSComponent
-rts:CurtainRTSComponent
-rts:DimmableLightRTSComponent
-rts:DualCurtainRTSComponent
-rts:ExteriorBlindRTSComponent
-rts:ExteriorHeatingRTSComponent
-rts:ExteriorVenetianBlindRTSComponent
-rts:GarageDoor4TRTSComponent
-rts:GarageDoorRTSComponent
-rts:GarageDoorWithVentilationPositionRTSComponent
-rts:GateOpenerRTS4TComponent
-rts:GateOpenerRTSComponent
-rts:GateOpenerWithPedestrianPositionRTSComponent
-rts:Generic4TRTSComponent
-rts:GenericRTSComponent
-rts:HorizontalAwningRTSComponent
-rts:LightRTSComponent
-rts:OnOffRTSComponent
-rts:RollerShutterRTSComponent
-rts:SlidingGarageDoor4TRTSComponent
-rts:SlidingGarageDoorRTSComponent
-rts:SlidingGarageDoorWithPedestrianPositionRTSComponent
-rts:SlidingGateOpener4TRTSComponent
-rts:SlidingGateOpenerRTSComponent
-rts:SlidingGateOpenerWithPedestrianPositionRTSComponent
-rts:SwingingShutterRTSComponent
-rts:ThermostatRTSComponent
-rts:UnknowRTSComponent
-rts:VenetianBlindRTSComponent
-rts:WindowsOpenerRTSComponent
-somfythermostat:SomfyThermostatHumiditySensor
-somfythermostat:SomfyThermostatTemperatureSensor
-somfythermostat:SomfyThermostatThermostatComponent
-upnpcontrol:GenericSonosPlayerComponent
-upnpcontrol:SonosConnectAMPComponent
-upnpcontrol:SonosConnectComponent
-upnpcontrol:SonosOneComponent
-upnpcontrol:SonosPlayBarComponent
-upnpcontrol:SonosPlayBaseComponent
-upnpcontrol:SonosPlayFiveComponent
-upnpcontrol:SonosPlayOneComponent
-upnpcontrol:SonosSubComponent
-upnpcontrol:UPnPControlMediaRendererComponent
-verisure:GenericAlarmPanel
-wiser:CookingCumulatedElectricalEnergyConsumptionSensor
-wiser:CoolingCumulatedElectricalEnergyConsumptionSensor
-wiser:DHWCumulatedElectricalEnergyConsumptionSensor
-wiser:HeatingCumulatedElectricalEnergyConsumptionSensor
-wiser:OtherCumulatedElectricalEnergyConsumptionSensor
-wiser:PlugsCumulatedElectricalEnergyConsumptionSensor
-wiser:TotalColdWaterConsumptionSensor
-wiser:TotalElectricalEnergyConsumptionSensor
-wiser:TotalElectricalEnergyProductionSensor
-wiser:TotalGasConsumptionSensor
-wiser:TotalHotWaterConsumptionSensor
-wiser:TotalThermalEnergyConsumptionSensor
-zigbee:AirQualityBasedOnTemperatureHumiditySensorComponent
-zigbee:AwningComponent
-zigbee:BallastComponent
-zigbee:CIEColorSpaceXYLightComponent
-zigbee:CarbonMonoxideSensorComponent
-zigbee:CleodeZPilotComponent
-zigbee:ColorLightComponent
-zigbee:ColorLoopCIEXYLightComponent
-zigbee:ColorLoopColorTemperatureLightComponent
-zigbee:ColorLoopLightComponent
-zigbee:ColorTemperatureCIEXYLightComponent
-zigbee:ColorTemperatureLightComponent
-zigbee:CurtainComponent
-zigbee:DanfossIconModuleComponent
-zigbee:DoorSensorComponent
-zigbee:ElectricalEnergyConsumptionSensorComponent
-zigbee:ElectricalPowerConsumptionSensorComponent
-zigbee:EnhancedHueCIEXYLightComponent
-zigbee:EnhancedHueColorTemperatureLightComponent
-zigbee:EnhancedHueLightComponent
-zigbee:EnhancedHueXYTemperatureLightComponent
-zigbee:GlobalAirQualityComponent
-zigbee:GroupCIEColorSpaceXYLightComponent
-zigbee:GroupColorLightComponent
-zigbee:GroupColorLoopCIEXYLightComponent
-zigbee:GroupColorLoopColorTemperatureLightComponent
-zigbee:GroupColorLoopLightComponent
-zigbee:GroupColorTemperatureCIEXYLightComponent
-zigbee:GroupColorTemperatureLightComponent
-zigbee:GroupConfigurationComponent
-zigbee:GroupEnhancedHueCIEXYLightComponent
-zigbee:GroupEnhancedHueColorTemperatureLightComponent
-zigbee:GroupEnhancedHueLightComponent
-zigbee:GroupEnhancedHueXYTemperatureLightComponent
-zigbee:GroupHueCIEXYLightComponent
-zigbee:GroupHueColorTemperatureLightComponent
-zigbee:GroupHueLightComponent
-zigbee:GroupHueXYTemperatureLightComponent
-zigbee:GroupLightComponent
-zigbee:GroupOnOffComponent
-zigbee:GroupOnOffLightComponent
-zigbee:GroupSomfyCurtainComponent
-zigbee:GroupSomfyInteriorBlindComponent
-zigbee:GroupSomfyRollerShadeComponent
-zigbee:HueCIEXYLightComponent
-zigbee:HueColorTemperatureLightComponent
-zigbee:HueLightComponent
-zigbee:HueXYTemperatureLightComponent
-zigbee:HumidityComfortSensorComponent
-zigbee:InteriorBlindComponent
-zigbee:LightComponent
-zigbee:MotionSensorComponent
-zigbee:NexelecSmokeSensorComponent
-zigbee:OnOffComponent
-zigbee:OnOffLightComponent
-zigbee:ProfaluxExteriorVenetianBlindComponent
-zigbee:ProfaluxGenericComponent
-zigbee:ProfaluxInteriorBlindComponent
-zigbee:ProfaluxRollerShutterComponent
-zigbee:ProfaluxTransceiverComponent
-zigbee:RelativeHumidityComponent
-zigbee:RollerShadeComponent
-zigbee:RollerShutterComponent
-zigbee:SchneiderBallastComponent
-zigbee:SchneiderSwitchConfigurationComponent
-zigbee:SmokeSensorComponent
-zigbee:SomfyConfigurationToolComponent
-zigbee:SomfyCurtainComponent
-zigbee:SomfyInteriorBlindComponent
-zigbee:SomfyRollerShadeComponent
-zigbee:StackComponent
-zigbee:StackV3Component
-zigbee:TemperatureSensorComponent
-zigbee:TransceiverV3_0Component
-zigbee:WaterLeakageSensorComponent
-zigbee:ZigbeeNetworkNode
-zigbee:ZigbeeRemoteComponent
-zigbee:ZigbeeSomfyIRBlasterComponent
-zigbee:ZigbeeSomfyRemoteComponent
-zwave:AeotecControllerConfigurationZWaveComponent
-zwave:AeotecHeavyDutySmartSwitchConfigurationZWaveComponent
-zwave:AeotecHomeEnergyMeterMainConfigurationZWaveComponent
-zwave:AeotecInWallSmartSwitchConfigurationZWaveComponent
-zwave:AeotecNanoDimmerConfigurationZWaveComponent
-zwave:AssociationClassV2Component
-zwave:BasicClassComponent
-zwave:BatteryClassV1Component
-zwave:BatteryConfigurationZWaveComponent
-zwave:BinarySwitchClassV1Component
-zwave:CentralSceneClassV1Component
-zwave:ConfigurationV1ClassComponent
-zwave:DanfossHydronicControllerThermostatZWaveComponent
-zwave:DanfossHydronicControllerZWaveComponent
-zwave:DanfossRSLinkConfigurationZWaveComponent
-zwave:DimmableLightZWaveComponent
-zwave:DimmableOnOffZWaveComponent
-zwave:DoorLockClassOpenCloseSensorComponent
-zwave:DoorLockClassV1Component
-zwave:DoorLockClassV2Component
-zwave:DynamicNodeComponent
-zwave:FibaroDoorWindowSensorConfigurationZWaveComponent
-zwave:FibaroGateRollerShutterConfigurationZWaveComponent
-zwave:FibaroGateWithoutPositionRollerShutterConfigurationZWaveComponent
-zwave:FibaroVenetianRollerShutterConfigurationZWaveComponent
-zwave:FibaroWithPositionRollerShutterConfigurationZWaveComponent
-zwave:FibaroWithoutPositionRollerShutterConfigurationZWaveComponent
-zwave:GateWithoutPositionZWaveComponent
-zwave:GateZWaveComponent
-zwave:HeatitThermostatConfigurationZWaveComponent
-zwave:HeatitZWaterConfigurationZWaveComponent
-zwave:IDLockConfigurationZWaveComponent
-zwave:IDLockZWaveComponent
-zwave:IndicatorClassV1Component
-zwave:MeterClassV1Component
-zwave:MeterClassV2Component
-zwave:MeterClassV3Component
-zwave:MultiChannelAssociationClassV2Component
-zwave:MultilevelSensorClassV1Component
-zwave:MultilevelSensorClassV2Component
-zwave:MultilevelSensorClassV3Component
-zwave:MultilevelSensorClassV4Component
-zwave:MultilevelSwitchClassV1Component
-zwave:MultilevelWireZWaveComponent
-zwave:NodeComponent
-zwave:NodonRemoteControllerConfigurationZWaveComponent
-zwave:NotificationClassV2V8Component
-zwave:OnOffButtonZWaveComponent
-zwave:OnOffLightZWaveComponent
-zwave:OnOffZWaveComponent
-zwave:PowerLevelClassV1Component
-zwave:ProgrammableAndProtectableThermostatSetPointZWaveComponent
-zwave:QubinoDINPilotWireConfigurationZWaveComponent
-zwave:QubinoFlush1DRelayConfigurationZWaveComponent
-zwave:QubinoFlush2DRelayConfigurationZWaveComponent
-zwave:QubinoFlushDimmerConfigurationZWaveComponent
-zwave:QubinoFlushShutterMainConfigurationComponent
-zwave:QubinoFlushShutterTemperatureConfigurationComponent
-zwave:QubinoFlushShutterVenetianConfigurationComponent
-zwave:QubinoMiniDimmerConfigurationZWaveComponent
-zwave:QubinoRollerShutterConfigurationZWaveComponent
-zwave:QubinoVenitianRollerShutterConfigurationZWaveComponent
-zwave:RepeaterZWaveComponent
-zwave:RockerSwitchx2WithBatteryZWaveComponent
-zwave:RockerSwitchx2ZWaveComponent
-zwave:RollerShutterV3Component
-zwave:RollerShutterWithoutPositionZWaveComponent
-zwave:RollerShutterZWaveComponent
-zwave:SEDeviceCentralSceneZWaveComponent
-zwave:SEDevicesDimmerConfigurationZWaveComponent
-zwave:SEDevicesMultiSwitchConfigurationZWaveComponent
-zwave:SEDevicesRelayConfigurationZWaveComponent
-zwave:SEDevicesThermostatConfigurationZWaveComponent
-zwave:SEDevicesWheelControllerConfigurationZWaveComponent
-zwave:SensativeStripsGuardsConfiguration
-zwave:SlatsOrientationV3Component
-zwave:TemperatureSensorV7Component
-zwave:ThermostatModeClassV1Component
-zwave:ThermostatModeClassV2Component
-zwave:ThermostatModeClassV3Component
-zwave:ThermostatSetPointClassV1Component
-zwave:ThermostatSetPointClassV2Component
-zwave:ThermostatSetPointGenericZWaveComponent
-zwave:TransceiverZWaveComponent
-zwave:VenetianBlindZWaveComponent
-zwave:WakeUpClassV1Component
-zwave:WakeUpClassV2Component
-zwave:ZWaveLightSensor
-zwave:ZWaveMoistureSensor
-zwave:ZWaveNotificationCO2Sensor
-zwave:ZWaveNotificationCOSensor
-zwave:ZWaveNotificationContactSensor
-zwave:ZWaveNotificationHeatSensor
-zwave:ZWaveNotificationHomeSecuritySensor
-zwave:ZWaveNotificationMotionSensor
-zwave:ZWaveNotificationSmokeSensor
-zwave:ZWaveNotificationWaterSensor
-zwave:ZWaveSmokeSensor
-zwave:ZWaveTemperatureSensor
diff --git a/docs/contribute.md b/docs/contribute.md
new file mode 100644
index 00000000..41b3d79a
--- /dev/null
+++ b/docs/contribute.md
@@ -0,0 +1,50 @@
+# Contribute
+
+Thanks for contributing to pyOverkiz! This project powers production integrations and values careful changes, clear tests, and thoughtful documentation.
+
+## Development setup
+
+```bash
+uv sync
+```
+
+Install git hooks:
+
+```bash
+uv run prek install
+```
+
+## Run the docs locally
+
+```bash
+uv run mkdocs serve
+```
+
+Docs will be available at http://localhost:8000.
+
+```bash
+uv run mkdocs build
+```
+
+## Tests and linting
+
+```bash
+uv run pytest
+```
+
+```bash
+uv run prek run --all-files
+```
+
+## Project guidelines
+
+- Use Python 3.10+ features and type annotations.
+- Keep absolute imports and avoid relative imports.
+- Preserve existing comments and logging unless your change requires updates.
+- Prefer small, focused changes and add tests when behavior changes.
+
+## Submitting changes
+
+1. Fork the repository and create a new feature branch for your changes.
+2. Ensure all linting and tests pass locally before submitting.
+3. Open a pull request with a clear description and context.
diff --git a/docs/core-concepts.md b/docs/core-concepts.md
new file mode 100644
index 00000000..901ea942
--- /dev/null
+++ b/docs/core-concepts.md
@@ -0,0 +1,44 @@
+# Core concepts
+
+## Servers and gateways
+
+A server describes where the API calls go. A gateway is the physical hub in your home. Cloud servers route through vendor infrastructure; local servers talk directly to the gateway on your network.
+
+## Setup and devices
+
+The setup describes the current gateway configuration and device inventory. Devices expose metadata like `uiClass` and `widget`, plus a list of current `states`.
+
+## Actions, action groups, and commands
+
+Commands are sent as `Action` objects, grouped into an action group. Each action targets a device URL and a set of commands with parameters.
+
+## States
+
+States are name/value pairs that represent the current device status, such as closure position or temperature.
+
+## Events and listeners
+
+The API uses an event listener that you register once per session. Fetching events drains the server-side buffer.
+
+## Execution model
+
+Commands are executed asynchronously by the platform. You can poll execution state via events or refresh device states after a delay.
+
+## Relationship diagram
+
+```
+Client
+ |
+ |-- Server (cloud or local)
+ |
+ |-- Gateway
+ |
+ |-- Setup
+ | |
+ | |-- Devices
+ | |
+ | |-- States
+ | |-- Actions -> Commands -> Parameters
+ |
+ |-- Event Listener -> Events
+```
diff --git a/docs/device-control.md b/docs/device-control.md
new file mode 100644
index 00000000..50467f99
--- /dev/null
+++ b/docs/device-control.md
@@ -0,0 +1,102 @@
+# Device control
+
+All examples assume you are authenticated and have an active session. For details on setting up authentication, see the [Getting Started](getting-started.md) guide.
+
+## List devices
+
+```python
+devices = await client.get_devices()
+
+for device in devices:
+ print(device.label, device.controllable_name)
+
+ # Access device metadata, including available commands, states, and identifiers:
+ print(device.definition)
+```
+
+## Read a state value
+
+```python
+from pyoverkiz.enums import OverkizState
+
+devices = await client.get_devices()
+
+# For demo purposes we take the first available device
+device = devices[0]
+
+availability_state = next(
+ (state for state in device.states if state.name == OverkizState.CORE_AVAILABILITY),
+ None,
+)
+
+print(availability_state.value if availability_state else None)
+```
+
+## Send a command
+
+```python
+from pyoverkiz.enums import OverkizCommand
+from pyoverkiz.models import Action, Command
+
+await client.execute_action_group(
+ actions=[
+ Action(
+ device_url="io://1234-5678-1234/12345678",
+ commands=[
+ Command(
+ name=OverkizCommand.SET_CLOSURE,
+ parameters=[50],
+ )
+ ],
+ )
+ ],
+ label="Execution: set closure",
+)
+```
+
+## Action groups and common patterns
+
+- Use a single action group to batch multiple device commands.
+
+```python
+from pyoverkiz.enums import OverkizCommand
+from pyoverkiz.models import Action, Command
+
+await client.execute_action_group(
+ actions=[
+ Action(
+ device_url="io://1234-5678-1234/12345678",
+ commands=[
+ Command(
+ name=OverkizCommand.SET_DEROGATION,
+ parameters=[21.5, OverkizCommandParam.FURTHER_NOTICE],
+ )
+ ],
+ ),
+ Action(
+ device_url="io://1234-5678-1234/12345678",
+ commands=[
+ Command(
+ name=OverkizCommand.SET_MODE_TEMPERATURE,
+ parameters=[OverkizCommandParam.MANUAL_MODE, 21.5],
+ )
+ ],
+ )
+ ],
+ label="Execution: multiple commands",
+)
+
+## Limitations and rate limits
+
+Gateways impose limits on how many executions can run or be queued simultaneously. If the execution queue is full, the API will raise an `ExecutionQueueFullException`. Most gateways allow up to 10 concurrent executions.
+
+### Action queue (batching across calls)
+
+If you trigger many action groups in rapid succession, you can enable the action
+queue to batch calls within a short window. This reduces API calls and helps
+avoid rate limits. See the [Action queue](action-queue.md) guide for setup,
+advanced settings, and usage patterns.
+
+### Polling vs event-driven
+
+Polling with `get_devices()` is straightforward but may introduce latency and increase server load. For more immediate updates, consider using event listeners. Refer to the [event handling](event-handling.md) guide for implementation details.
diff --git a/docs/error-handling.md b/docs/error-handling.md
new file mode 100644
index 00000000..738e7b1d
--- /dev/null
+++ b/docs/error-handling.md
@@ -0,0 +1,56 @@
+# Error handling
+
+## Common exceptions
+
+- `NotAuthenticatedException`
+- `TooManyRequestsException`
+- `TooManyConcurrentRequestsException`
+- `TooManyExecutionsException`
+- `MaintenanceException`
+- `AccessDeniedToGatewayException`
+- `BadCredentialsException`
+
+## Retry and backoff guidance
+
+Use short, jittered delays for transient errors and re-authenticate on auth failures.
+
+```python
+import asyncio
+
+from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.enums import Server
+from pyoverkiz.exceptions import (
+ NotAuthenticatedException,
+ TooManyConcurrentRequestsException,
+ TooManyRequestsException,
+)
+
+
+async def fetch_devices_with_retry() -> None:
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+ for attempt in range(5):
+ try:
+ devices = await client.get_devices()
+ print(devices)
+ return
+ except (TooManyRequestsException, TooManyConcurrentRequestsException):
+ await asyncio.sleep(0.5 * (attempt + 1))
+ except NotAuthenticatedException:
+ await client.login()
+
+
+asyncio.run(fetch_devices_with_retry())
+```
+
+## SSL and local certificates
+
+If you use local gateways with `.local` hostnames, you may need `verify_ssl=False` when a trusted certificate is not available.
+
+## Timeouts
+
+For long-running operations, prefer shorter request timeouts with retries rather than a single long timeout.
diff --git a/docs/event-handling.md b/docs/event-handling.md
new file mode 100644
index 00000000..6bb359e4
--- /dev/null
+++ b/docs/event-handling.md
@@ -0,0 +1,101 @@
+# Event handling
+
+## Register and unregister a listener
+
+```python
+import asyncio
+
+from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.enums import Server
+
+
+async def main() -> None:
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+ await client.register_event_listener()
+ await client.unregister_event_listener()
+
+
+asyncio.run(main())
+```
+
+## Fetch events with backoff
+
+```python
+import asyncio
+
+from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.enums import Server
+from pyoverkiz.exceptions import (
+ InvalidEventListenerIdException,
+ NoRegisteredEventListenerException,
+)
+
+
+async def main() -> None:
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+ await client.register_event_listener()
+
+ while True:
+ try:
+ events = await client.fetch_events()
+ except (InvalidEventListenerIdException, NoRegisteredEventListenerException):
+ await asyncio.sleep(1)
+ await client.register_event_listener()
+ continue
+
+ for event in events:
+ print(event)
+
+ await asyncio.sleep(2)
+
+
+asyncio.run(main())
+```
+
+## Update an in-memory state map
+
+```python
+import asyncio
+
+from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.enums import Server
+
+
+async def main() -> None:
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+ await client.register_event_listener()
+
+ state_map: dict[str, dict[str, object]] = {}
+
+ while True:
+ events = await client.fetch_events()
+ for event in events:
+ device_id = event.device_id
+ state_map.setdefault(device_id, {})[event.state_name] = event.state_value
+
+ await asyncio.sleep(2)
+
+
+asyncio.run(main())
+```
+
+## Reconnect tips
+
+- Re-register the listener when you see `InvalidEventListenerIdException`.
+- Poll occasionally if your network has unstable connectivity.
+- Keep the fetch loop alive to avoid listener timeout.
diff --git a/docs/getting-started.md b/docs/getting-started.md
new file mode 100644
index 00000000..962f0fe3
--- /dev/null
+++ b/docs/getting-started.md
@@ -0,0 +1,151 @@
+# Getting started
+
+This guide will help you install the library, connect to your hub, and perform your first actions.
+
+## Prerequisites
+
+- Python 3.10+
+- An OverKiz-compatible hub and account
+
+## Install pyOverkiz from PyPI
+
+### With UV recommended
+
+```bash
+uv add pyoverkiz
+```
+
+### With pip
+
+```bash
+pip install pyoverkiz
+```
+
+## Choose your server
+
+Use a cloud server when you want to connect through the vendor’s public API. Use a local server when you want LAN access to a gateway.
+
+- Cloud servers use the `Server` enum.
+- Local servers use `create_local_server_config` with a hostname or IP address.
+
+## Authentication
+
+=== "Somfy (cloud)"
+
+ Authentication to the Somfy cloud requires your mobile app username and password and your region.
+
+ Use `Server.SOMFY_EUROPE`, `Server.SOMFY_AMERICA`, or `Server.SOMFY_OCEANIA` with `UsernamePasswordCredentials` to select your region and authenticate.
+
+ ```python
+ import asyncio
+
+ from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+ from pyoverkiz.client import OverkizClient
+ from pyoverkiz.enums import Server
+
+
+ async def main() -> None:
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+
+ asyncio.run(main())
+ ```
+
+=== "Somfy (local)"
+
+ Local authentication requires a token generated via the official mobile app. For details on obtaining a token, refer to [Somfy TaHoma Developer Mode](https://github.com/Somfy-Developer/Somfy-TaHoma-Developer-Mode).
+
+ Use the helper function `create_local_server_config` to create a `Server` with `LocalTokenCredentials` to provide your token.
+
+ ```python
+ import asyncio
+
+ from pyoverkiz.auth.credentials import LocalTokenCredentials
+ from pyoverkiz.client import OverkizClient
+ from pyoverkiz.utils import create_local_server_config
+
+
+ async def main() -> None:
+ async with OverkizClient(
+ server=create_local_server_config(host="gateway-xxxx-xxxx-xxxx.local"),
+ credentials=LocalTokenCredentials("token-from-your-mobile-app"),
+ verify_ssl=True, # disable if you connect via IP
+ ) as client:
+ await client.login()
+
+ asyncio.run(main())
+ ```
+
+=== "Cozytouch (cloud)"
+
+ Authentication to the Cozytouch cloud requires your mobile app username and password and your vendor.
+
+ Use `Server.ATLANTIC_COZYTOUCH`, `Server.SAUTER_COZYTOUCH`, or `Server.THERMOR_COZYTOUCH` with `UsernamePasswordCredentials` to select your vendor and authenticate.
+
+ ```python
+ import asyncio
+
+ from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+ from pyoverkiz.client import OverkizClient
+ from pyoverkiz.enums import Server
+
+
+ async def main() -> None:
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+
+ asyncio.run(main())
+ ```
+
+=== "Hitachi Hi Kumo (cloud)"
+
+ Authentication to the Hitachi Hi Kumo cloud requires your mobile app username and password and your region.
+
+ Use `Server.HI_KUMO_ASIA`, `Server.HI_KUMO_EUROPE`, or `Server.HI_KUMO_OCEANIA` with `UsernamePasswordCredentials` to select your region and authenticate.
+
+ ```python
+ import asyncio
+
+ from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+ from pyoverkiz.client import OverkizClient
+ from pyoverkiz.enums import Server
+
+
+ async def main() -> None:
+ async with OverkizClient(
+ server=Server.HI_KUMO_EUROPE,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+
+ asyncio.run(main())
+ ```
+
+=== "Rexel (cloud)"
+
+ Authentication to the Rexel cloud requires your mobile app username and password.
+
+ Use `Server.REXEL` with `UsernamePasswordCredentials` to authenticate.
+
+ ```python
+ import asyncio
+
+ from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+ from pyoverkiz.client import OverkizClient
+ from pyoverkiz.enums import Server
+
+ async def main() -> None:
+ async with OverkizClient(
+ server=Server.REXEL,
+ credentials=UsernamePasswordCredentials("you@example.com", "password"),
+ ) as client:
+ await client.login()
+
+ asyncio.run(main())
+ ```
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..d07f685f
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,37 @@
+---
+hide:
+ - navigation
+ - toc
+ - title
+---
+
+
+pyOverkiz is an async Python library for interacting with Overkiz-based platforms, including Somfy and Atlantic. It enables authentication, device discovery, state reading, command execution, and real-time event streaming from supported gateways.
+
+## What you can do
+
+- Authenticate with Overkiz cloud or local gateways
+- Automatically discover and list connected devices
+- Read device states and attributes
+- Execute commands on devices
+- Receive real-time updates via event streams
+
+## Supported hubs
+
+- Atlantic Cozytouch
+- Bouygues Flexom
+- Hitachi Hi Kumo
+- Nexity Eugénie
+- Sauter Cozytouch
+- Simu (LiveIn2)
+- Somfy Connexoon IO
+- Somfy Connexoon RTS
+- Somfy TaHoma
+- Somfy TaHoma Switch
+- Thermor Cozytouch
+
+
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
new file mode 100644
index 00000000..4508af6c
--- /dev/null
+++ b/docs/troubleshooting.md
@@ -0,0 +1,40 @@
+# Troubleshooting
+
+## Cloud vs local connectivity
+
+- Cloud servers require internet access and valid vendor credentials.
+- Local servers require direct access to the gateway on your LAN.
+
+## SSL and .local hostnames
+
+If the gateway uses a self-signed certificate, pass `verify_ssl=False` when creating `OverkizClient` for local access.
+
+## Authentication failures
+
+- Confirm the server matches your vendor region.
+- Re-run `login()` and retry the call.
+
+## Rate limits and concurrency
+
+- Reduce polling frequency.
+- Back off on `TooManyRequestsException` or `TooManyConcurrentRequestsException`.
+
+## Listener drops
+
+- Re-register the event listener when you see `InvalidEventListenerIdException`.
+- Ensure your fetch loop is running every few seconds.
+
+## Device not found
+
+- Refresh setup with `get_setup()` and re-fetch devices.
+- Confirm you are using the correct gateway and server.
+
+## Logging tips
+
+Enable debug logging in your application to inspect request/response details.
+
+```python
+import logging
+
+logging.basicConfig(level=logging.DEBUG)
+```
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 00000000..2a8508e7
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,54 @@
+site_name: pyOverkiz Documentation
+repo_url: https://github.com/iMicknl/python-overkiz-api
+edit_uri: edit/main/docs/
+
+docs_dir: docs
+copyright: This project is not affiliated with Overkiz, Somfy, or any other mentioned brand. All trademarks and brand names are the property of their respective owners.
+
+extra:
+ generator: false
+
+theme:
+ name: material
+ features:
+ - navigation.tabs
+ - toc.follow
+ - search.suggest
+ - search.highlight
+ - navigation.footer
+
+plugins:
+ - search
+ - autorefs
+ - mkdocstrings:
+ handlers:
+ python:
+ options:
+ show_source: true
+ docstring_style: google
+ show_docstring_attributes: true
+ show_signature: true
+ show_signature_annotations: true
+ signature_crossrefs: true
+
+markdown_extensions:
+ - pymdownx.highlight
+ - pymdownx.superfences
+ - pymdownx.tasklist
+ - admonition
+ - pymdownx.superfences
+ - pymdownx.tabbed:
+ alternate_style: true
+
+nav:
+ - Overview: index.md
+ - User Guide:
+ - Getting started: getting-started.md
+ - Core concepts: core-concepts.md
+ - Device control: device-control.md
+ - Action queue: action-queue.md
+ - Event handling: event-handling.md
+ - Error handling: error-handling.md
+ - Troubleshooting: troubleshooting.md
+ - API Reference: api-reference.md
+ - Contribute: contribute.md
diff --git a/pyoverkiz/action_queue.py b/pyoverkiz/action_queue.py
new file mode 100644
index 00000000..57ae5c49
--- /dev/null
+++ b/pyoverkiz/action_queue.py
@@ -0,0 +1,324 @@
+"""Action queue for batching multiple action executions into single API calls."""
+
+from __future__ import annotations
+
+import asyncio
+import contextlib
+from collections.abc import Callable, Coroutine, Generator
+from dataclasses import dataclass
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING:
+ from pyoverkiz.enums import CommandMode
+ from pyoverkiz.models import Action
+
+
+@dataclass(frozen=True, slots=True)
+class ActionQueueSettings:
+ """Settings for configuring the action queue behavior."""
+
+ delay: float = 0.5
+ max_actions: int = 20
+
+ def validate(self) -> None:
+ """Validate configuration values for the action queue."""
+ if self.delay <= 0:
+ raise ValueError(f"action_queue_delay must be positive, got {self.delay!r}")
+ if self.max_actions < 1:
+ raise ValueError(
+ f"action_queue_max_actions must be at least 1, got {self.max_actions!r}"
+ )
+
+
+class QueuedExecution:
+ """Represents a queued action execution that will resolve to an exec_id when the batch executes."""
+
+ def __init__(self) -> None:
+ """Initialize the queued execution."""
+ # Future is created lazily to ensure it is bound to the running event loop.
+ # Creating it in __init__ would fail if no loop is running yet.
+ self._future: asyncio.Future[str] | None = None
+
+ def _ensure_future(self) -> asyncio.Future[str]:
+ """Create the underlying future lazily, bound to the running event loop."""
+ # This method is the single point of future creation to guarantee
+ # consistent loop binding for callers that await or set results later.
+ if self._future is None:
+ loop = asyncio.get_running_loop()
+ self._future = loop.create_future()
+ return self._future
+
+ def set_result(self, exec_id: str) -> None:
+ """Set the execution ID result."""
+ future = self._ensure_future()
+ if not future.done():
+ future.set_result(exec_id)
+
+ def set_exception(self, exception: BaseException) -> None:
+ """Set an exception if the batch execution failed."""
+ future = self._ensure_future()
+ if not future.done():
+ future.set_exception(exception)
+
+ def is_done(self) -> bool:
+ """Check if the execution has completed (either with result or exception)."""
+ return self._future.done() if self._future is not None else False
+
+ def __await__(self) -> Generator[Any, None, str]:
+ """Make this awaitable."""
+ return self._ensure_future().__await__()
+
+
+class ActionQueue:
+ """Batches multiple action executions into single API calls.
+
+ When actions are added, they are held for a configurable delay period.
+ If more actions arrive during this window, they are batched together.
+ The batch is flushed when:
+ - The delay timer expires
+ - The max actions limit is reached
+ - The command mode changes
+ - The label changes
+ - Manual flush is requested
+ """
+
+ def __init__(
+ self,
+ executor: Callable[
+ [list[Action], CommandMode | None, str | None], Coroutine[None, None, str]
+ ],
+ delay: float = 0.5,
+ max_actions: int = 20,
+ ) -> None:
+ """Initialize the action queue.
+
+ :param executor: Async function to execute batched actions
+ :param delay: Seconds to wait before auto-flushing (default 0.5)
+ :param max_actions: Maximum actions per batch before forced flush (default 20)
+ """
+ self._executor = executor
+ self._delay = delay
+ self._max_actions = max_actions
+
+ self._pending_actions: list[Action] = []
+ self._pending_mode: CommandMode | None = None
+ self._pending_label: str | None = None
+ self._pending_waiters: list[QueuedExecution] = []
+
+ self._flush_task: asyncio.Task[None] | None = None
+ self._lock = asyncio.Lock()
+
+ async def add(
+ self,
+ actions: list[Action],
+ mode: CommandMode | None = None,
+ label: str | None = None,
+ ) -> QueuedExecution:
+ """Add actions to the queue.
+
+ When multiple actions target the same device, their commands are merged
+ into a single action to respect the gateway limitation of one action per
+ device in each action group.
+
+ :param actions: Actions to queue
+ :param mode: Command mode (will flush if different from pending mode)
+ :param label: Label for the action group
+ :return: QueuedExecution that resolves to exec_id when batch executes
+ """
+ batches_to_execute: list[
+ tuple[list[Action], CommandMode | None, str | None, list[QueuedExecution]]
+ ] = []
+
+ if not actions:
+ raise ValueError("actions must contain at least one Action")
+
+ normalized_actions: list[Action] = []
+ normalized_index: dict[str, Action] = {}
+ for action in actions:
+ existing = normalized_index.get(action.device_url)
+ if existing is None:
+ normalized_actions.append(action)
+ normalized_index[action.device_url] = action
+ else:
+ existing.commands.extend(action.commands)
+
+ async with self._lock:
+ # If mode or label changes, flush existing queue first
+ if self._pending_actions and (
+ mode != self._pending_mode or label != self._pending_label
+ ):
+ batches_to_execute.append(self._prepare_flush())
+
+ # Add actions to pending queue
+ for action in normalized_actions:
+ pending = next(
+ (
+ pending_action
+ for pending_action in self._pending_actions
+ if pending_action.device_url == action.device_url
+ ),
+ None,
+ )
+ if pending is None:
+ self._pending_actions.append(action)
+ else:
+ pending.commands.extend(action.commands)
+ self._pending_mode = mode
+ self._pending_label = label
+
+ # Create waiter for this caller. This waiter is added to the current
+ # batch being built, even if we flushed a previous batch above due to
+ # a mode/label change. This ensures the waiter belongs to the batch
+ # containing the actions we just added.
+ waiter = QueuedExecution()
+ self._pending_waiters.append(waiter)
+
+ # If we hit max actions, flush immediately
+ if len(self._pending_actions) >= self._max_actions:
+ # Prepare the current batch for flushing (which includes the actions
+ # we just added). If we already flushed due to mode change, this is
+ # a second batch.
+ batches_to_execute.append(self._prepare_flush())
+ elif self._flush_task is None or self._flush_task.done():
+ # Schedule delayed flush if not already scheduled
+ self._flush_task = asyncio.create_task(self._delayed_flush())
+
+ # Execute batches outside the lock if we flushed
+ for batch in batches_to_execute:
+ if batch[0]:
+ await self._execute_batch(*batch)
+
+ return waiter
+
+ async def _delayed_flush(self) -> None:
+ """Wait for the delay period, then flush the queue."""
+ waiters: list[QueuedExecution] = []
+ try:
+ await asyncio.sleep(self._delay)
+ async with self._lock:
+ if not self._pending_actions:
+ return
+
+ # Take snapshot and clear state while holding lock
+ actions = self._pending_actions
+ mode = self._pending_mode
+ label = self._pending_label
+ waiters = self._pending_waiters
+
+ self._pending_actions = []
+ self._pending_mode = None
+ self._pending_label = None
+ self._pending_waiters = []
+ self._flush_task = None
+
+ # Execute outside the lock
+ await self._execute_batch(actions, mode, label, waiters)
+ except asyncio.CancelledError as exc:
+ # Ensure all waiters are notified if this task is cancelled
+ for waiter in waiters:
+ waiter.set_exception(exc)
+ raise
+
+ def _prepare_flush(
+ self,
+ ) -> tuple[list[Action], CommandMode | None, str | None, list[QueuedExecution]]:
+ """Prepare a flush by taking snapshot and clearing state (must be called with lock held).
+
+ Returns a tuple of (actions, mode, label, waiters) that should be executed
+ outside the lock using _execute_batch().
+ """
+ if not self._pending_actions:
+ return ([], None, None, [])
+
+ # Cancel any pending flush task
+ if self._flush_task and not self._flush_task.done():
+ self._flush_task.cancel()
+ self._flush_task = None
+
+ # Take snapshot of current batch
+ actions = self._pending_actions
+ mode = self._pending_mode
+ label = self._pending_label
+ waiters = self._pending_waiters
+
+ # Clear pending state
+ self._pending_actions = []
+ self._pending_mode = None
+ self._pending_label = None
+ self._pending_waiters = []
+
+ return (actions, mode, label, waiters)
+
+ async def _execute_batch(
+ self,
+ actions: list[Action],
+ mode: CommandMode | None,
+ label: str | None,
+ waiters: list[QueuedExecution],
+ ) -> None:
+ """Execute a batch of actions and notify waiters (must be called without lock)."""
+ if not actions:
+ return
+
+ try:
+ exec_id = await self._executor(actions, mode, label)
+ # Notify all waiters
+ for waiter in waiters:
+ waiter.set_result(exec_id)
+ except asyncio.CancelledError as exc:
+ # Propagate cancellation to all waiters, then re-raise.
+ for waiter in waiters:
+ waiter.set_exception(exc)
+ raise
+ except Exception as exc:
+ # Propagate exceptions to all waiters without swallowing system-level exits.
+ for waiter in waiters:
+ waiter.set_exception(exc)
+
+ async def flush(self) -> None:
+ """Force flush all pending actions immediately.
+
+ This method forces the queue to execute any pending batched actions
+ without waiting for the delay timer. The execution results are delivered
+ to the corresponding QueuedExecution objects returned by add().
+
+ This method is useful for forcing immediate execution without having to
+ wait for the delay timer to expire.
+ """
+ batch_to_execute = None
+ async with self._lock:
+ if self._pending_actions:
+ batch_to_execute = self._prepare_flush()
+
+ # Execute outside the lock
+ if batch_to_execute:
+ await self._execute_batch(*batch_to_execute)
+
+ def get_pending_count(self) -> int:
+ """Get the (approximate) number of actions currently waiting in the queue.
+
+ This method does not acquire the internal lock and therefore returns a
+ best-effort snapshot that may be slightly out of date if the queue is
+ being modified concurrently by other coroutines. Do not rely on this
+ value for critical control flow or for making flush decisions.
+ """
+ return len(self._pending_actions)
+
+ async def shutdown(self) -> None:
+ """Shutdown the queue, flushing any pending actions."""
+ batch_to_execute = None
+ async with self._lock:
+ if self._flush_task and not self._flush_task.done():
+ task = self._flush_task
+ task.cancel()
+ self._flush_task = None
+ # Wait for cancellation to complete
+ with contextlib.suppress(asyncio.CancelledError):
+ await task
+
+ if self._pending_actions:
+ batch_to_execute = self._prepare_flush()
+
+ # Execute outside the lock
+ if batch_to_execute:
+ await self._execute_batch(*batch_to_execute)
diff --git a/pyoverkiz/auth/__init__.py b/pyoverkiz/auth/__init__.py
new file mode 100644
index 00000000..535a2614
--- /dev/null
+++ b/pyoverkiz/auth/__init__.py
@@ -0,0 +1,24 @@
+"""Authentication module for pyoverkiz."""
+
+from __future__ import annotations
+
+from pyoverkiz.auth.base import AuthContext, AuthStrategy
+from pyoverkiz.auth.credentials import (
+ Credentials,
+ LocalTokenCredentials,
+ RexelOAuthCodeCredentials,
+ TokenCredentials,
+ UsernamePasswordCredentials,
+)
+from pyoverkiz.auth.factory import build_auth_strategy
+
+__all__ = [
+ "AuthContext",
+ "AuthStrategy",
+ "Credentials",
+ "LocalTokenCredentials",
+ "RexelOAuthCodeCredentials",
+ "TokenCredentials",
+ "UsernamePasswordCredentials",
+ "build_auth_strategy",
+]
diff --git a/pyoverkiz/auth/base.py b/pyoverkiz/auth/base.py
new file mode 100644
index 00000000..f4db7059
--- /dev/null
+++ b/pyoverkiz/auth/base.py
@@ -0,0 +1,42 @@
+"""Base classes for authentication strategies."""
+
+from __future__ import annotations
+
+import datetime
+from collections.abc import Mapping
+from dataclasses import dataclass
+from typing import Protocol
+
+
+@dataclass(slots=True)
+class AuthContext:
+ """Authentication context holding tokens and expiration."""
+
+ access_token: str | None = None
+ refresh_token: str | None = None
+ expires_at: datetime.datetime | None = None
+
+ def is_expired(self, *, skew_seconds: int = 5) -> bool:
+ """Check if the access token is expired, considering a skew time."""
+ if not self.expires_at:
+ return False
+
+ return datetime.datetime.now(
+ datetime.UTC
+ ) >= self.expires_at - datetime.timedelta(seconds=skew_seconds)
+
+
+class AuthStrategy(Protocol):
+ """Protocol for authentication strategies."""
+
+ async def login(self) -> None:
+ """Perform login to obtain tokens."""
+
+ async def refresh_if_needed(self) -> bool:
+ """Refresh tokens if they are expired. Return True if refreshed."""
+
+ def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
+ """Generate authentication headers for requests."""
+
+ async def close(self) -> None:
+ """Clean up any resources held by the strategy."""
diff --git a/pyoverkiz/auth/credentials.py b/pyoverkiz/auth/credentials.py
new file mode 100644
index 00000000..777f950b
--- /dev/null
+++ b/pyoverkiz/auth/credentials.py
@@ -0,0 +1,37 @@
+"""Credentials for authentication strategies."""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+
+class Credentials:
+ """Marker base class for auth credentials."""
+
+
+@dataclass(slots=True)
+class UsernamePasswordCredentials(Credentials):
+ """Credentials using username and password."""
+
+ username: str
+ password: str
+
+
+@dataclass(slots=True)
+class TokenCredentials(Credentials):
+ """Credentials using an (API) token."""
+
+ token: str
+
+
+@dataclass(slots=True)
+class LocalTokenCredentials(TokenCredentials):
+ """Credentials using a local API token."""
+
+
+@dataclass(slots=True)
+class RexelOAuthCodeCredentials(Credentials):
+ """Credentials using Rexel OAuth2 authorization code."""
+
+ code: str
+ redirect_uri: str
diff --git a/pyoverkiz/auth/factory.py b/pyoverkiz/auth/factory.py
new file mode 100644
index 00000000..c0901ae6
--- /dev/null
+++ b/pyoverkiz/auth/factory.py
@@ -0,0 +1,127 @@
+"""Factory to build authentication strategies based on server and credentials."""
+
+from __future__ import annotations
+
+import ssl
+
+from aiohttp import ClientSession
+
+from pyoverkiz.auth.credentials import (
+ Credentials,
+ LocalTokenCredentials,
+ RexelOAuthCodeCredentials,
+ TokenCredentials,
+ UsernamePasswordCredentials,
+)
+from pyoverkiz.auth.strategies import (
+ AuthStrategy,
+ BearerTokenAuthStrategy,
+ CozytouchAuthStrategy,
+ LocalTokenAuthStrategy,
+ NexityAuthStrategy,
+ RexelAuthStrategy,
+ SessionLoginStrategy,
+ SomfyAuthStrategy,
+)
+from pyoverkiz.enums import APIType, Server
+from pyoverkiz.models import ServerConfig
+
+
+def build_auth_strategy(
+ *,
+ server_config: ServerConfig,
+ credentials: Credentials,
+ session: ClientSession,
+ ssl_context: ssl.SSLContext | bool,
+) -> AuthStrategy:
+ """Build the correct auth strategy for the given server and credentials."""
+ server: Server | None = server_config.server
+
+ if server == Server.SOMFY_EUROPE:
+ return SomfyAuthStrategy(
+ _ensure_username_password(credentials),
+ session,
+ server_config,
+ ssl_context,
+ server_config.type,
+ )
+
+ if server in {
+ Server.ATLANTIC_COZYTOUCH,
+ Server.THERMOR_COZYTOUCH,
+ Server.SAUTER_COZYTOUCH,
+ }:
+ return CozytouchAuthStrategy(
+ _ensure_username_password(credentials),
+ session,
+ server_config,
+ ssl_context,
+ server_config.type,
+ )
+
+ if server == Server.NEXITY:
+ return NexityAuthStrategy(
+ _ensure_username_password(credentials),
+ session,
+ server_config,
+ ssl_context,
+ server_config.type,
+ )
+
+ if server == Server.REXEL:
+ return RexelAuthStrategy(
+ _ensure_rexel(credentials),
+ session,
+ server_config,
+ ssl_context,
+ server_config.type,
+ )
+
+ if server_config.type == APIType.LOCAL:
+ if isinstance(credentials, LocalTokenCredentials):
+ return LocalTokenAuthStrategy(
+ credentials, session, server_config, ssl_context, server_config.type
+ )
+ return BearerTokenAuthStrategy(
+ _ensure_token(credentials),
+ session,
+ server_config,
+ ssl_context,
+ server_config.type,
+ )
+
+ if isinstance(credentials, TokenCredentials) and not isinstance(
+ credentials, LocalTokenCredentials
+ ):
+ return BearerTokenAuthStrategy(
+ credentials, session, server_config, ssl_context, server_config.type
+ )
+
+ return SessionLoginStrategy(
+ _ensure_username_password(credentials),
+ session,
+ server_config,
+ ssl_context,
+ server_config.type,
+ )
+
+
+def _ensure_username_password(credentials: Credentials) -> UsernamePasswordCredentials:
+ """Validate that credentials are username/password based."""
+ if not isinstance(credentials, UsernamePasswordCredentials):
+ raise TypeError("UsernamePasswordCredentials are required for this server.")
+ return credentials
+
+
+def _ensure_token(credentials: Credentials) -> TokenCredentials:
+ """Validate that credentials carry a bearer token."""
+ if not isinstance(credentials, TokenCredentials):
+ raise TypeError("TokenCredentials are required for this server.")
+ return credentials
+
+
+def _ensure_rexel(credentials: Credentials) -> RexelOAuthCodeCredentials:
+ """Validate that credentials are of Rexel OAuth code type."""
+ if not isinstance(credentials, RexelOAuthCodeCredentials):
+ raise TypeError("RexelOAuthCodeCredentials are required for this server.")
+ return credentials
diff --git a/pyoverkiz/auth/strategies.py b/pyoverkiz/auth/strategies.py
new file mode 100644
index 00000000..761ba49c
--- /dev/null
+++ b/pyoverkiz/auth/strategies.py
@@ -0,0 +1,471 @@
+"""Authentication strategies for Overkiz API."""
+
+from __future__ import annotations
+
+import asyncio
+import base64
+import binascii
+import datetime
+import json
+import ssl
+from collections.abc import Mapping
+from typing import Any, cast
+
+import boto3
+from aiohttp import ClientSession, FormData
+from botocore.client import BaseClient
+from botocore.config import Config
+from warrant_lite import WarrantLite
+
+from pyoverkiz.auth.base import AuthContext, AuthStrategy
+from pyoverkiz.auth.credentials import (
+ LocalTokenCredentials,
+ RexelOAuthCodeCredentials,
+ TokenCredentials,
+ UsernamePasswordCredentials,
+)
+from pyoverkiz.const import (
+ COZYTOUCH_ATLANTIC_API,
+ COZYTOUCH_CLIENT_ID,
+ NEXITY_API,
+ NEXITY_COGNITO_CLIENT_ID,
+ NEXITY_COGNITO_REGION,
+ NEXITY_COGNITO_USER_POOL,
+ REXEL_OAUTH_CLIENT_ID,
+ REXEL_OAUTH_SCOPE,
+ REXEL_OAUTH_TOKEN_URL,
+ REXEL_REQUIRED_CONSENT,
+ SOMFY_API,
+ SOMFY_CLIENT_ID,
+ SOMFY_CLIENT_SECRET,
+)
+from pyoverkiz.enums import APIType
+from pyoverkiz.exceptions import (
+ BadCredentialsException,
+ CozyTouchBadCredentialsException,
+ CozyTouchServiceException,
+ InvalidTokenException,
+ NexityBadCredentialsException,
+ NexityServiceException,
+ SomfyBadCredentialsException,
+ SomfyServiceException,
+)
+from pyoverkiz.models import ServerConfig
+
+
+class BaseAuthStrategy(AuthStrategy):
+ """Base class for authentication strategies."""
+
+ def __init__(
+ self,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Store shared auth context for Overkiz API interactions."""
+ self.session = session
+ self.server = server
+ self._ssl = ssl_context
+ self.api_type = api_type
+
+ async def login(self) -> None:
+ """Perform authentication; default is a no-op for subclasses to override."""
+ return None
+
+ async def refresh_if_needed(self) -> bool:
+ """Refresh authentication tokens if needed; default returns False."""
+ return False
+
+ def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
+ """Return authentication headers for a request path."""
+ return {}
+
+ async def close(self) -> None:
+ """Close any resources held by the strategy; default is no-op."""
+ return None
+
+
+class SessionLoginStrategy(BaseAuthStrategy):
+ """Authentication strategy using session-based login."""
+
+ def __init__(
+ self,
+ credentials: UsernamePasswordCredentials,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Initialize SessionLoginStrategy with given parameters."""
+ super().__init__(session, server, ssl_context, api_type)
+ self.credentials = credentials
+
+ async def login(self) -> None:
+ """Perform login using username and password."""
+ payload = {
+ "userId": self.credentials.username,
+ "userPassword": self.credentials.password,
+ }
+ await self._post_login(payload)
+
+ async def _post_login(self, data: Mapping[str, Any]) -> None:
+ """Post login data to the server and handle response."""
+ async with self.session.post(
+ f"{self.server.endpoint}login",
+ data=data,
+ ssl=self._ssl,
+ ) as response:
+ if response.status not in (200, 204):
+ raise BadCredentialsException(
+ f"Login failed for {self.server.name}: {response.status}"
+ )
+
+ # A 204 No Content response cannot have a body, so skip JSON parsing.
+ if response.status == 204:
+ return
+
+ result = await response.json()
+ if not result.get("success"):
+ raise BadCredentialsException("Login failed: bad credentials")
+
+
+class SomfyAuthStrategy(BaseAuthStrategy):
+ """Authentication strategy using Somfy OAuth2."""
+
+ def __init__(
+ self,
+ credentials: UsernamePasswordCredentials,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Initialize SomfyAuthStrategy with given parameters."""
+ super().__init__(session, server, ssl_context, api_type)
+ self.credentials = credentials
+ self.context = AuthContext()
+
+ async def login(self) -> None:
+ """Perform login using Somfy OAuth2."""
+ await self._request_access_token(
+ grant_type="password",
+ extra_fields={
+ "username": self.credentials.username,
+ "password": self.credentials.password,
+ },
+ )
+
+ async def refresh_if_needed(self) -> bool:
+ """Refresh Somfy OAuth2 tokens if needed."""
+ if not self.context.is_expired() or not self.context.refresh_token:
+ return False
+
+ await self._request_access_token(
+ grant_type="refresh_token",
+ extra_fields={"refresh_token": cast(str, self.context.refresh_token)},
+ )
+ return True
+
+ def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
+ """Return authentication headers for a request path."""
+ if self.context.access_token:
+ return {"Authorization": f"Bearer {self.context.access_token}"}
+
+ return {}
+
+ async def _request_access_token(
+ self, *, grant_type: str, extra_fields: Mapping[str, str]
+ ) -> None:
+ form = FormData(
+ {
+ "grant_type": grant_type,
+ "client_id": SOMFY_CLIENT_ID,
+ "client_secret": SOMFY_CLIENT_SECRET,
+ **extra_fields,
+ }
+ )
+
+ async with self.session.post(
+ f"{SOMFY_API}/oauth/oauth/v2/token/jwt",
+ data=form,
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
+ ) as response:
+ token = await response.json()
+
+ if token.get("message") == "error.invalid.grant":
+ raise SomfyBadCredentialsException(token["message"])
+
+ access_token = token.get("access_token")
+ if not access_token:
+ raise SomfyServiceException("No Somfy access token provided.")
+
+ self.context.access_token = cast(str, access_token)
+ self.context.refresh_token = token.get("refresh_token")
+ expires_in = token.get("expires_in")
+ if expires_in:
+ self.context.expires_at = datetime.datetime.now(
+ datetime.UTC
+ ) + datetime.timedelta(seconds=cast(int, expires_in) - 5)
+
+
+class CozytouchAuthStrategy(SessionLoginStrategy):
+ """Authentication strategy using Cozytouch session-based login."""
+
+ def __init__(
+ self,
+ credentials: UsernamePasswordCredentials,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Initialize CozytouchAuthStrategy with given parameters."""
+ super().__init__(credentials, session, server, ssl_context, api_type)
+
+ async def login(self) -> None:
+ """Perform login using Cozytouch username and password."""
+ form = FormData(
+ {
+ "grant_type": "password",
+ "username": f"GA-PRIVATEPERSON/{self.credentials.username}",
+ "password": self.credentials.password,
+ }
+ )
+ async with self.session.post(
+ f"{COZYTOUCH_ATLANTIC_API}/token",
+ data=form,
+ headers={
+ "Authorization": f"Basic {COZYTOUCH_CLIENT_ID}",
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ ) as response:
+ token = await response.json()
+
+ if token.get("error") == "invalid_grant":
+ raise CozyTouchBadCredentialsException(token["error_description"])
+
+ if "token_type" not in token:
+ raise CozyTouchServiceException("No CozyTouch token provided.")
+
+ async with self.session.get(
+ f"{COZYTOUCH_ATLANTIC_API}/magellan/accounts/jwt",
+ headers={"Authorization": f"Bearer {token['access_token']}"},
+ ) as response:
+ jwt = await response.text()
+
+ if not jwt:
+ raise CozyTouchServiceException("No JWT token provided.")
+
+ jwt = jwt.strip('"')
+
+ await self._post_login({"jwt": jwt})
+
+
+class NexityAuthStrategy(SessionLoginStrategy):
+ """Authentication strategy using Nexity session-based login."""
+
+ def __init__(
+ self,
+ credentials: UsernamePasswordCredentials,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Initialize NexityAuthStrategy with given parameters."""
+ super().__init__(credentials, session, server, ssl_context, api_type)
+
+ async def login(self) -> None:
+ """Perform login using Nexity username and password."""
+ loop = asyncio.get_event_loop()
+
+ def _client() -> BaseClient:
+ return boto3.client(
+ "cognito-idp", config=Config(region_name=NEXITY_COGNITO_REGION)
+ )
+
+ client = await loop.run_in_executor(None, _client)
+ aws = WarrantLite(
+ username=self.credentials.username,
+ password=self.credentials.password,
+ pool_id=NEXITY_COGNITO_USER_POOL,
+ client_id=NEXITY_COGNITO_CLIENT_ID,
+ client=client,
+ )
+
+ try:
+ tokens = await loop.run_in_executor(None, aws.authenticate_user)
+ except Exception as error:
+ raise NexityBadCredentialsException() from error
+
+ id_token = tokens["AuthenticationResult"]["IdToken"]
+
+ async with self.session.get(
+ f"{NEXITY_API}/deploy/api/v1/domotic/token",
+ headers={"Authorization": id_token},
+ ) as response:
+ token = await response.json()
+
+ if "token" not in token:
+ raise NexityServiceException("No Nexity SSO token provided.")
+
+ user_id = self.credentials.username.replace("@", "_-_")
+ await self._post_login({"ssoToken": token["token"], "userId": user_id})
+
+
+class LocalTokenAuthStrategy(BaseAuthStrategy):
+ """Authentication strategy using a local API token."""
+
+ def __init__(
+ self,
+ credentials: LocalTokenCredentials,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Initialize LocalTokenAuthStrategy with given parameters."""
+ super().__init__(session, server, ssl_context, api_type)
+ self.credentials = credentials
+
+ async def login(self) -> None:
+ """Validate that a token is provided for local API access."""
+ if not self.credentials.token:
+ raise InvalidTokenException("Local API requires a token.")
+
+ def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
+ """Return authentication headers for a request path."""
+ return {"Authorization": f"Bearer {self.credentials.token}"}
+
+
+class RexelAuthStrategy(BaseAuthStrategy):
+ """Authentication strategy using Rexel OAuth2."""
+
+ def __init__(
+ self,
+ credentials: RexelOAuthCodeCredentials,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Initialize RexelAuthStrategy with given parameters."""
+ super().__init__(session, server, ssl_context, api_type)
+ self.credentials = credentials
+ self.context = AuthContext()
+
+ async def login(self) -> None:
+ """Perform login using Rexel OAuth2 authorization code."""
+ await self._exchange_token(
+ {
+ "grant_type": "authorization_code",
+ "client_id": REXEL_OAUTH_CLIENT_ID,
+ "scope": REXEL_OAUTH_SCOPE,
+ "code": self.credentials.code,
+ "redirect_uri": self.credentials.redirect_uri,
+ }
+ )
+
+ async def refresh_if_needed(self) -> bool:
+ """Refresh Rexel OAuth2 tokens if needed."""
+ if not self.context.is_expired() or not self.context.refresh_token:
+ return False
+
+ await self._exchange_token(
+ {
+ "grant_type": "refresh_token",
+ "client_id": REXEL_OAUTH_CLIENT_ID,
+ "scope": REXEL_OAUTH_SCOPE,
+ "refresh_token": cast(str, self.context.refresh_token),
+ }
+ )
+ return True
+
+ def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
+ """Return authentication headers for a request path."""
+ if self.context.access_token:
+ return {"Authorization": f"Bearer {self.context.access_token}"}
+ return {}
+
+ async def _exchange_token(self, payload: Mapping[str, str]) -> None:
+ """Exchange authorization code or refresh token for access token."""
+ form = FormData(payload)
+ async with self.session.post(
+ REXEL_OAUTH_TOKEN_URL,
+ data=form,
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
+ ) as response:
+ token = await response.json()
+
+ # Handle OAuth error responses explicitly before accessing the access token.
+ error = token.get("error")
+ if error:
+ description = token.get("error_description") or token.get("message")
+ if description:
+ raise InvalidTokenException(
+ f"Error retrieving Rexel access token: {description}"
+ )
+ raise InvalidTokenException(
+ f"Error retrieving Rexel access token: {error}"
+ )
+
+ access_token = token.get("access_token")
+ if not access_token:
+ raise InvalidTokenException("No Rexel access token provided.")
+
+ self._ensure_consent(access_token)
+ self.context.access_token = cast(str, access_token)
+ self.context.refresh_token = token.get("refresh_token")
+ expires_in = token.get("expires_in")
+ if expires_in:
+ self.context.expires_at = datetime.datetime.now(
+ datetime.UTC
+ ) + datetime.timedelta(seconds=cast(int, expires_in) - 5)
+
+ @staticmethod
+ def _ensure_consent(access_token: str) -> None:
+ """Ensure that the Rexel token has the required consent."""
+ payload = _decode_jwt_payload(access_token)
+ consent = payload.get("consent")
+ if consent != REXEL_REQUIRED_CONSENT:
+ raise InvalidTokenException(
+ "Consent is missing or revoked for Rexel token."
+ )
+
+
+class BearerTokenAuthStrategy(BaseAuthStrategy):
+ """Authentication strategy using a static bearer token."""
+
+ def __init__(
+ self,
+ credentials: TokenCredentials,
+ session: ClientSession,
+ server: ServerConfig,
+ ssl_context: ssl.SSLContext | bool,
+ api_type: APIType,
+ ) -> None:
+ """Initialize BearerTokenAuthStrategy with given parameters."""
+ super().__init__(session, server, ssl_context, api_type)
+ self.credentials = credentials
+
+ def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
+ """Return authentication headers for a request path."""
+ if self.credentials.token:
+ return {"Authorization": f"Bearer {self.credentials.token}"}
+ return {}
+
+
+def _decode_jwt_payload(token: str) -> dict[str, Any]:
+ """Decode the payload of a JWT token."""
+ parts = token.split(".")
+ if len(parts) < 2:
+ raise InvalidTokenException("Malformed JWT received.")
+
+ payload_segment = parts[1]
+ padding = "=" * (-len(payload_segment) % 4)
+ try:
+ decoded = base64.urlsafe_b64decode(payload_segment + padding)
+ return cast(dict[str, Any], json.loads(decoded))
+ except (binascii.Error, json.JSONDecodeError) as error:
+ raise InvalidTokenException("Malformed JWT received.") from error
diff --git a/pyoverkiz/client.py b/pyoverkiz/client.py
index 772751dd..8767eff8 100644
--- a/pyoverkiz/client.py
+++ b/pyoverkiz/client.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-import asyncio
-import datetime
import logging
import os
import ssl
@@ -13,40 +11,23 @@
from typing import Any, cast
import backoff
-import boto3
import humps
from aiohttp import (
ClientConnectorError,
ClientResponse,
ClientSession,
- FormData,
ServerDisconnectedError,
)
from backoff.types import Details
-from botocore.client import BaseClient
-from botocore.config import Config
-from warrant_lite import WarrantLite
-
-from pyoverkiz.const import (
- COZYTOUCH_ATLANTIC_API,
- COZYTOUCH_CLIENT_ID,
- LOCAL_API_PATH,
- NEXITY_API,
- NEXITY_COGNITO_CLIENT_ID,
- NEXITY_COGNITO_REGION,
- NEXITY_COGNITO_USER_POOL,
- SOMFY_API,
- SOMFY_CLIENT_ID,
- SOMFY_CLIENT_SECRET,
- SUPPORTED_SERVERS,
-)
-from pyoverkiz.enums import APIType, Server
+
+from pyoverkiz.action_queue import ActionQueue, ActionQueueSettings
+from pyoverkiz.auth import AuthStrategy, Credentials, build_auth_strategy
+from pyoverkiz.const import SUPPORTED_SERVERS
+from pyoverkiz.enums import APIType, CommandMode, Server
from pyoverkiz.exceptions import (
AccessDeniedToGatewayException,
ActionGroupSetupNotFoundException,
BadCredentialsException,
- CozyTouchBadCredentialsException,
- CozyTouchServiceException,
DuplicateActionOnDeviceException,
ExecutionQueueFullException,
InvalidCommandException,
@@ -55,8 +36,6 @@
MaintenanceException,
MissingAPIKeyException,
MissingAuthorizationTokenException,
- NexityBadCredentialsException,
- NexityServiceException,
NoRegisteredEventListenerException,
NoSuchResourceException,
NotAuthenticatedException,
@@ -64,8 +43,6 @@
OverkizException,
ServiceUnavailableException,
SessionAndBearerInSameRequestException,
- SomfyBadCredentialsException,
- SomfyServiceException,
TooManyAttemptsBannedException,
TooManyConcurrentRequestsException,
TooManyExecutionsException,
@@ -74,22 +51,24 @@
UnknownUserException,
)
from pyoverkiz.models import (
- Command,
+ Action,
+ ActionGroup,
Device,
Event,
Execution,
Gateway,
HistoryExecution,
- LocalToken,
Option,
OptionParameter,
- OverkizServer,
Place,
- Scenario,
+ ProtocolType,
+ ServerConfig,
Setup,
State,
+ UIProfileDefinition,
)
from pyoverkiz.obfuscate import obfuscate_sensitive_data
+from pyoverkiz.serializers import prepare_payload
from pyoverkiz.types import JSON
_LOGGER = logging.getLogger(__name__)
@@ -164,10 +143,16 @@ def _create_local_ssl_context() -> ssl.SSLContext:
This method is not async-friendly and should be called from a thread
because it will load certificates from disk and do other blocking I/O.
"""
- return ssl.create_default_context(
+ context = ssl.create_default_context(
cafile=os.path.dirname(os.path.realpath(__file__)) + "/overkiz-root-ca-2048.crt"
)
+ # Disable strict validation introduced in Python 3.13, which doesn't work with
+ # Overkiz self-signed gateway certificates. Applied once to the shared context.
+ context.verify_flags &= ~ssl.VERIFY_X509_STRICT
+
+ return context
+
# The default SSLContext objects are created at import time
# since they do blocking I/O to load certificates from disk,
@@ -178,66 +163,80 @@ def _create_local_ssl_context() -> ssl.SSLContext:
class OverkizClient:
"""Interface class for the Overkiz API."""
- username: str
- password: str
- server: OverkizServer
+ server_config: ServerConfig
setup: Setup | None
devices: list[Device]
gateways: list[Gateway]
event_listener_id: str | None
session: ClientSession
- api_type: APIType
-
- _refresh_token: str | None = None
- _expires_in: datetime.datetime | None = None
- _access_token: str | None = None
_ssl: ssl.SSLContext | bool = True
+ _auth: AuthStrategy
+ _action_queue: ActionQueue | None = None
def __init__(
self,
- username: str,
- password: str,
- server: OverkizServer,
+ *,
+ server: ServerConfig | Server | str,
+ credentials: Credentials,
verify_ssl: bool = True,
- token: str | None = None,
session: ClientSession | None = None,
+ action_queue: bool | ActionQueueSettings = False,
) -> None:
"""Constructor.
- :param username: the username
- :param password: the password
- :param server: OverkizServer
+ :param server: ServerConfig
+ :param credentials: Credentials for authentication
+ :param verify_ssl: Enable SSL certificate verification
:param session: optional ClientSession
+ :param action_queue: enable batching or provide queue settings (default False)
"""
- self.username = username
- self.password = password
- self.server = server
- self._access_token = token
+ self.server_config = self._normalize_server(server)
self.setup: Setup | None = None
self.devices: list[Device] = []
self.gateways: list[Gateway] = []
self.event_listener_id: str | None = None
- self.session = session if session else ClientSession()
+ self.session = (
+ session
+ if session
+ else ClientSession(headers={"User-Agent": "python-overkiz-api"})
+ )
self._ssl = verify_ssl
- if LOCAL_API_PATH in self.server.endpoint:
- self.api_type = APIType.LOCAL
-
- if verify_ssl:
- # To avoid security issues while authentication to local API, we add the following authority to
- # our HTTPS client trust store: https://ca.overkiz.com/overkiz-root-ca-2048.crt
- self._ssl = SSL_CONTEXT_LOCAL_API
+ if self.server_config.type == APIType.LOCAL and verify_ssl:
+ # Use the prebuilt SSL context with disabled strict validation for local API.
+ self._ssl = SSL_CONTEXT_LOCAL_API
- # Disable strict validation introduced in Python 3.13, which doesn't
- # work with Overkiz self-signed gateway certificates
- self._ssl.verify_flags &= ~ssl.VERIFY_X509_STRICT
+ # Initialize action queue if enabled
+ queue_settings: ActionQueueSettings | None
+ if isinstance(action_queue, ActionQueueSettings):
+ queue_settings = action_queue
+ elif isinstance(action_queue, bool):
+ queue_settings = ActionQueueSettings() if action_queue else None
else:
- self.api_type = APIType.CLOUD
+ raise TypeError(
+ "action_queue must be a bool or ActionQueueSettings, "
+ f"got {type(action_queue).__name__}"
+ )
+
+ if queue_settings:
+ queue_settings.validate()
+ self._action_queue = ActionQueue(
+ executor=self._execute_action_group_direct,
+ delay=queue_settings.delay,
+ max_actions=queue_settings.max_actions,
+ )
+
+ self._auth = build_auth_strategy(
+ server_config=self.server_config,
+ credentials=credentials,
+ session=self.session,
+ ssl_context=self._ssl,
+ )
async def __aenter__(self) -> OverkizClient:
- """Enter the async context manager and return the client."""
+ """Enter async context manager and return the client instance."""
return self
async def __aexit__(
@@ -249,11 +248,31 @@ async def __aexit__(
"""Exit the async context manager and close the client session."""
await self.close()
+ @staticmethod
+ def _normalize_server(server: ServerConfig | Server | str) -> ServerConfig:
+ """Resolve user-provided server identifiers into a `ServerConfig`."""
+ if isinstance(server, ServerConfig):
+ return server
+
+ server_key = server.value if isinstance(server, Server) else str(server)
+
+ try:
+ return SUPPORTED_SERVERS[server_key]
+ except KeyError as error:
+ raise OverkizException(
+ f"Unknown server '{server_key}'. Provide a supported server key or ServerConfig instance."
+ ) from error
+
async def close(self) -> None:
"""Close the session."""
+ # Flush any pending actions in queue
+ if self._action_queue:
+ await self._action_queue.shutdown()
+
if self.event_listener_id:
await self.unregister_event_listener()
+ await self._auth.close()
await self.session.close()
async def login(
@@ -264,207 +283,21 @@ async def login(
Caller must provide one of [userId+userPassword, userId+ssoToken, accessToken, jwt].
"""
- # Local authentication
- if self.api_type == APIType.LOCAL:
+ await self._auth.login()
+
+ if self.server_config.type == APIType.LOCAL:
if register_event_listener:
await self.register_event_listener()
else:
- # Call a simple endpoint to verify if our token is correct
- # Since local API does not have a /login endpoint
+ # Validate local API token by calling a simple endpoint
await self.get_gateways()
return True
- # Somfy TaHoma authentication using access_token
- if self.server == SUPPORTED_SERVERS[Server.SOMFY_EUROPE]:
- await self.somfy_tahoma_get_access_token()
-
- if register_event_listener:
- await self.register_event_listener()
-
- return True
-
- # CozyTouch authentication using jwt
- if self.server in [
- SUPPORTED_SERVERS[Server.ATLANTIC_COZYTOUCH],
- SUPPORTED_SERVERS[Server.THERMOR_COZYTOUCH],
- SUPPORTED_SERVERS[Server.SAUTER_COZYTOUCH],
- ]:
- jwt = await self.cozytouch_login()
- payload = {"jwt": jwt}
-
- # Nexity authentication using ssoToken
- elif self.server == SUPPORTED_SERVERS[Server.NEXITY]:
- sso_token = await self.nexity_login()
- user_id = self.username.replace("@", "_-_") # Replace @ for _-_
- payload = {"ssoToken": sso_token, "userId": user_id}
-
- # Regular authentication using userId+userPassword
- else:
- payload = {"userId": self.username, "userPassword": self.password}
-
- response = await self.__post("login", data=payload)
-
- if response.get("success"):
- if register_event_listener:
- await self.register_event_listener()
- return True
-
- return False
-
- async def somfy_tahoma_get_access_token(self) -> str:
- """Authenticate via Somfy identity and acquire access_token."""
- # Request access token
- async with self.session.post(
- SOMFY_API + "/oauth/oauth/v2/token/jwt",
- data=FormData(
- {
- "grant_type": "password",
- "username": self.username,
- "password": self.password,
- "client_id": SOMFY_CLIENT_ID,
- "client_secret": SOMFY_CLIENT_SECRET,
- }
- ),
- headers={
- "Content-Type": "application/x-www-form-urlencoded",
- },
- ) as response:
- token = await response.json()
-
- # { "message": "error.invalid.grant", "data": [], "uid": "xxx" }
- if "message" in token and token["message"] == "error.invalid.grant":
- raise SomfyBadCredentialsException(token["message"])
+ if register_event_listener:
+ await self.register_event_listener()
- if "access_token" not in token:
- raise SomfyServiceException("No Somfy access token provided.")
-
- self._access_token = cast(str, token["access_token"])
- self._refresh_token = token["refresh_token"]
- self._expires_in = datetime.datetime.now() + datetime.timedelta(
- seconds=token["expires_in"] - 5
- )
-
- return self._access_token
-
- async def refresh_token(self) -> None:
- """Update the access and the refresh token. The refresh token will be valid 14 days."""
- if self.server != SUPPORTED_SERVERS[Server.SOMFY_EUROPE]:
- return
-
- if not self._refresh_token:
- raise ValueError("No refresh token provided. Login method must be used.")
-
- # &grant_type=refresh_token&refresh_token=REFRESH_TOKEN
- # Request access token
- async with self.session.post(
- SOMFY_API + "/oauth/oauth/v2/token/jwt",
- data=FormData(
- {
- "grant_type": "refresh_token",
- "refresh_token": self._refresh_token,
- "client_id": SOMFY_CLIENT_ID,
- "client_secret": SOMFY_CLIENT_SECRET,
- }
- ),
- headers={
- "Content-Type": "application/x-www-form-urlencoded",
- },
- ) as response:
- token = await response.json()
- # { "message": "error.invalid.grant", "data": [], "uid": "xxx" }
- if "message" in token and token["message"] == "error.invalid.grant":
- raise SomfyBadCredentialsException(token["message"])
-
- if "access_token" not in token:
- raise SomfyServiceException("No Somfy access token provided.")
-
- self._access_token = cast(str, token["access_token"])
- self._refresh_token = token["refresh_token"]
- self._expires_in = datetime.datetime.now() + datetime.timedelta(
- seconds=token["expires_in"] - 5
- )
-
- async def cozytouch_login(self) -> str:
- """Authenticate via CozyTouch identity and acquire JWT token."""
- # Request access token
- async with self.session.post(
- COZYTOUCH_ATLANTIC_API + "/token",
- data=FormData(
- {
- "grant_type": "password",
- "username": "GA-PRIVATEPERSON/" + self.username,
- "password": self.password,
- }
- ),
- headers={
- "Authorization": f"Basic {COZYTOUCH_CLIENT_ID}",
- "Content-Type": "application/x-www-form-urlencoded",
- },
- ) as response:
- token = await response.json()
-
- # {'error': 'invalid_grant',
- # 'error_description': 'Provided Authorization Grant is invalid.'}
- if "error" in token and token["error"] == "invalid_grant":
- raise CozyTouchBadCredentialsException(token["error_description"])
-
- if "token_type" not in token:
- raise CozyTouchServiceException("No CozyTouch token provided.")
-
- # Request JWT
- async with self.session.get(
- COZYTOUCH_ATLANTIC_API + "/magellan/accounts/jwt",
- headers={"Authorization": f"Bearer {token['access_token']}"},
- ) as response:
- jwt = await response.text()
-
- if not jwt:
- raise CozyTouchServiceException("No JWT token provided.")
-
- jwt = jwt.strip('"') # Remove surrounding quotes
-
- return jwt
-
- async def nexity_login(self) -> str:
- """Authenticate via Nexity identity and acquire SSO token."""
- loop = asyncio.get_event_loop()
-
- def _get_client() -> BaseClient:
- return boto3.client(
- "cognito-idp", config=Config(region_name=NEXITY_COGNITO_REGION)
- )
-
- # Request access token
- client = await loop.run_in_executor(None, _get_client)
-
- aws = WarrantLite(
- username=self.username,
- password=self.password,
- pool_id=NEXITY_COGNITO_USER_POOL,
- client_id=NEXITY_COGNITO_CLIENT_ID,
- client=client,
- )
-
- try:
- tokens = await loop.run_in_executor(None, aws.authenticate_user)
- except Exception as error:
- raise NexityBadCredentialsException() from error
-
- id_token = tokens["AuthenticationResult"]["IdToken"]
-
- async with self.session.get(
- NEXITY_API + "/deploy/api/v1/domotic/token",
- headers={
- "Authorization": id_token,
- },
- ) as response:
- token = await response.json()
-
- if "token" not in token:
- raise NexityServiceException("No Nexity SSO token provided.")
-
- return cast(str, token["token"])
+ return True
@retry_on_auth_error
async def get_setup(self, refresh: bool = False) -> Setup:
@@ -658,100 +491,112 @@ async def get_api_version(self) -> str:
@retry_on_too_many_executions
@retry_on_auth_error
- async def execute_command(
+ async def _execute_action_group_direct(
self,
- device_url: str,
- command: Command | str,
+ actions: list[Action],
+ mode: CommandMode | None = None,
label: str | None = "python-overkiz-api",
) -> str:
- """Send a command."""
- if isinstance(command, str):
- command = Command(command)
+ """Execute a non-persistent action group directly (internal method).
- response: str = await self.execute_commands(device_url, [command], label)
+ The executed action group does not have to be persisted on the server before use.
+ Per-session rate-limit : 1 calls per 28min 48s period for all operations of the same category (exec)
+ """
+ # Build a logical (snake_case) payload using model helpers and convert it
+ # to the exact JSON schema expected by the API (camelCase + small fixes).
+ payload = {"label": label, "actions": [a.to_payload() for a in actions]}
+
+ # Prepare final payload with camelCase keys and special abbreviation handling
+ final_payload = prepare_payload(payload)
+
+ if mode == CommandMode.GEOLOCATED:
+ url = "exec/apply/geolocated"
+ elif mode == CommandMode.INTERNAL:
+ url = "exec/apply/internal"
+ elif mode == CommandMode.HIGH_PRIORITY:
+ url = "exec/apply/highPriority"
+ else:
+ url = "exec/apply"
- return response
+ response: dict = await self.__post(url, final_payload)
- @retry_on_auth_error
- async def cancel_command(self, exec_id: str) -> None:
- """Cancel a running setup-level execution."""
- await self.__delete(f"/exec/current/setup/{exec_id}")
+ return cast(str, response["execId"])
- @retry_on_auth_error
- async def execute_commands(
+ async def execute_action_group(
self,
- device_url: str,
- commands: list[Command],
+ actions: list[Action],
+ mode: CommandMode | None = None,
label: str | None = "python-overkiz-api",
) -> str:
- """Send several commands in one call."""
- payload = {
- "label": label,
- "actions": [{"deviceURL": device_url, "commands": commands}],
- }
- response: dict = await self.__post("exec/apply", payload)
- return cast(str, response["execId"])
+ """Execute a non-persistent action group.
- @retry_on_auth_error
- async def get_scenarios(self) -> list[Scenario]:
- """List the scenarios."""
- response = await self.__get("actionGroups")
- return [Scenario(**scenario) for scenario in humps.decamelize(response)]
+ When action queue is enabled, actions will be batched with other actions
+ executed within the configured delay window. The method will wait for the
+ batch to execute and return the exec_id.
- @retry_on_auth_error
- async def get_places(self) -> Place:
- """List the places."""
- response = await self.__get("setup/places")
- places = Place(**humps.decamelize(response))
- return places
+ Gateways only allow a single action per device in each action group. The
+ action queue enforces this by merging commands for the same device into
+ a single action in the batch.
- @retry_on_auth_error
- async def generate_local_token(self, gateway_id: str) -> str:
- """Generates a new token.
+ When action queue is disabled, executes immediately and returns exec_id.
- Access scope : Full enduser API access (enduser/*).
- """
- response = await self.__get(f"config/{gateway_id}/local/tokens/generate")
+ The API is consistent regardless of queue configuration - always returns
+ exec_id string directly.
- return cast(str, response["token"])
+ :param actions: List of actions to execute
+ :param mode: Command mode (GEOLOCATED, INTERNAL, HIGH_PRIORITY, or None)
+ :param label: Label for the action group
+ :return: exec_id string from the executed action group
- @retry_on_auth_error
- async def activate_local_token(
- self, gateway_id: str, token: str, label: str, scope: str = "devmode"
- ) -> str:
- """Create a token.
+ Example usage::
- Access scope : Full enduser API access (enduser/*).
+ # Works the same with or without queue
+ exec_id = await client.execute_action_group([action])
"""
- response = await self.__post(
- f"config/{gateway_id}/local/tokens",
- {"label": label, "token": token, "scope": scope},
- )
-
- return cast(str, response["requestId"])
+ if self._action_queue:
+ queued = await self._action_queue.add(actions, mode, label)
+ return await queued
+ else:
+ return await self._execute_action_group_direct(actions, mode, label)
- @retry_on_auth_error
- async def get_local_tokens(
- self, gateway_id: str, scope: str = "devmode"
- ) -> list[LocalToken]:
- """Get all gateway tokens with the given scope.
+ async def flush_action_queue(self) -> None:
+ """Force flush all pending actions in the queue immediately.
- Access scope : Full enduser API access (enduser/*).
+ If action queue is disabled, this method does nothing.
+ If there are no pending actions, this method does nothing.
"""
- response = await self.__get(f"config/{gateway_id}/local/tokens/{scope}")
- local_tokens = [LocalToken(**lt) for lt in humps.decamelize(response)]
+ if self._action_queue:
+ await self._action_queue.flush()
- return local_tokens
+ def get_pending_actions_count(self) -> int:
+ """Get the approximate number of actions currently waiting in the queue.
+
+ Returns 0 if action queue is disabled. This is a best-effort snapshot
+ and may be stale if other coroutines modify the queue concurrently.
+ """
+ if self._action_queue:
+ return self._action_queue.get_pending_count()
+ return 0
@retry_on_auth_error
- async def delete_local_token(self, gateway_id: str, uuid: str) -> bool:
- """Delete a token.
+ async def cancel_command(self, exec_id: str) -> None:
+ """Cancel a running setup-level execution."""
+ await self.__delete(f"/exec/current/setup/{exec_id}")
- Access scope : Full enduser API access (enduser/*).
- """
- await self.__delete(f"config/{gateway_id}/local/tokens/{uuid}")
+ @retry_on_auth_error
+ async def get_action_groups(self) -> list[ActionGroup]:
+ """List the action groups (scenarios)."""
+ response = await self.__get("actionGroups")
+ return [
+ ActionGroup(**action_group) for action_group in humps.decamelize(response)
+ ]
- return True
+ @retry_on_auth_error
+ async def get_places(self) -> Place:
+ """List the places."""
+ response = await self.__get("setup/places")
+ places = Place(**humps.decamelize(response))
+ return places
@retry_on_auth_error
async def execute_scenario(self, oid: str) -> str:
@@ -808,16 +653,83 @@ async def get_setup_option_parameter(
return None
+ @retry_on_auth_error
+ async def get_reference_controllable(self, controllable_name: str) -> JSON:
+ """Get a controllable definition."""
+ return await self.__get(
+ f"reference/controllable/{urllib.parse.quote_plus(controllable_name)}"
+ )
+
+ @retry_on_auth_error
+ async def get_reference_controllable_types(self) -> JSON:
+ """Get details about all supported controllable types."""
+ return await self.__get("reference/controllableTypes")
+
+ @retry_on_auth_error
+ async def search_reference_devices_model(self, payload: JSON) -> JSON:
+ """Search reference device models using a POST payload."""
+ return await self.__post("reference/devices/search", payload)
+
+ @retry_on_auth_error
+ async def get_reference_protocol_types(self) -> list[ProtocolType]:
+ """Get details about supported protocol types on that server instance.
+
+ Returns a list of protocol type definitions, each containing:
+ - id: Numeric protocol identifier
+ - prefix: URL prefix used in device addresses
+ - name: Internal protocol name
+ - label: Human-readable protocol label
+ """
+ response = await self.__get("reference/protocolTypes")
+ return [ProtocolType(**protocol) for protocol in response]
+
+ @retry_on_auth_error
+ async def get_reference_timezones(self) -> JSON:
+ """Get timezones list."""
+ return await self.__get("reference/timezones")
+
+ @retry_on_auth_error
+ async def get_reference_ui_classes(self) -> list[str]:
+ """Get a list of all defined UI classes."""
+ return await self.__get("reference/ui/classes")
+
+ @retry_on_auth_error
+ async def get_reference_ui_classifiers(self) -> list[str]:
+ """Get a list of all defined UI classifiers."""
+ return await self.__get("reference/ui/classifiers")
+
+ @retry_on_auth_error
+ async def get_reference_ui_profile(self, profile_name: str) -> UIProfileDefinition:
+ """Get a description of a given UI profile (or form-factor variant).
+
+ Returns a profile definition containing:
+ - name: Profile name
+ - commands: Available commands with parameters and descriptions
+ - states: Available states with value types and descriptions
+ - form_factor: Whether profile is tied to a specific physical device type
+ """
+ response = await self.__get(
+ f"reference/ui/profile/{urllib.parse.quote_plus(profile_name)}"
+ )
+ return UIProfileDefinition(**humps.decamelize(response))
+
+ @retry_on_auth_error
+ async def get_reference_ui_profile_names(self) -> list[str]:
+ """Get a list of all defined UI profiles (and form-factor variants)."""
+ return await self.__get("reference/ui/profileNames")
+
+ @retry_on_auth_error
+ async def get_reference_ui_widgets(self) -> list[str]:
+ """Get a list of all defined UI widgets."""
+ return await self.__get("reference/ui/widgets")
+
async def __get(self, path: str) -> Any:
"""Make a GET request to the OverKiz API."""
- headers = {}
-
await self._refresh_token_if_expired()
- if self._access_token:
- headers["Authorization"] = f"Bearer {self._access_token}"
+ headers = dict(self._auth.auth_headers(path))
async with self.session.get(
- f"{self.server.endpoint}{path}",
+ f"{self.server_config.endpoint}{path}",
headers=headers,
ssl=self._ssl,
) as response:
@@ -828,14 +740,11 @@ async def __post(
self, path: str, payload: JSON | None = None, data: JSON | None = None
) -> Any:
"""Make a POST request to the OverKiz API."""
- headers = {}
-
- if path != "login" and self._access_token:
- await self._refresh_token_if_expired()
- headers["Authorization"] = f"Bearer {self._access_token}"
+ await self._refresh_token_if_expired()
+ headers = dict(self._auth.auth_headers(path))
async with self.session.post(
- f"{self.server.endpoint}{path}",
+ f"{self.server_config.endpoint}{path}",
data=data,
json=payload,
headers=headers,
@@ -846,15 +755,11 @@ async def __post(
async def __delete(self, path: str) -> None:
"""Make a DELETE request to the OverKiz API."""
- headers = {}
-
await self._refresh_token_if_expired()
-
- if self._access_token:
- headers["Authorization"] = f"Bearer {self._access_token}"
+ headers = dict(self._auth.auth_headers(path))
async with self.session.delete(
- f"{self.server.endpoint}{path}",
+ f"{self.server_config.endpoint}{path}",
headers=headers,
ssl=self._ssl,
) as response:
@@ -976,12 +881,7 @@ async def check_response(response: ClientResponse) -> None:
async def _refresh_token_if_expired(self) -> None:
"""Check if token is expired and request a new one."""
- if (
- self._expires_in
- and self._refresh_token
- and self._expires_in <= datetime.datetime.now()
- ):
- await self.refresh_token()
-
- if self.event_listener_id:
- await self.register_event_listener()
+ refreshed = await self._auth.refresh_if_needed()
+
+ if refreshed and self.event_listener_id:
+ await self.register_event_listener()
diff --git a/pyoverkiz/const.py b/pyoverkiz/const.py
index 15345295..3fccd9dd 100644
--- a/pyoverkiz/const.py
+++ b/pyoverkiz/const.py
@@ -3,7 +3,8 @@
from __future__ import annotations
from pyoverkiz.enums import Server
-from pyoverkiz.models import OverkizServer
+from pyoverkiz.enums.server import APIType
+from pyoverkiz.models import ServerConfig
COZYTOUCH_ATLANTIC_API = "https://apis.groupe-atlantic.com"
COZYTOUCH_CLIENT_ID = (
@@ -15,6 +16,18 @@
NEXITY_COGNITO_USER_POOL = "eu-west-1_wj277ucoI"
NEXITY_COGNITO_REGION = "eu-west-1"
+REXEL_BACKEND_API = (
+ "https://app-ec-backend-enduser-prod.azurewebsites.net/api/enduser/overkiz/"
+)
+REXEL_OAUTH_CLIENT_ID = "2b635ede-c3fb-43bc-8d23-f6d17f80e96d"
+REXEL_OAUTH_SCOPE = "https://adb2cservicesfrenduserprod.onmicrosoft.com/94f05108-65f7-477a-a84d-e67e1aed6f79/ExternalProvider"
+REXEL_OAUTH_TENANT = (
+ "https://consumerlogin.rexelservices.fr/670998c0-f737-4d75-a32f-ba9292755b70"
+)
+REXEL_OAUTH_POLICY = "B2C_1A_SIGNINONLYHOMEASSISTANT"
+REXEL_OAUTH_TOKEN_URL = f"{REXEL_OAUTH_TENANT}/oauth2/v2.0/token?p={REXEL_OAUTH_POLICY}"
+REXEL_REQUIRED_CONSENT = "homeassistant"
+
SOMFY_API = "https://accounts.somfy.com"
SOMFY_CLIENT_ID = "0d8e920c-1478-11e7-a377-02dd59bd3041_1ewvaqmclfogo4kcsoo0c8k4kso884owg08sg8c40sk4go4ksg"
# OAuth client secrets are public by design (embedded in mobile apps)
@@ -28,101 +41,117 @@
Server.SOMFY_AMERICA,
]
-SUPPORTED_SERVERS: dict[str, OverkizServer] = {
- Server.ATLANTIC_COZYTOUCH: OverkizServer(
+SUPPORTED_SERVERS: dict[str, ServerConfig] = {
+ Server.ATLANTIC_COZYTOUCH: ServerConfig(
+ server=Server.ATLANTIC_COZYTOUCH,
name="Atlantic Cozytouch",
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Atlantic",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.BRANDT: OverkizServer(
+ Server.BRANDT: ServerConfig(
+ server=Server.BRANDT,
name="Brandt Smart Control",
endpoint="https://ha3-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Brandt",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.FLEXOM: OverkizServer(
+ Server.FLEXOM: ServerConfig(
+ server=Server.FLEXOM,
name="Flexom",
endpoint="https://ha108-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Bouygues",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.HEXAOM_HEXACONNECT: OverkizServer(
+ Server.HEXAOM_HEXACONNECT: ServerConfig(
+ server=Server.HEXAOM_HEXACONNECT,
name="Hexaom HexaConnect",
endpoint="https://ha5-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Hexaom",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.HI_KUMO_ASIA: OverkizServer(
+ Server.HI_KUMO_ASIA: ServerConfig(
+ server=Server.HI_KUMO_ASIA,
name="Hitachi Hi Kumo (Asia)",
endpoint="https://ha203-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Hitachi",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.HI_KUMO_EUROPE: OverkizServer(
+ Server.HI_KUMO_EUROPE: ServerConfig(
+ server=Server.HI_KUMO_EUROPE,
name="Hitachi Hi Kumo (Europe)",
endpoint="https://ha117-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Hitachi",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.HI_KUMO_OCEANIA: OverkizServer(
+ Server.HI_KUMO_OCEANIA: ServerConfig(
+ server=Server.HI_KUMO_OCEANIA,
name="Hitachi Hi Kumo (Oceania)",
endpoint="https://ha203-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Hitachi",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.NEXITY: OverkizServer(
+ Server.NEXITY: ServerConfig(
+ server=Server.NEXITY,
name="Nexity Eugénie",
endpoint="https://ha106-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Nexity",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.REXEL: OverkizServer(
+ Server.REXEL: ServerConfig(
+ server=Server.REXEL,
name="Rexel Energeasy Connect",
- endpoint="https://ha112-1.overkiz.com/enduser-mobile-web/enduserAPI/",
+ endpoint=REXEL_BACKEND_API,
manufacturer="Rexel",
- configuration_url="https://utilisateur.energeasyconnect.com/user/#/zone/equipements",
+ type=APIType.CLOUD,
),
- Server.SAUTER_COZYTOUCH: OverkizServer( # duplicate of Atlantic Cozytouch
+ Server.SAUTER_COZYTOUCH: ServerConfig( # duplicate of Atlantic Cozytouch
+ server=Server.SAUTER_COZYTOUCH,
name="Sauter Cozytouch",
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Sauter",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.SIMU_LIVEIN2: OverkizServer( # alias of https://tahomalink.com
+ Server.SIMU_LIVEIN2: ServerConfig( # alias of https://tahomalink.com
+ server=Server.SIMU_LIVEIN2,
name="SIMU (LiveIn2)",
endpoint="https://ha101-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Somfy",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.SOMFY_EUROPE: OverkizServer( # alias of https://tahomalink.com
+ Server.SOMFY_EUROPE: ServerConfig( # alias of https://tahomalink.com
+ server=Server.SOMFY_EUROPE,
name="Somfy (Europe)",
endpoint="https://ha101-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Somfy",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.SOMFY_AMERICA: OverkizServer(
+ Server.SOMFY_AMERICA: ServerConfig(
+ server=Server.SOMFY_AMERICA,
name="Somfy (North America)",
endpoint="https://ha401-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Somfy",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.SOMFY_OCEANIA: OverkizServer(
+ Server.SOMFY_OCEANIA: ServerConfig(
+ server=Server.SOMFY_OCEANIA,
name="Somfy (Oceania)",
endpoint="https://ha201-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Somfy",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.THERMOR_COZYTOUCH: OverkizServer( # duplicate of Atlantic Cozytouch
+ Server.THERMOR_COZYTOUCH: ServerConfig( # duplicate of Atlantic Cozytouch
+ server=Server.THERMOR_COZYTOUCH,
name="Thermor Cozytouch",
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Thermor",
- configuration_url=None,
+ type=APIType.CLOUD,
),
- Server.UBIWIZZ: OverkizServer(
+ Server.UBIWIZZ: ServerConfig(
+ server=Server.UBIWIZZ,
name="Ubiwizz",
endpoint="https://ha129-1.overkiz.com/enduser-mobile-web/enduserAPI/",
manufacturer="Decelect",
- configuration_url=None,
+ type=APIType.CLOUD,
),
}
diff --git a/pyoverkiz/enums/__init__.py b/pyoverkiz/enums/__init__.py
index e7fc94e0..bf6080d1 100644
--- a/pyoverkiz/enums/__init__.py
+++ b/pyoverkiz/enums/__init__.py
@@ -1,13 +1,43 @@
"""Convenience re-exports for the enums package."""
-# flake8: noqa: F403
+# Explicitly re-export all Enum subclasses to avoid wildcard import issues
+from pyoverkiz.enums.command import CommandMode, OverkizCommand, OverkizCommandParam
+from pyoverkiz.enums.execution import (
+ ExecutionState,
+ ExecutionSubType,
+ ExecutionType,
+)
+from pyoverkiz.enums.gateway import GatewaySubType, GatewayType, UpdateBoxStatus
+from pyoverkiz.enums.general import DataType, EventName, FailureType, ProductType
+from pyoverkiz.enums.measured_value_type import MeasuredValueType
+from pyoverkiz.enums.protocol import Protocol
+from pyoverkiz.enums.server import APIType, Server
+from pyoverkiz.enums.state import OverkizAttribute, OverkizState
+from pyoverkiz.enums.ui import UIClass, UIClassifier, UIWidget
+from pyoverkiz.enums.ui_profile import UIProfile
-from .command import *
-from .execution import *
-from .gateway import *
-from .general import *
-from .measured_value_type import *
-from .protocol import *
-from .server import *
-from .state import *
-from .ui import *
+__all__ = [
+ "APIType",
+ "CommandMode",
+ "DataType",
+ "EventName",
+ "ExecutionState",
+ "ExecutionSubType",
+ "ExecutionType",
+ "FailureType",
+ "GatewaySubType",
+ "GatewayType",
+ "MeasuredValueType",
+ "OverkizAttribute",
+ "OverkizCommand",
+ "OverkizCommandParam",
+ "OverkizState",
+ "ProductType",
+ "Protocol",
+ "Server",
+ "UIClass",
+ "UIClassifier",
+ "UIProfile",
+ "UIWidget",
+ "UpdateBoxStatus",
+]
diff --git a/pyoverkiz/enums/base.py b/pyoverkiz/enums/base.py
new file mode 100644
index 00000000..9204194b
--- /dev/null
+++ b/pyoverkiz/enums/base.py
@@ -0,0 +1,27 @@
+"""Shared enum helpers for consistent parsing and logging."""
+
+from __future__ import annotations
+
+import logging
+from typing import Self, cast
+
+
+class UnknownEnumMixin:
+ """Mixin for enums that need an `UNKNOWN` fallback.
+
+ Define `UNKNOWN` on the enum and optionally override
+ `__missing_message__` to customize the log message.
+ """
+
+ __missing_message__ = "Unsupported value %s has been returned for %s"
+
+ @classmethod
+ def _missing_(cls, value: object) -> Self: # type: ignore[override]
+ """Return `UNKNOWN` and log unrecognized values.
+
+ Intentionally overrides the Enum base `_missing_` to provide an UNKNOWN fallback.
+ """
+ message = cls.__missing_message__
+ logging.getLogger(cls.__module__).warning(message, value, cls)
+ # Type checker cannot infer UNKNOWN exists on Self, but all subclasses define it
+ return cast(Self, cls.UNKNOWN) # type: ignore[attr-defined]
diff --git a/pyoverkiz/enums/command.py b/pyoverkiz/enums/command.py
index abc3caf7..48a9f23f 100644
--- a/pyoverkiz/enums/command.py
+++ b/pyoverkiz/enums/command.py
@@ -3,24 +3,18 @@
# ruff: noqa: S105
# Enum values contain "PASS" in API names (e.g. PassAPC), not passwords
-import sys
-from enum import unique
-
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from enum import StrEnum, unique
@unique
class OverkizCommand(StrEnum):
"""Device commands used by Overkiz."""
+ ACTIVATE_CALENDAR = "activateCalendar"
ACTIVATE_OPTION = "activateOption"
ADD_LOCK_LEVEL = "addLockLevel"
ADVANCED_REFRESH = "advancedRefresh"
+ ADVANCED_SOMFY_DISCOVER = "advancedSomfyDiscover"
ALARM_OFF = "alarmOff"
ALARM_ON = "alarmOn"
ALARM_PARTIAL_1 = "alarmPartial1"
@@ -29,48 +23,121 @@ class OverkizCommand(StrEnum):
ARM = "arm"
ARM_PARTIAL_DAY = "armPartialDay"
ARM_PARTIAL_NIGHT = "armPartialNight"
+ BIND = "bind"
+ BINDING_NETWORK = "bindingNetwork"
BIP = "bip"
+ BLINK_MODEM_LED = "blinkModemLed"
CANCEL_ABSENCE = "cancelAbsence"
+ CANCEL_HEATING_LEVEL = "cancelHeatingLevel"
+ CANCEL_HOLIDAY_MODE = "cancelHolidayMode"
CHECK_EVENT_TRIGGER = "checkEventTrigger"
+ CLEAR_CREDENTIALS = "clearCredentials"
CLOSE = "close"
+ CLOSE_NETWORK = "closeNetwork"
+ CLOSE_NETWORK_MANAGEMENT = "closeNetworkManagement"
CLOSE_SLATS = "closeSlats"
+ CONFIGURE_HOLIDAY_MODE = "configureHolidayMode"
CYCLE = "cycle"
+ DEACTIVATE_CALENDAR = "deactivateCalendar"
DEACTIVATE_OPTION = "deactivateOption"
DELAYED_STOP_IDENTIFY = "delayedStopIdentify"
+ DELETE_CONTROLLERS = "deleteControllers"
DEPLOY = "deploy"
- DISARM = "disarm"
DING_DONG = "dingDong"
+ DISARM = "disarm"
+ DISCOVER = "discover"
+ DISCOVER1_WAY_CONTROLLER = "discover1WayController"
+ DISCOVER_ACTUATORS = "discoverActuators"
+ DISCOVER_SENSORS = "discoverSensors"
+ DISCOVER_SOMFY_UNSET_ACTUATORS = "discoverSomfyUnsetActuators"
DOWN = "down"
EXIT_DEROGATION = "exitDerogation"
+ FAST_BACKWARD = "fastBackward"
FAST_BIP_SEQUENCE = "fastBipSequence"
+ FAST_FORWARD = "fastForward"
+ GET_ALL_PLAYING_INFO = "getAllPlayingInfo"
GET_CLOSURE = "getClosure"
+ GET_CURRENT_TRANSPORT_ACTIONS = "getCurrentTransportActions"
+ GET_GROUP_MUTE = "getGroupMute"
+ GET_GROUP_VOLUME = "getGroupVolume"
GET_LEVEL = "getLevel"
GET_LOWER_CLOSURE = "getLowerClosure"
+ GET_MEDIA_ELEMENTS = "getMediaElements"
+ GET_MEDIA_INFO = "getMediaInfo"
+ GET_MUTE = "getMute"
GET_NAME = "getName"
+ GET_POSITION_INFO = "getPositionInfo"
+ GET_SONOS_FAVORITES = "getSonosFavorites"
+ GET_SONOS_PLAYLIST = "getSonosPlaylist"
+ GET_TRANSPORT_INFO = "getTransportInfo"
GET_UPPER_CLOSURE = "getUpperClosure"
+ GET_VOLUME = "getVolume"
GLOBAL_CONTROL = "globalControl"
GO_TO_ALIAS = "goToAlias"
IDENTIFY = "identify"
+ INCREASING_FREQUENCY_BIP = "increasingFrequencyBip"
+ IS_FAILED_DEVICE = "isFailedDevice"
+ JOIN_NETWORK = "joinNetwork"
+ KEEP_ONE_WAY_CONTROLLERS_AND_DELETE_NODE = "keepOneWayControllersAndDeleteNode"
+ LEAVE_NETWORK = "leaveNetwork"
LOCK = "lock"
+ LONG_BIP = "longBip"
LOWER_CLOSE = "lowerClose"
LOWER_DOWN = "lowerDown"
LOWER_OPEN = "lowerOpen"
LOWER_UP = "lowerUp"
MEMORIZED_VOLUME = "memorizedVolume"
+ MUTE = "mute"
+ MUTE_GROUP = "muteGroup"
MY = "my"
+ NEXT = "next"
+ NO_PERSON_INSIDE = "noPersonInside"
OFF = "off"
OFFLINE = "offline"
ON = "on"
ONLINE = "online"
+ ON_WITH_INTERNAL_TIMER = "onWithInternalTimer"
ON_WITH_TIMER = "onWithTimer"
OPEN = "open"
+ OPEN_CONFIGURATION = "openConfiguration"
+ OPEN_NETWORK = "openNetwork"
+ OPEN_NETWORK_MANAGEMENT = "openNetworkManagement"
+ OPEN_NETWORK_WITH_COMMISSIONING_MANAGEMENT = (
+ "openNetworkWithCommissioningManagement"
+ )
OPEN_SLATS = "openSlats"
+ PAIR = "pair"
PAIR_ONE_WAY_CONTROLLER = "pairOneWayController"
PARTIAL = "partial"
PARTIAL_POSITION = "partialPosition"
+ PAUSE = "pause"
+ PERSON_INSIDE = "personInside"
+ PLAY = "play"
+ PLAY_URI = "playURI"
+ PREVIOUS = "previous"
+ REBOOT_MODEM = "rebootModem"
+ REFRESH_ABSENCE_END_DATE = "refreshAbsenceEndDate"
+ REFRESH_ABSENCE_MODE = "refreshAbsenceMode"
REFRESH_ABSENCE_SCHEDULING_AVAILABILITY = "refreshAbsenceSchedulingAvailability"
+ REFRESH_ABSENCE_START_DATE = "refreshAbsenceStartDate"
+ REFRESH_ACTIVE_MODE = "refreshActiveMode"
+ REFRESH_AIRPLANE_MODE = "refreshAirplaneMode"
+ REFRESH_ALARM_DELAY = "refreshAlarmDelay"
+ REFRESH_ALARM_STATUS = "refreshAlarmStatus"
+ REFRESH_ANTI_LEGIONELLOSIS = "refreshAntiLegionellosis"
+ REFRESH_AUTO_PROGRAM = "refreshAutoProgram"
REFRESH_AWAY_MODE_DURATION = "refreshAwayModeDuration"
+ REFRESH_BATTERY_LEVEL = "refreshBatteryLevel"
+ REFRESH_BATTERY_STATUS = "refreshBatteryStatus"
+ REFRESH_BINDING_TABLE = "refreshBindingTable"
+ REFRESH_BOILER_INSTALLATION_OPTION = "refreshBoilerInstallationOption"
+ REFRESH_BOOST_END_DATE = "refreshBoostEndDate"
+ REFRESH_BOOST_MODE = "refreshBoostMode"
REFRESH_BOOST_MODE_DURATION = "refreshBoostModeDuration"
+ REFRESH_BOOST_MODE_PARAMETERS = "refreshBoostModeParameters"
+ REFRESH_BOOST_START_DATE = "refreshBoostStartDate"
+ REFRESH_BOTTOM_TANK_WATER_TEMPERATURE = "refreshBottomTankWaterTemperature"
+ REFRESH_CO2_HISTORY = "refreshCO2History"
REFRESH_COMFORT_COOLING_TARGET_TEMPERATURE = (
"refreshComfortCoolingTargetTemperature"
)
@@ -78,77 +145,197 @@ class OverkizCommand(StrEnum):
"refreshComfortHeatingTargetTemperature"
)
REFRESH_COMFORT_TARGET_DWH_TEMPERATURE = "refreshComfortTargetDHWTemperature"
+ REFRESH_COMFORT_TEMPERATURE = "refreshComfortTemperature"
+ REFRESH_CONTROLLER_ADDRESS = "refreshControllerAddress"
+ REFRESH_CUMULATED_LOWERING = "refreshCumulatedLowering"
+ REFRESH_CURRENT_ALARM_MODE = "refreshCurrentAlarmMode"
+ REFRESH_CURRENT_OPERATING_MODE = "refreshCurrentOperatingMode"
+ REFRESH_CURRENT_WORKING_RATE = "refreshCurrentWorkingRate"
+ REFRESH_DATE_TIME = "refreshDateTime"
+ REFRESH_DELETION_CANCELATION = "refreshDeletionCancelation"
+ REFRESH_DEROGATED_TARGET_TEMPERATURE = "refreshDerogatedTargetTemperature"
+ REFRESH_DEROGATION = "refreshDerogation"
REFRESH_DEROGATION_REMAINING_TIME = "refreshDerogationRemainingTime"
REFRESH_DEVICE_SERIAL_NUMBER = "refreshDeviceSerialNumber"
+ REFRESH_DHWCAPACITY = "refreshDHWCapacity"
+ REFRESH_DHWERROR = "refreshDHWError"
+ REFRESH_DHW_MODE = "refreshDHWMode"
+ REFRESH_DRYING_DURATION = "refreshDryingDuration"
+ REFRESH_DRYING_PARAMETERS = "refreshDryingParameters"
REFRESH_ECO_COOLING_TARGET_TEMPERATURE = "refreshEcoCoolingTargetTemperature"
REFRESH_ECO_HEATING_TARGET_TEMPERATURE = "refreshEcoHeatingTargetTemperature"
REFRESH_ECO_TARGET_DWH_TEMPERATURE = "refreshEcoTargetDHWTemperature"
+ REFRESH_ECO_TEMPERATURE = "refreshEcoTemperature"
+ REFRESH_EFFECTIVE_TEMPERATURE_SETPOINT = "refreshEffectiveTemperatureSetpoint"
+ REFRESH_ELECTRICAL_EXTRA_MANAGEMENT = "refreshElectricalExtraManagement"
+ REFRESH_ELECTRIC_ENERGY_CONSUMPTION = "refreshElectricEnergyConsumption"
+ REFRESH_ENERGY_SAVING = "refreshEnergySaving"
REFRESH_ERROR_CODE = "refreshErrorCode"
- REFRESH_DHW_MODE = "refreshDHWMode"
- REFRESH_HEATING_STATUS = "refreshHeatingStatus"
+ REFRESH_EXPECTED_NUMBER_OF_SHOWER = "refreshExpectedNumberOfShower"
+ REFRESH_EXTRACTION_OPTION = "refreshExtractionOption"
+ REFRESH_GPRSREGISTRATION = "refreshGPRSRegistration"
+ REFRESH_GSMREGISTRATION = "refreshGSMRegistration"
REFRESH_HEATING_DEROGATION_AVAILABILITY = "refreshHeatingDerogationAvailability"
+ REFRESH_HEATING_LEVEL = "refreshHeatingLevel"
+ REFRESH_HEATING_STATUS = "refreshHeatingStatus"
+ REFRESH_IDENTIFIER = "refreshIdentifier"
+ REFRESH_INSTALLATION = "refreshInstallation"
+ REFRESH_INTRUSION_DETECTED = "refreshIntrusionDetected"
+ REFRESH_LOCAL_LEAD_TIME = "refreshLocalLeadTime"
+ REFRESH_MANUFACTURER_NAME = "refreshManufacturerName"
+ REFRESH_MANUFACTURER_SPECIFIC = "refreshManufacturerSpecific"
+ REFRESH_MAXIMUM_COOLING_TARGET_TEMPERATURE = (
+ "refreshMaximumCoolingTargetTemperature"
+ )
+ REFRESH_MAXIMUM_HEATING_TARGET_TEMPERATURE = (
+ "refreshMaximumHeatingTargetTemperature"
+ )
+ REFRESH_MAXIMUM_TARGET_TEMPERATURE = "refreshMaximumTargetTemperature"
+ REFRESH_MEMORIZED1_ORIENTATION = "refreshMemorized1Orientation"
REFRESH_MEMORIZED_1_POSITION = "refreshMemorized1Position"
+ REFRESH_MIDDLE_WATER_TEMPERATURE = "refreshMiddleWaterTemperature"
+ REFRESH_MIDDLE_WATER_TEMPERATURE_IN = "refreshMiddleWaterTemperatureIn"
+ REFRESH_MINIMUM_COOLING_TARGET_TEMPERATURE = (
+ "refreshMinimumCoolingTargetTemperature"
+ )
+ REFRESH_MINIMUM_HEATING_TARGET_TEMPERATURE = (
+ "refreshMinimumHeatingTargetTemperature"
+ )
+ REFRESH_MODEL = "refreshModel"
+ REFRESH_MODE_SETTINGS = "refreshModeSettings"
+ REFRESH_NATIVE_FUNCTIONAL_LEVEL = "refreshNativeFunctionalLevel"
+ REFRESH_NEIGHBOR_TABLE = "refreshNeighborTable"
+ REFRESH_NETWORK = "refreshNetwork"
+ REFRESH_OCCUPANCY = "refreshOccupancy"
+ REFRESH_ON_OFF_STATE = "refreshOnOffState"
REFRESH_OPERATING_MODE = "refreshOperatingMode"
+ REFRESH_OPERATING_MODE_CAPABILITIES = "refreshOperatingModeCapabilities"
+ REFRESH_OPERATING_RANGE = "refreshOperatingRange"
+ REFRESH_OPERATING_TIME = "refreshOperatingTime"
+ REFRESH_OPERATOR_CONFIG = "refreshOperatorConfig"
+ REFRESH_OPERATOR_NAME = "refreshOperatorName"
REFRESH_PASS_APC_COOLING_MODE = "refreshPassAPCCoolingMode"
REFRESH_PASS_APC_COOLING_PROFILE = "refreshPassAPCCoolingProfile"
REFRESH_PASS_APC_HEATING_MODE = "refreshPassAPCHeatingMode"
REFRESH_PASS_APC_HEATING_PROFILE = "refreshPassAPCHeatingProfile"
+ REFRESH_PEAK_NOTICE = "refreshPeakNotice"
+ REFRESH_PEAK_WARNING = "refreshPeakWarning"
+ REFRESH_POD_MODE = "refreshPodMode"
+ REFRESH_POWER_AND_TENSION = "refreshPowerAndTension"
REFRESH_POWER_HEAT_ELECTRICAL = "refreshPowerHeatElectrical"
REFRESH_PRODUCT_TYPE = "refreshProductType"
+ REFRESH_PROGRAMMING_SLOTS = "refreshProgrammingSlots"
+ REFRESH_RATE_MANAGEMENT = "refreshRateManagement"
+ REFRESH_RELATIVE_HUMIDITY = "refreshRelativeHumidity"
+ REFRESH_ROOM_DELETION_THRESHOLD = "refreshRoomDeletionThreshold"
+ REFRESH_ROUTING_TABLE = "refreshRoutingTable"
+ REFRESH_RSSILEVEL = "refreshRSSILevel"
+ REFRESH_SECURED_POSITION_TEMPERATURE = "refreshSecuredPositionTemperature"
+ REFRESH_SENSORS_STATE = "refreshSensorsState"
+ REFRESH_SETPOINT_LOWERING_TEMPERATURE_IN_PROG_MODE = (
+ "refreshSetpointLoweringTemperatureInProgMode"
+ )
+ REFRESH_SET_POINT_MODE = "refreshSetPointMode"
+ REFRESH_SIMCARD_STATUS = "refreshSIMCardStatus"
+ REFRESH_SIREN_STATUS = "refreshSirenStatus"
+ REFRESH_SMART_GRID_OPTION = "refreshSmartGridOption"
+ REFRESH_SSVERROR6 = "refreshSSVError6"
REFRESH_STATE = "refreshState"
+ REFRESH_SYNCHRONISATION_REQUEST = "refreshSynchronisationRequest"
REFRESH_TARGET_DWH_TEMPERATURE = "refreshTargetDHWTemperature"
REFRESH_TARGET_TEMPERATURE = "refreshTargetTemperature"
+ REFRESH_TEMPERATURE = "refreshTemperature"
+ REFRESH_TEMPERATURE_PROBE_CALIBRATION_OFFSET = (
+ "refreshTemperatureProbeCalibrationOffset"
+ )
REFRESH_THERMAL_SCHEDULING_AVAILABILITY = "refreshThermalSchedulingAvailability"
+ REFRESH_THERMOSTAT_SETPOINT_SUPPORTED = "refreshThermostatSetpointSupported"
+ REFRESH_THERMOSTAT_SET_POINT = "refreshThermostatSetPoint"
+ REFRESH_TIME_PROGRAM = "refreshTimeProgram"
REFRESH_TIME_PROGRAM_BY_ID = "refreshTimeProgramById"
+ REFRESH_TOWEL_DRYER_TEMPORARY_STATE = "refreshTowelDryerTemporaryState"
+ REFRESH_TOWEL_DRYER_TIME_PROGRAM = "refreshTowelDryerTimeProgram"
+ REFRESH_UPDATE_STATUS = "refreshUpdateStatus"
REFRESH_VENTILATION_CONFIGURATION_MODE = "refreshVentilationConfigurationMode"
REFRESH_VENTILATION_STATE = "refreshVentilationState"
+ REFRESH_WAKE_UP_INTERVAL = "refreshWakeUpInterval"
+ REFRESH_WATER_CONSUMPTION = "refreshWaterConsumption"
REFRESH_WATER_TARGET_TEMPERATURE = "refreshWaterTargetTemperature"
+ REFRESH_WATER_TEMPERATURE = "refreshWaterTemperature"
REFRESH_ZONES_NUMBER = "refreshZonesNumber"
+ REFRESH_ZONES_PASS_APC_COOLING_PROFILE = "refreshZonesPassAPCCoolingProfile"
+ REFRESH_ZONES_PASS_APC_HEATING_PROFILE = "refreshZonesPassAPCHeatingProfile"
REFRESH_ZONES_TARGET_TEMPERATURE = "refreshZonesTargetTemperature"
REFRESH_ZONES_TEMPERATURE = "refreshZonesTemperature"
REFRESH_ZONES_TEMPERATURE_SENSOR_AVAILABILITY = (
"refreshZonesTemperatureSensorAvailability"
)
REFRESH_ZONES_THERMAL_CONFIGURATION = "refreshZonesThermalConfiguration"
- REFRESH_ZONES_PASS_APC_COOLING_PROFILE = "refreshZonesPassAPCCoolingProfile"
- REFRESH_ZONES_PASS_APC_HEATING_PROFILE = "refreshZonesPassAPCHeatingProfile"
+ REMOVE_FAILED_NODE = "removeFailedNode"
REMOVE_LOCK_LEVEL = "removeLockLevel"
+ REPLACE_FAILED_DEVICE = "replaceFailedDevice"
+ RESET = "reset"
RESET_LOCK_LEVELS = "resetLockLevels"
+ RESET_NETWORK_SECURITY = "resetNetworkSecurity"
+ RESET_VENTILATION = "resetVentilation"
REST = "rest"
+ RESUME = "resume"
+ REWIND = "rewind"
RIGHT = "right"
RING = "ring"
+ RING_WITH_DOUBLE_SIMPLE_SEQUENCE = "ringWithDoubleSimpleSequence"
RING_WITH_SINGLE_SIMPLE_SEQUENCE = "ringWithSingleSimpleSequence"
+ RING_WITH_THREE_SIMPLE_SEQUENCE = "ringWithThreeSimpleSequence"
+ RUN_MANUFACTURER_SETTINGS_COMMAND = "runManufacturerSettingsCommand"
SAVE_ALIAS = "saveAlias"
- SET_ABSENCE_MODE = "setAbsenceMode"
+ SEND_IOKEY = "sendIOKey"
+ SEND_PRIVATE = "sendPrivate"
SET_ABSENCE_COOLING_TARGET_TEMPERATURE = "setAbsenceCoolingTargetTemperature"
SET_ABSENCE_END_DATE = "setAbsenceEndDate"
SET_ABSENCE_END_DATE_TIME = "setAbsenceEndDateTime"
SET_ABSENCE_HEATING_TARGET_TEMPERATURE = "setAbsenceHeatingTargetTemperature"
+ SET_ABSENCE_MODE = "setAbsenceMode"
SET_ABSENCE_START_DATE = "setAbsenceStartDate"
SET_ABSENCE_START_DATE_TIME = "setAbsenceStartDateTime"
+ SET_ACTIVE_COOLING_TIME_PROGRAM = "setActiveCoolingTimeProgram"
+ SET_ACTIVE_HEATING_TIME_PROGRAM = "setActiveHeatingTimeProgram"
SET_ACTIVE_MODE = "setActiveMode"
+ SET_AIRPLANE_MODE = "setAirplaneMode"
SET_AIR_DEMAND_MODE = "setAirDemandMode"
+ SET_ALARM_DELAY = "setAlarmDelay"
SET_ALARM_STATUS = "setAlarmStatus"
SET_ALL_MODE_TEMPERATURES = "setAllModeTemperatures"
+ SET_ANTI_LEGIONELLOSIS = "setAntiLegionellosis"
SET_AUTO_MANU_MODE = "setAutoManuMode"
SET_AWAY_MODE_DURATION = "setAwayModeDuration"
+ SET_BOILER_INSTALLATION_OPTION = "setBoilerInstallationOption"
+ SET_BOOST_END_DATE = "setBoostEndDate"
SET_BOOST_MODE = "setBoostMode"
SET_BOOST_MODE_DURATION = "setBoostModeDuration"
SET_BOOST_ON_OFF_STATE = "setBoostOnOffState"
+ SET_BOOST_START_DATE = "setBoostStartDate"
+ SET_CALENDAR = "setCalendar"
+ SET_CIE_COLOR_SPACE_XY = "setCieColorSpaceXY"
SET_CLOSURE = "setClosure"
SET_CLOSURE_AND_LINEAR_SPEED = "setClosureAndLinearSpeed"
SET_CLOSURE_AND_ORIENTATION = "setClosureAndOrientation"
- SET_CONFIG_STATE = "setConfigState"
+ SET_COLOR_TEMPERATURE = "setColorTemperature"
SET_COMFORT_COOLING_TARGET_TEMPERATURE = "setComfortCoolingTargetTemperature"
SET_COMFORT_HEATING_TARGET_TEMPERATURE = "setComfortHeatingTargetTemperature"
SET_COMFORT_TARGET_DHW_TEMPERATURE = "setComfortTargetDHWTemperature"
+ SET_COMFORT_TARGET_TEMPERATURE = "setComfortTargetTemperature"
SET_COMFORT_TEMPERATURE = "setComfortTemperature"
+ SET_COMMUNICATION_TEST = "setCommunicationTest"
+ SET_CONFIG_STATE = "setConfigState"
SET_CONTROL_DHW = "setControlDHW"
SET_CONTROL_DHW_SETTING_TEMPERATURE = "setControlDHWSettingTemperature"
SET_COOLING_ON_OFF = "setCoolingOnOffState"
SET_COOLING_TARGET_TEMPERATURE = "setCoolingTargetTemperature"
+ SET_COUNTRY_CODE = "setCountryCode"
+ SET_CTB = "setCTB"
SET_CURRENT_OPERATING_MODE = "setCurrentOperatingMode"
SET_DATE_TIME = "setDateTime"
+ SET_DELETION_CANCELATION = "setDeletionCancelation"
SET_DEPLOYMENT = "setDeployment"
SET_DEROGATED_TARGET_TEMPERATURE = "setDerogatedTargetTemperature"
SET_DEROGATION = "setDerogation"
@@ -156,47 +343,111 @@ class OverkizCommand(StrEnum):
SET_DEROGATION_TIME = "setDerogationTime"
SET_DHW_MODE = "setDHWMode"
SET_DHW_ON_OFF_STATE = "setDHWOnOffState"
+ SET_DRYING_DURATION = "setDryingDuration"
SET_ECO_COOLING_TARGET_TEMPERATURE = "setEcoCoolingTargetTemperature"
SET_ECO_HEATING_TARGET_TEMPERATURE = "setEcoHeatingTargetTemperature"
SET_ECO_TARGET_DHW_TEMPERATURE = "setEcoTargetDHWTemperature"
+ SET_ECO_TARGET_TEMPERATURE = "setEcoTargetTemperature"
SET_ECO_TEMPERATURE = "setEcoTemperature"
+ SET_ELECTRICAL_EXTRA_MANAGEMENT = "setElectricalExtraManagement"
SET_EXPECTED_NUMBER_OF_SHOWER = "setExpectedNumberOfShower"
+ SET_EXPECTED_PRESENCE = "setExpectedPresence"
+ SET_EXTRACTION_OPTION = "setExtractionOption"
SET_FORCE_HEATING = "setForceHeating"
+ SET_FROST_PROTECTION_TARGET_TEMPERATURE = "setFrostProtectionTargetTemperature"
+ SET_GROUP_VOLUME = "setGroupVolume"
+ SET_HALTED_TARGET_TEMPERATURE = "setHaltedTargetTemperature"
SET_HEATING_COOLING_AUTO_SWITCH = "setHeatingCoolingAutoSwitch"
SET_HEATING_LEVEL = "setHeatingLevel"
+ SET_HEATING_LEVEL_FOR_TRIGGER = "setHeatingLevelForTrigger"
+ SET_HEATING_LEVEL_WITH_TIMER = "setHeatingLevelWithTimer"
SET_HEATING_ON_OFF = "setHeatingOnOffState"
SET_HEATING_TARGET_TEMPERATURE = "setHeatingTargetTemperature"
SET_HOLIDAYS = "setHolidays"
+ SET_HOLIDAYS_TARGET_TEMPERATURE = "setHolidaysTargetTemperature"
+ SET_HSB = "setHSB"
+ SET_HUE_AND_SATURATION = "setHueAndSaturation"
+ SET_INSTALLATION = "setInstallation"
SET_INTENSITY = "setIntensity"
+ SET_INTENSITY_WITH_TIMER = "setIntensityWithTimer"
+ SET_INTERNAL_TIMER = "setInternalTimer"
+ SET_INTRUSION_DETECTED = "setIntrusionDetected"
SET_LEVEL = "setLevel"
+ SET_LIGHTING_LED_MODEM_MODE = "setLightingLedModemMode"
+ SET_LIGHTING_LED_POD_MODE = "setLightingLedPodMode"
SET_LOWER_CLOSURE = "setLowerClosure"
SET_LOWER_POSITION = "setLowerPosition"
SET_MANU_AND_SET_POINT_MODES = "setManuAndSetPointModes"
+ SET_MAXIMUM_COOLING_TARGET_TEMPERATURE = "setMaximumCoolingTargetTemperature"
+ SET_MAXIMUM_HEATING_TARGET_TEMPERATURE = "setMaximumHeatingTargetTemperature"
+ SET_MAXIMUM_TEMPERATURE = "setMaximumTemperature"
+ SET_MEDIA_ELEMENT = "setMediaElement"
+ SET_MEMORIZED1_ORIENTATION = "setMemorized1Orientation"
SET_MEMORIZED_1_POSITION = "setMemorized1Position"
SET_MEMORIZED_SIMPLE_VOLUME = "setMemorizedSimpleVolume"
+ SET_MINIMUM_COOLING_TARGET_TEMPERATURE = "setMinimumCoolingTargetTemperature"
+ SET_MINIMUM_HEATING_TARGET_TEMPERATURE = "setMinimumHeatingTargetTemperature"
+ SET_MINIMUM_TEMPERATURE = "setMinimumTemperature"
+ SET_MODE = "setMode"
+ SET_MODEM_LED_OFF = "setModemLedOff"
+ SET_MODEM_LED_ON = "setModemLedOn"
SET_MODE_TEMPERATURE = "setModeTemperature"
SET_NAME = "setName"
+ SET_NATIVE_LEVELS_LIST = "setNativeLevelsList"
+ SET_OCCUPANCY = "setOccupancy"
+ SET_OCCUPANCY_ACTIVATION = "setOccupancyActivation"
SET_ON_OFF = "setOnOff"
+ SET_OPEN_WINDOW_DETECTION_ACTIVATION = "setOpenWindowDetectionActivation"
SET_OPERATING_MODE = "setOperatingMode"
+ SET_OPERATING_RANGE = "setOperatingRange"
+ SET_OPERATOR_CONFIG = "setOperatorConfig"
SET_ORIENTATION = "setOrientation"
SET_PASS_APC_COOLING_MODE = "setPassAPCCoolingMode"
SET_PASS_APC_DHW_MODE = "setPassAPCDHWMode"
SET_PASS_APC_HEATING_MODE = "setPassAPCHeatingMode"
SET_PASS_APC_OPERATING_MODE = "setPassAPCOperatingMode"
+ SET_PEAK_NOTICE = "setPeakNotice"
+ SET_PEAK_WARNING = "setPeakWarning"
SET_PEDESTRIAN_POSITION = "setPedestrianPosition"
+ SET_PICTO_VALUE = "setPictoValue"
+ SET_POD_LED_OFF = "setPodLedOff"
+ SET_POD_LED_ON = "setPodLedOn"
SET_POSITION = "setPosition"
+ SET_POSITION_AND_LINEAR_SPEED = "setPositionAndLinearSpeed"
+ SET_PREVIOUS_TARGET_TEMPERATURE = "setPreviousTargetTemperature"
+ SET_PROGRAMMING_SLOTS = "setProgrammingSlots"
+ SET_RATE_MANAGEMENT = "setRateManagement"
SET_RGB = "setRGB"
+ SET_ROOM_DELETION_THRESHOLD = "setRoomDeletionThreshold"
SET_SCHEDULING_TYPE = "setSchedulingType"
+ SET_SECURED_ORIENTATION = "setSecuredOrientation"
SET_SECURED_POSITION = "setSecuredPosition"
SET_SECURED_POSITION_TEMPERATURE = "setSecuredPositionTemperature"
- SET_TARGET_MODE = "setTargetMode"
+ SET_SETPOINT_LOWERING_TEMPERATURE_IN_PROG_MODE = (
+ "setSetpointLoweringTemperatureInProgMode"
+ )
+ SET_SET_POINT_TYPE_AND_TARGET_TEMPERATURE = "setSetPointTypeAndTargetTemperature"
+ SET_SIMPIN_CODE = "setSIMPinCode"
+ SET_SIREN_STATUS = "setSirenStatus"
+ SET_SMART_GRID_OPTION = "setSmartGridOption"
SET_TARGET_ALARM_MODE = "setTargetAlarmMode"
- SET_TARGET_TEMPERATURE = "setTargetTemperature"
SET_TARGET_DHW_TEMPERATURE = "setTargetDHWTemperature"
+ SET_TARGET_INFRA_CONFIG = "setTargetInfraConfig"
+ SET_TARGET_MODE = "setTargetMode"
+ SET_TARGET_MODE_ALIAS = "setTargetModeAlias"
+ SET_TARGET_TEMPERATURE = "setTargetTemperature"
+ SET_TARGET_WAKE_UP_INTERVAL = "setTargetWakeUpInterval"
+ SET_TEMPERATURE_PROBE_CALIBRATION_OFFSET = "setTemperatureProbeCalibrationOffset"
+ SET_THERMOSTAT_SETPOINT = "setThermostatSetpoint"
SET_THERMOSTAT_SETTING_CONTROL_ZONE_1 = "setThermostatSettingControlZone1"
+ SET_TIMELINE_POSITION = "setTimelinePosition"
+ SET_TIME_PROGRAM = "setTimeProgram"
SET_TIME_PROGRAM_BY_ID = "setTimeProgramById"
+ SET_TOWEL_DRYER_BOOST_MODE_DURATION = "setTowelDryerBoostModeDuration"
SET_TOWEL_DRYER_OPERATING_MODE = "setTowelDryerOperatingMode"
SET_TOWEL_DRYER_TEMPORARY_STATE = "setTowelDryerTemporaryState"
+ SET_TOWEL_DRYER_TIME_PROGRAM = "setTowelDryerTimeProgram"
+ SET_TWINNING_EXIT = "setTwinningExit"
SET_UPPER_AND_LOWER_CLOSURE = "setUpperAndLowerClosure"
SET_UPPER_AND_LOWER_POSITION = "setUpperAndLowerPosition"
SET_UPPER_CLOSURE = "setUpperClosure"
@@ -204,21 +455,53 @@ class OverkizCommand(StrEnum):
SET_VALVE_SETTINGS = "setValveSettings"
SET_VENTILATION_CONFIGURATION_MODE = "setVentilationConfigurationMode"
SET_VENTILATION_MODE = "setVentilationMode"
+ SET_VOLUME = "setVolume"
+ SET_WAKE_UP_INTERVAL = "setWakeUpInterval"
SET_WATER_TARGET_TEMPERATURE = "setWaterTargetTemperature"
+ SET_WATER_TEMPERATURE = "setWaterTemperature"
+ SET_WEATHER_STATUS = "setWeatherStatus"
+ SET_WIFI_MODE = "setWifiMode"
+ SET_XYB = "setXYB"
+ SHARE_NETWORK = "shareNetwork"
+ SHORT_BIP = "shortBip"
+ SIREN = "siren"
STANDARD = "standard"
+ START = "start"
+ START_EXCLUSION = "startExclusion"
+ START_EXCLUSION_WITH_MODE = "startExclusionWithMode"
START_IDENTIFY = "startIdentify"
+ START_INCLUSION = "startInclusion"
+ START_INCLUSION_WITH_MODE = "startInclusionWithMode"
+ START_INCLUSION_WITH_MODE_AND_SECURITY_LEVEL = (
+ "startInclusionWithModeAndSecurityLevel"
+ )
+ START_LEARN = "startLearn"
+ START_LEARN_WITH_MODE = "startLearnWithMode"
STOP = "stop"
+ STOP_BLINK_MODEM_LED = "stopBlinkModemLed"
+ STOP_EXCLUSION = "stopExclusion"
STOP_IDENTIFY = "stopIdentify"
+ STOP_INCLUSION = "stopInclusion"
+ STOP_LEARN = "stopLearn"
+ TAKE_PICTURE = "takePicture"
+ TAKE_PICTURE_SEQUENCE = "takePictureSequence"
TEST = "test"
TILT = "tilt"
TILT_DOWN = "tiltDown"
TILT_NEGATIVE = "tiltNegative"
TILT_POSITIVE = "tiltPositive"
TILT_UP = "tiltUp"
+ TRESPASS = "trespass"
+ UNBIND = "unbind"
UNDEPLOY = "undeploy"
UNINSTALLED = "uninstalled"
UNLOCK = "unlock"
+ UNMUTE = "unmute"
+ UNMUTE_GROUP = "unmuteGroup"
UNPAIR_ALL_ONE_WAY_CONTROLLERS = "unpairAllOneWayControllers"
+ UNPAIR_ALL_ONE_WAY_CONTROLLERS_AND_DELETE_NODE = (
+ "unpairAllOneWayControllersAndDeleteNode"
+ )
UNPAIR_ONE_WAY_CONTROLLER = "unpairOneWayController"
UP = "up"
UPDATE = "update"
@@ -235,82 +518,141 @@ class OverkizCommandParam(StrEnum):
A = "A"
ABSENCE = "absence"
+ ACCUMULATION_DOMESTIC_HOT_WATER = "accumulationDomesticHotWater"
ACTIVE = "active"
ADJUSTMENT = "adjustment"
- ANTIFREEZE = "antifreeze"
+ AIR_CONDITIONING = "airConditioning"
+ ALERT = "alert"
ALWAYS = "always"
+ ANTIFREEZE = "antifreeze"
ARMED = "armed"
ARMED_DAY = "armedDay"
ARMED_NIGHT = "armedNight"
AT_HOME_MODE = "atHomeMode"
AUTO = "auto"
+ AUTOCHANGEOVER = "autochangeover"
AUTOCOOLING = "autocooling"
AUTOHEATING = "autoheating"
+ AUTO_COOLING = "autoCooling"
+ AUTO_HEATING = "autoHeating"
AUTO_MODE = "autoMode"
AVAILABLE = "available"
AWAY = "away"
+ AWAYCOOL = "awaycool"
+ AWAYHEAT = "awayheat"
AWAY_MODE = "awayMode"
B = "B"
+ BASE = "base"
BASIC = "basic"
- BY_PASS = "by_pass"
+ BOILER = "boiler"
+ BOILER_OPTIMISING = "boilerOptimising"
+ BOILER_PRIORITY = "boilerPriority"
BOOST = "boost"
BOTH = "both"
+ BY_PASS = "by_pass"
C = "C"
+ CIRCULATOR = "circulator"
+ CLEAN = "clean"
CLOSE = "close"
CLOSED = "closed"
COMFORT = "comfort"
COMFORT_1 = "comfort-1"
COMFORT_2 = "comfort-2"
+ COMFORT_LEVEL1 = "comfortLevel1"
+ COMFORT_LEVEL2 = "comfortLevel2"
+ COMFORT_LEVEL3 = "comfortLevel3"
+ COMFORT_LEVEL4 = "comfortLevel4"
+ CONVECTOR = "convector"
COOLING = "cooling"
+ CT = "ct"
+ CYCLIC = "cyclic"
+ DATE = "date"
DATE_SCHEDULING = "dateScheduling"
DAY_OFF = "day-off"
- DEROGATION = "derogation"
+ DEACTIVE = "deactive"
DEAD = "dead"
DEHUMIDIFY = "dehumidify"
+ DELETION_CANCELATION = "deletion cancelation" # value with space
+ DEROGATION = "derogation"
DETECTED = "detected"
- DISARMED = "disarmed"
+ DIRTY = "dirty"
DISABLE = "disable"
DISABLED = "disabled"
+ DISARMED = "disarmed"
+ DOUBLE_FLOW_CONTROLLED_MECHANICAL_VENTILATION = (
+ "doubleFlowControlledMechanicalVentilation"
+ )
+ DOUBLE_PRESS = "doublePress"
+ DOWN = "down"
+ DRY = "dry"
DRYING = "drying"
ECO = "eco"
ENABLE = "enable"
ENABLED = "enabled"
+ ENERGYCOOL = "energycool"
+ ENERGYHEAT = "energyheat"
ENERGY_DEMAND_STATUS = "energyDemandStatus"
+ ENVIRONMENT_PROTECTION = "environmentProtection"
EXTERNAL = "external"
EXTERNAL_GATEWAY = "externalGateway"
EXTERNAL_SCHEDULING = "externalScheduling"
EXTERNAL_SETPOINT = "externalSetpoint"
+ EXTRA_BOILER = "extraBoiler"
+ EXTRA_SOLAR = "extraSolar"
+ FAILED = "failed"
+ FALSE = "false"
FAN = "fan"
+ FAST_EXTRACTION_SPEED = "fastExtractionSpeed"
FINISHED = "finished"
+ FORBIDDEN = "forbidden"
+ FORWARD = "forward"
FREE = "free"
FREEZE_MODE = "freezeMode"
FROSTPROTECTION = "frostprotection"
FULL = "full"
+ FULLPOWER = "fullpower"
FULL_CLOSED = "full_closed"
FULL_OPEN = "full_open"
- FURTHER_NOTICE = "further_notice"
+ FURNACE = "furnace"
+ FURTHER_NOTICE = "furtherNotice"
GEOFENCING_MODE = "geofencingMode"
+ GOOD = "good"
+ HEATER = "heater"
HEATING = "heating"
HEATING_AND_COOLING = "heatingAndCooling"
HEATING_AND_COOLING_COMMON_SCHEDULING = "heatingAndCoolingCommonScheduling"
HEATING_AND_COOLING_SEPARATED_SCHEDULING = "heatingAndCoolingSeparatedScheduling"
+ HEAT_PUMP = "heatPump"
+ HEAT_PUMP_OPTIMISING = "heatPumpOptimising"
+ HEAT_PUMP_PRIORITY = "heatPumpPriority"
HI = "hi"
HIGH = "high"
HIGHEST = "highest"
- HIGH_DEMAND = "high demand" # not a typo...
+ HIGH_DEMAND = "high demand" # value with space
HOLIDAYS = "holidays"
HORIZONTAL = "horizontal"
+ HS = "hs"
+ HUMAN_PROTECTION = "humanProtection"
+ HYBRID = "hybrid"
INACTIVE = "inactive"
INCREASE = "increase"
INTERNAL = "internal"
INTERNAL_SCHEDULING = "internalScheduling"
+ IN_PROGRESS = "inProgress"
+ KEPT = "kept"
+ KO = "KO"
LO = "lo"
LOCAL_USER = "localUser"
LOCKED = "locked"
LOCK_KEY = "lock_key"
+ LONG_PEAK = "long peak" # value with space
+ LONG_PEAK_WARNING = "long peak warning" # value with space
+ LONG_PRESS = "longPress"
+ LOST = "lost"
LOW = "low"
LOWSPEED = "lowspeed"
LOW_BATTERY = "lowBattery"
+ LOW_EXTRACTION_SPEED = "lowExtractionSpeed"
LSC = "LSC"
MAINTENANCE_REQUIRED = "maintenanceRequired"
MANU = "manu"
@@ -318,56 +660,109 @@ class OverkizCommandParam(StrEnum):
MANUAL_ECO_ACTIVE = "manualEcoActive"
MANUAL_ECO_INACTIVE = "manualEcoInactive"
MANUAL_MODE = "manualMode"
+ MAX = "max"
MAX_SETPOINT = "max_setpoint"
MED = "med"
MEDIUM = "medium"
MEMORIZED_VOLUME = "memorizedVolume"
MIN_SETPOINT = "min_setpoint"
- NORMAL = "normal"
+ MOIST = "moist"
+ MYSELF = "myself"
+ NEXT_MODE = "nextMode"
+ NO = "no"
NONE = "none"
+ NORMAL = "normal"
NOT_DETECTED = "notDetected"
NO_DEFECT = "noDefect"
+ NO_DELETION_CANCELATION = "no deletion cancelation" # value with space
+ NO_EXTRACTION = "noExtraction"
+ NO_PEAK = "no peak" # value with space
+ NO_PERSON_INSIDE = "noPersonInside"
+ NO_WARNING = "no warning" # value with space
NUMBER_OF_DAYS_SCHEDULING = "numberOfDaysScheduling"
OFF = "off"
+ OFFLINE = "offline"
+ OK = "OK"
ON = "on"
+ ONLINE = "online"
+ ONLY_THERMODYNAMIC = "onlyThermodynamic"
+ ON_OFF_LIGHT = "onOffLight"
OPEN = "open"
OPENED = "opened"
OPEN_WINDOW = "open_window"
+ PAC24H_ELEC24H = "pac24h_elec24h"
+ PAC_PROG_ELEC_PROG = "pacProg_elecProg"
PAIRING = "pairing"
PARTIAL = "partial"
PARTIAL_1 = "partial1"
PARTIAL_2 = "partial2"
+ PAUSE = "pause"
PEDESTRIAN = "pedestrian"
PENDING = "pending"
PERFORMANCE = "performance"
PERMANENT_HEATING = "permanentHeating"
PERSON_INSIDE = "personInside"
+ PLAYING = "playing"
+ PRESSED = "pressed"
PROG = "prog"
PROGRAM = "program"
+ RAIN = "rain"
+ RECOMMENDED = "recommended"
+ REGISTERED = "registered"
RELAUNCH = "relaunch"
RESET = "reset"
+ REWIND = "rewind"
SAAC = "SAAC"
SECURED = "secured"
+ SECURITY = "security"
SFC = "SFC"
SHORT = "short"
+ SHORT_PEAK = "short peak" # value with space
+ SHORT_PEAK_WARNING = "short peak warning" # value with space
SILENCE = "silence"
SILENT = "silent"
+ SIMPLE_PRESS = "simplePress"
+ SINGLE_FLOW_CONTROLLED_MECHANICAL_VENTILATION = (
+ "singleFlowControlledMechanicalVentilation"
+ )
SLEEPING_MODE = "sleepingMode"
+ SOS = "sos"
STANDARD = "standard"
STANDBY = "standby"
+ STARTED = "started"
STOP = "stop"
+ STOPPED = "stopped"
SUDDEN_DROP_MODE = "suddenDropMode"
+ TEMPERATURE = "temperature"
TEMPERATURE_OFFSET = "temperature_offset"
+ THERMODYNAMIC_DOMESTIC_HOT_WATER = "thermodynamicDomesticHotWater"
TILT = "tilt"
- TILT_POSITIVE = "tiltPositive"
TILT_NEGATIVE = "tiltNegative"
+ TILT_POSITIVE = "tiltPositive"
+ TIMER = "timer"
+ TOP = "top"
TOTAL = "total"
+ TRIPLE_PRESS = "triplePress"
+ TRUE = "true"
+ UNAVAILABLE = "unavailable"
UNDETECTED = "undetected"
+ UNINSTALLED = "uninstalled"
+ UNKNOWN = "unknown"
+ UNSUITABLE = "unsuitable"
+ UP = "up"
UPS = "UPS"
+ USER = "user"
+ USER_LEVEL1 = "userLevel1"
+ USER_LEVEL2 = "userLevel2"
VERTICAL = "vertical"
+ VERY_LONG_PRESS = "veryLongPress"
VERY_LOW = "verylow"
+ WANTED = "wanted"
+ WIND = "wind"
+ XY = "xy"
ZONE_1 = "zone1"
ZONE_2 = "zone2"
+ ZONE_CONTROLLER = "zoneController"
@unique
diff --git a/pyoverkiz/enums/execution.py b/pyoverkiz/enums/execution.py
index 3096e4b0..bb394df5 100644
--- a/pyoverkiz/enums/execution.py
+++ b/pyoverkiz/enums/execution.py
@@ -1,21 +1,12 @@
"""Execution related enums (types, states and subtypes)."""
-import logging
-import sys
-from enum import unique
+from enum import StrEnum, unique
-_LOGGER = logging.getLogger(__name__)
-
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from pyoverkiz.enums.base import UnknownEnumMixin
@unique
-class ExecutionType(StrEnum):
+class ExecutionType(UnknownEnumMixin, StrEnum):
"""High-level execution categories returned by the API."""
UNKNOWN = "UNKNOWN"
@@ -26,14 +17,9 @@ class ExecutionType(StrEnum):
RAW_TRIGGER_SERVER = "Raw trigger (Server)"
RAW_TRIGGER_GATEWAY = "Raw trigger (Gateway)"
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
-
@unique
-class ExecutionState(StrEnum):
+class ExecutionState(UnknownEnumMixin, StrEnum):
"""Execution lifecycle states."""
UNKNOWN = "UNKNOWN"
@@ -46,14 +32,9 @@ class ExecutionState(StrEnum):
QUEUED_GATEWAY_SIDE = "QUEUED_GATEWAY_SIDE"
QUEUED_SERVER_SIDE = "QUEUED_SERVER_SIDE"
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
-
@unique
-class ExecutionSubType(StrEnum):
+class ExecutionSubType(UnknownEnumMixin, StrEnum):
"""Subtypes for execution reasons or sources."""
UNKNOWN = "UNKNOWN"
@@ -71,8 +52,3 @@ class ExecutionSubType(StrEnum):
NO_ERROR = "NO_ERROR"
P2P_COMMAND_REGULATION = "P2P_COMMAND_REGULATION"
TIME_TRIGGER = "TIME_TRIGGER"
-
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
diff --git a/pyoverkiz/enums/gateway.py b/pyoverkiz/enums/gateway.py
index feb43893..9d1b2811 100644
--- a/pyoverkiz/enums/gateway.py
+++ b/pyoverkiz/enums/gateway.py
@@ -1,21 +1,12 @@
"""Enums for gateway types and related helpers."""
-import logging
-import sys
-from enum import IntEnum, unique
+from enum import IntEnum, StrEnum, unique
-_LOGGER = logging.getLogger(__name__)
-
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from pyoverkiz.enums.base import UnknownEnumMixin
@unique
-class GatewayType(IntEnum):
+class GatewayType(UnknownEnumMixin, IntEnum):
"""Enumeration of known gateway types returned by Overkiz."""
UNKNOWN = -1
@@ -71,22 +62,19 @@ class GatewayType(IntEnum):
TAHOMA_SWITCH_CH = 126
TAHOMA_SWITCH_SC = 128
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
-
@property
def beautify_name(self) -> str:
"""Return a human friendly name for the gateway type."""
- name = self.name.replace("_", " ").title()
- name = name.replace("Tahoma", "TaHoma")
- name = name.replace("Rts", "RTS")
- return name
+ return (
+ self.name.replace("_", " ")
+ .title()
+ .replace("Tahoma", "TaHoma")
+ .replace("Rts", "RTS")
+ )
@unique
-class GatewaySubType(IntEnum):
+class GatewaySubType(UnknownEnumMixin, IntEnum):
"""Sub-type enumeration for gateways to identify specific models/variants."""
UNKNOWN = -1
@@ -108,18 +96,15 @@ class GatewaySubType(IntEnum):
TAHOMA_SECURITY_PRO = 16
# TAHOMA_BOX_C_IO = 12 # Note: This is likely 17, but tahomalink.com lists it as 12
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
-
@property
def beautify_name(self) -> str:
"""Return a human friendly name for the gateway sub-type."""
- name = self.name.replace("_", " ").title()
- name = name.replace("Tahoma", "TaHoma")
- name = name.replace("Rts", "RTS")
- return name
+ return (
+ self.name.replace("_", " ")
+ .title()
+ .replace("Tahoma", "TaHoma")
+ .replace("Rts", "RTS")
+ )
@unique
diff --git a/pyoverkiz/enums/general.py b/pyoverkiz/enums/general.py
index 7c25ac9e..4157e4d5 100644
--- a/pyoverkiz/enums/general.py
+++ b/pyoverkiz/enums/general.py
@@ -3,18 +3,9 @@
# ruff: noqa: S105
# Enum values contain "TOKEN" in API event names, not passwords
-import logging
-import sys
-from enum import IntEnum, unique
+from enum import IntEnum, StrEnum, unique
-_LOGGER = logging.getLogger(__name__)
-
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from pyoverkiz.enums.base import UnknownEnumMixin
@unique
@@ -48,7 +39,7 @@ class DataType(IntEnum):
@unique
-class FailureType(IntEnum):
+class FailureType(UnknownEnumMixin, IntEnum):
"""Failure type codes returned by the API."""
UNKNOWN = -1
@@ -256,14 +247,9 @@ class FailureType(IntEnum):
TIME_OUT_ON_COMMAND_PROGRESS = 20003
DEVICE_NO_ANSWER = 60004
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
-
@unique
-class EventName(StrEnum):
+class EventName(UnknownEnumMixin, StrEnum):
"""Enumeration of event names emitted by Overkiz."""
UNKNOWN = "Unknown"
@@ -448,8 +434,3 @@ class EventName(StrEnum):
ZONE_CREATED = "ZoneCreatedEvent"
ZONE_DELETED = "ZoneDeletedEvent"
ZONE_UPDATED = "ZoneUpdatedEvent"
-
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
diff --git a/pyoverkiz/enums/measured_value_type.py b/pyoverkiz/enums/measured_value_type.py
index aa011950..5c6911c7 100644
--- a/pyoverkiz/enums/measured_value_type.py
+++ b/pyoverkiz/enums/measured_value_type.py
@@ -1,14 +1,6 @@
"""Measured value type enums used to interpret numeric sensor data."""
-import sys
-from enum import unique
-
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from enum import StrEnum, unique
@unique
diff --git a/pyoverkiz/enums/protocol.py b/pyoverkiz/enums/protocol.py
index 70816828..7f958db3 100644
--- a/pyoverkiz/enums/protocol.py
+++ b/pyoverkiz/enums/protocol.py
@@ -1,21 +1,16 @@
-"""Protocol enums describe device URL schemes used by Overkiz."""
+"""Protocol enums describe device URL schemes used by Overkiz.
-import logging
-import sys
-from enum import unique
+THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
+Run `uv run utils/generate_enums.py` to regenerate.
+"""
-_LOGGER = logging.getLogger(__name__)
+from enum import StrEnum, unique
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from pyoverkiz.enums.base import UnknownEnumMixin
@unique
-class Protocol(StrEnum):
+class Protocol(UnknownEnumMixin, StrEnum):
"""Protocol used by Overkiz.
Values have been retrieved from /reference/protocolTypes
@@ -23,38 +18,33 @@ class Protocol(StrEnum):
UNKNOWN = "unknown"
- AUGUST = "august"
- CAMERA = "camera"
- ELIOT = "eliot"
- ENOCEAN = "enocean"
+ AUGUST = "august" # 59: August Webservices
+ CAMERA = "camera" # 13: Generic Camera Control Protocol
+ ELIOT = "eliot" # 45: Eliot Webservices
+ ENOCEAN = "enocean" # 7: EnOcean
HLRR_WIFI = "hlrrwifi"
- HOMEKIT = "homekit"
- HUE = "hue"
- INTERNAL = "internal"
- IO = "io"
- JSW = "jsw"
- MODBUS = "modbus"
+ HOMEKIT = "homekit" # 48: HOMEKIT
+ HUE = "hue" # 22: Philips HUE - Personal Wireless Lighting
+ INTERNAL = "internal" # 29: Kizbox Internal Modules
+ IO = "io" # 1: IO HomeControl©
+ JSW = "jsw" # 30: JSW Webservices
+ MODBUS = "modbus" # 20: Modbus
MODBUSLINK = "modbuslink"
- MYFOX = "myfox"
- NETATMO = "netatmo"
- OGCP = "ogcp"
- OGP = "ogp"
- OPENDOORS = "opendoors"
- OVP = "ovp"
- PROFALUX_868 = "profalux868"
- RAMSES = "ramses"
- RTD = "rtd"
- RTDS = "rtds"
+ MYFOX = "myfox" # 25: MyFox Webservices
+ NETATMO = "netatmo" # 38: Netatmo Webservices
+ OGCP = "ogcp" # 62: Overkiz Generic Cloud Protocol
+ OGP = "ogp" # 56: Overkiz Generic Protocol
+ OPENDOORS = "opendoors" # 35: OpenDoors Webservices
+ OVP = "ovp" # 14: OVERKIZ Radio Protocol
+ PROFALUX_868 = "profalux868" # 50: Profalux 868
+ RAMSES = "ramses" # 6: Ramses II (Honeywell)
+ RTD = "rtd" # 5: Domis RTD - Actuator
+ RTDS = "rtds" # 11: Domis RTD - Sensor
RTN = "rtn"
- RTS = "rts"
- SOMFY_THERMOSTAT = "somfythermostat"
- UPNP_CONTROL = "upnpcontrol"
- VERISURE = "verisure"
- WISER = "wiser"
- ZIGBEE = "zigbee"
- ZWAVE = "zwave"
-
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported protocol {value} has been returned for {cls}")
- return cls.UNKNOWN
+ RTS = "rts" # 2: Somfy RTS
+ SOMFY_THERMOSTAT = "somfythermostat" # 39: Somfy Thermostat Webservice
+ UPNP_CONTROL = "upnpcontrol" # 43: UPnP Control
+ VERISURE = "verisure" # 23: Verisure Webservices
+ WISER = "wiser" # 54: Schneider Wiser
+ ZIGBEE = "zigbee" # 3: Zigbee
+ ZWAVE = "zwave" # 8: Z-Wave
diff --git a/pyoverkiz/enums/server.py b/pyoverkiz/enums/server.py
index 3d495666..9970bddb 100644
--- a/pyoverkiz/enums/server.py
+++ b/pyoverkiz/enums/server.py
@@ -1,14 +1,6 @@
"""Server and API type enums used to select target Overkiz endpoints."""
-import sys
-from enum import unique
-
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from enum import StrEnum, unique
@unique
diff --git a/pyoverkiz/enums/state.py b/pyoverkiz/enums/state.py
index fe8ce530..1988fc27 100644
--- a/pyoverkiz/enums/state.py
+++ b/pyoverkiz/enums/state.py
@@ -3,15 +3,7 @@
# ruff: noqa: S105
# Enum values contain "PASS" or "TOKEN" in API names, not passwords
-import sys
-from enum import unique
-
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from enum import StrEnum, unique
@unique
diff --git a/pyoverkiz/enums/ui.py b/pyoverkiz/enums/ui.py
index efa41d25..3d2b918a 100644
--- a/pyoverkiz/enums/ui.py
+++ b/pyoverkiz/enums/ui.py
@@ -1,35 +1,31 @@
-"""UI enums for classes and widgets used to interpret device UI metadata."""
+"""UI enums for classes and widgets used to interpret device UI metadata.
+
+THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
+Run `uv run utils/generate_enums.py` to regenerate.
+"""
# ruff: noqa: S105
# Enum values contain "PASS" in API names (e.g. PassAPC), not passwords
-import logging
-import sys
-from enum import unique
-
-_LOGGER = logging.getLogger(__name__)
+from enum import StrEnum, unique
-# Since we support Python versions lower than 3.11, we use
-# a backport for StrEnum when needed.
-if sys.version_info >= (3, 11):
- from enum import StrEnum
-else:
- from backports.strenum import StrEnum # type: ignore[import]
+from pyoverkiz.enums.base import UnknownEnumMixin
@unique
-class UIClass(StrEnum):
+class UIClass(UnknownEnumMixin, StrEnum):
"""Enumeration of UI classes used to describe device categories and behaviors."""
- # A list of all defined UI classes from /reference/ui/classes
+ UNKNOWN = "Unknown"
+
ADJUSTABLE_SLATS_ROLLER_SHUTTER = "AdjustableSlatsRollerShutter"
AIR_SENSOR = "AirSensor"
ALARM = "Alarm"
AWNING = "Awning"
BALLAST = "Ballast"
CAMERA = "Camera"
- CARBON_DIOXIDE_SENSOR = "CarbonDioxideSensor"
CAR_BUTTON_SENSOR = "CarButtonSensor"
+ CARBON_DIOXIDE_SENSOR = "CarbonDioxideSensor"
CIRCUIT_BREAKER = "CircuitBreaker"
CONFIGURATION_COMPONENT = "ConfigurationComponent"
CONSUMPTION_SENSOR = "ConsumptionSensor"
@@ -57,8 +53,8 @@ class UIClass(StrEnum):
LIGHT = "Light"
LIGHT_SENSOR = "LightSensor"
MUSIC_PLAYER = "MusicPlayer"
- NOISE_SENSOR = "NoiseSensor"
NETWORK_COMPONENT = "NetworkComponent"
+ NOISE_SENSOR = "NoiseSensor"
OCCUPANCY_SENSOR = "OccupancySensor"
ON_OFF = "OnOff"
OVEN = "Oven"
@@ -68,6 +64,8 @@ class UIClass(StrEnum):
RAIN_SENSOR = "RainSensor"
REMOTE_CONTROLLER = "RemoteController"
ROLLER_SHUTTER = "RollerShutter"
+ SCENE = "Scene"
+ SCENE_LAUNCHER_CONTROLLER = "SceneLauncherController"
SCREEN = "Screen"
SHUTTER = "Shutter"
SIREN = "Siren"
@@ -76,11 +74,12 @@ class UIClass(StrEnum):
SUN_SENSOR = "SunSensor"
SWIMMING_POOL = "SwimmingPool"
SWINGING_SHUTTER = "SwingingShutter"
+ SWITCH = "Switch"
TEMPERATURE_SENSOR = "TemperatureSensor"
THERMAL_ENERGY_SENSOR = "ThermalEnergySensor"
THIRD_PARTY_GATEWAY = "ThirdPartyGateway"
VENETIAN_BLIND = "VenetianBlind"
- VENTILATION_SYTEM = "VentilationSystem"
+ VENTILATION_SYSTEM = "VentilationSystem"
WASHING_MACHINE = "WashingMachine"
WATER_HEATING_SYSTEM = "WaterHeatingSystem"
WATER_SENSOR = "WaterSensor"
@@ -90,19 +89,13 @@ class UIClass(StrEnum):
WINDOW = "Window"
WINDOW_HANDLE = "WindowHandle"
- UNKNOWN = "unknown"
-
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
-
@unique
-class UIWidget(StrEnum):
+class UIWidget(UnknownEnumMixin, StrEnum):
"""Enumeration of UI widgets used by Overkiz for device presentation."""
- # A list of all defined UI widgets from /reference/ui/widgets
+ UNKNOWN = "Unknown"
+
AIR_FLOW_SENSOR = "AirFlowSensor"
AIR_QUALITY_SENSOR = "AirQualitySensor"
ALARM_PANEL_CONTROLLER = "AlarmPanelController"
@@ -130,10 +123,13 @@ class UIWidget(StrEnum):
BIOCLIMATIC_PERGOLA = "BioclimaticPergola"
CO2_SENSOR = "CO2Sensor"
CO_SENSOR = "COSensor"
+ CAMERA = "Camera"
CAR_BUTTON_SENSOR = "CarButtonSensor"
CAR_LOCK = "CarLock"
CARD_SWITCH = "CardSwitch"
CIRCUIT_BREAKER = "CircuitBreaker"
+ CIRCUIT_BREAKER_PEAK_AND_OFF_PEAK = "CircuitBreakerPeakAndOffPeak"
+ CONFIGURATION_SENSOR = "ConfigurationSensor"
CONTACT_SENSOR = "ContactSensor"
COTHERM_THERMOSTAT = "CothermThermostat"
CUMULATIVE_ELECTRIC_POWER_CONSUMPTION_SENSOR = (
@@ -153,7 +149,7 @@ class UIWidget(StrEnum):
CURTAIN_TRACK_UNO = "CurtainTrackUno"
CYCLIC_GARAGE_DOOR = "CyclicGarageDoor"
CYCLIC_GENERIC = "CyclicGeneric"
- CYCLIC_SWINGING_GATE_OPENER = "CyclicSwingingGateOpener "
+ CYCLIC_SWINGING_GATE_OPENER = "CyclicSwingingGateOpener"
DHW_SET_POINT = "DHWSetPoint"
DE_DIETRICH_BOILER = "DeDietrichBoiler"
DE_DIETRICH_DHW = "DeDietrichDHW"
@@ -168,7 +164,7 @@ class UIWidget(StrEnum):
DIMMER_LIGHT = "DimmerLight"
DIMMER_ON_OFF = "DimmerOnOff"
DIMMER_ON_OFF_LIGHT = "DimmerOnOffLight"
- DIMMER_RGBCOLOURED_LIGHT = "DimmerRGBColouredLight"
+ DIMMER_RGB_COLOURED_LIGHT = "DimmerRGBColouredLight"
DIMPLEX_VENTILATION_INLET_OUTLET = "DimplexVentilationInletOutlet"
DISCRETE_EXTERIOR_HEATING = "DiscreteExteriorHeating"
DISCRETE_GATE_WITH_PEDESTRIAN_POSITION = "DiscreteGateWithPedestrianPosition"
@@ -176,18 +172,27 @@ class UIWidget(StrEnum):
DISCRETE_POSITIONABLE_GATE = "DiscretePositionableGate"
DOCK = "Dock"
DOMESTIC_HOT_WATER_PRODUCTION = "DomesticHotWaterProduction"
+ DOMESTIC_HOT_WATER_PRODUCTION_PEAK_AND_OFF_PEAK = (
+ "DomesticHotWaterProductionPeakAndOffPeak"
+ )
DOMESTIC_HOT_WATER_TANK = "DomesticHotWaterTank"
DOOR_LOCK = "DoorLock"
DROP_ARM_AWNING = "DropArmAwning"
+ DYNAMIC_AIR_SENSOR = "DynamicAirSensor"
DYNAMIC_AIR_VENT = "DynamicAirVent"
DYNAMIC_ALARM = "DynamicAlarm"
+ DYNAMIC_AMBIENT_NOISE_SENSOR = "DynamicAmbientNoiseSensor"
DYNAMIC_AUDIO_PLAYER = "DynamicAudioPlayer"
DYNAMIC_AWNING = "DynamicAwning"
DYNAMIC_BRIDGE = "DynamicBridge"
+ DYNAMIC_CARBON_DIOXIDE_SENSOR = "DynamicCarbonDioxideSensor"
DYNAMIC_CIRCUIT_BREAKER = "DynamicCircuitBreaker"
DYNAMIC_CURTAIN = "DynamicCurtain"
DYNAMIC_DOMESTIC_HOT_WATER_PRODUCTION = "DynamicDomesticHotWaterProduction"
+ DYNAMIC_ELECTRICITY_CONSUMPTION_SENSOR = "DynamicElectricityConsumptionSensor"
+ DYNAMIC_EXTERIOR_VENETIAN_BLIND = "DynamicExteriorVenetianBlind"
DYNAMIC_GARAGE_DOOR = "DynamicGarageDoor"
+ DYNAMIC_GAS_MEASUREMENT_SENSOR = "DynamicGasMeasurementSensor"
DYNAMIC_GATE = "DynamicGate"
DYNAMIC_GATEWAY = "DynamicGateway"
DYNAMIC_HEATER = "DynamicHeater"
@@ -203,15 +208,20 @@ class UIWidget(StrEnum):
DYNAMIC_OVEN = "DynamicOven"
DYNAMIC_PERGOLA = "DynamicPergola"
DYNAMIC_RAIN_SENSOR = "DynamicRainSensor"
+ DYNAMIC_REMOTE_CONTROLLER = "DynamicRemoteController"
DYNAMIC_ROLLER_SHUTTER = "DynamicRollerShutter"
+ DYNAMIC_SCENE = "DynamicScene"
+ DYNAMIC_SCENE_LAUNCHER_CONTROLLER = "DynamicSceneLauncherController"
DYNAMIC_SCREEN = "DynamicScreen"
DYNAMIC_SHUTTER = "DynamicShutter"
+ DYNAMIC_SWITCH = "DynamicSwitch"
DYNAMIC_TEMPERATURE_SENSOR = "DynamicTemperatureSensor"
DYNAMIC_THERMOSTAT = "DynamicThermostat"
DYNAMIC_THIRD_PARTY_GATEWAY = "DynamicThirdPartyGateway"
DYNAMIC_VENETIAN_BLIND = "DynamicVenetianBlind"
DYNAMIC_VENTILATION = "DynamicVentilation"
DYNAMIC_WASHING_MACHINE = "DynamicWashingMachine"
+ DYNAMIC_WATER_MEASUREMENT_SENSOR = "DynamicWaterMeasurementSensor"
DYNAMIC_WEATHER_STATION = "DynamicWeatherStation"
DYNAMIC_WIND_SENSOR = "DynamicWindSensor"
DYNAMIC_WINDOW = "DynamicWindow"
@@ -224,15 +234,16 @@ class UIWidget(StrEnum):
EN_OCEAN_GENERIC_ELECTRIC_COUNTER = "EnOceanGenericElectricCounter"
EN_OCEAN_TRANSCEIVER = "EnOceanTransceiver"
EVO_HOME_CONTROLLER = "EvoHomeController"
- EWATTCH_TICCOUNTER = "EwattchTICCounter"
+ EWATTCH_TIC_COUNTER = "EwattchTICCounter"
EXTERIOR_VENETIAN_BLIND = "ExteriorVenetianBlind"
FLOOR_HEATING = "FloorHeating"
GAS_DHW_CONSUMPTION_SENSOR = "GasDHWConsumptionSensor"
GAS_HEATER_CONSUMPTION_SENSOR = "GasHeaterConsumptionSensor"
- GENERIC_16_CHANNELS_COUNTER = "Generic16ChannelsCounter"
- GENERIC_1_CHANNEL_COUNTER = "Generic1ChannelCounter"
+ GENERIC16_CHANNELS_COUNTER = "Generic16ChannelsCounter"
+ GENERIC1_CHANNEL_COUNTER = "Generic1ChannelCounter"
GENERIC_CAMERA = "GenericCamera"
GROUP_CONFIGURATION = "GroupConfiguration"
+ HLRR_WIFI_BRIDGE = "HLRRWifiBridge"
HEAT_DETECTION_SENSOR = "HeatDetectionSensor"
HEAT_PUMP = "HeatPump"
HEATING_SET_POINT = "HeatingSetPoint"
@@ -243,13 +254,12 @@ class UIWidget(StrEnum):
HITACHI_DHW = "HitachiDHW"
HITACHI_SWIMMING_POOL = "HitachiSwimmingPool"
HITACHI_THERMOSTAT = "HitachiThermostat"
- HLRR_WIFI_BRIDGE = "HLRRWifiBridge"
HOMEKIT_STACK = "HomekitStack"
HUE_BRIDGE = "HueBridge"
- IOGENERIC = "IOGeneric"
- IOSIREN = "IOSiren"
- IOSTACK = "IOStack"
- IRBLASTER = "IRBlaster"
+ IO_GENERIC = "IOGeneric"
+ IO_SIREN = "IOSiren"
+ IO_STACK = "IOStack"
+ IR_BLASTER = "IRBlaster"
IMHOTEP_HEATING_TEMPERATURE_INTERFACE = "ImhotepHeatingTemperatureInterface"
INSTANT_ELECTRIC_CURRENT_CONSUMPTION_SENSOR = (
"InstantElectricCurrentConsumptionSensor"
@@ -259,7 +269,7 @@ class UIWidget(StrEnum):
INTRUSION_EVENT_SENSOR = "IntrusionEventSensor"
INTRUSION_SENSOR = "IntrusionSensor"
INVALID = "Invalid"
- JSWCAMERA = "JSWCamera"
+ JSW_CAMERA = "JSWCamera"
KIZ_OTHERM_BRIDGE = "KizOThermBridge"
KIZ_OTHERM_V2_BRIDGE = "KizOThermV2Bridge"
LOCK_UNLOCK_DOOR_LOCK_WITH_UNKNOWN_POSITION = (
@@ -268,31 +278,38 @@ class UIWidget(StrEnum):
LUMINANCE_SENSOR = "LuminanceSensor"
MEDIA_RENDERER = "MediaRenderer"
MOTION_SENSOR = "MotionSensor"
+ MULTI_GATEWAY = "MultiGateway"
MULTI_METER_ELECTRIC_SENSOR = "MultiMeterElectricSensor"
MY_FOX_ALARM_CONTROLLER = "MyFoxAlarmController"
MY_FOX_CAMERA = "MyFoxCamera"
MY_FOX_SECURITY_CAMERA = "MyFoxSecurityCamera"
- NODE = "Node"
NETATMO_CONFIGURATION_COMPONENT = "NetatmoConfigurationComponent"
NETATMO_GATEWAY = "NetatmoGateway"
+ NETATMO_HEATING_TEMPERATURE_INTERFACE = "NetatmoHeatingTemperatureInterface"
NETATMO_HOME = "NetatmoHome"
+ NETATMO_HOME_COACH_CONFIGURATION = "NetatmoHomeCoachConfiguration"
NETATMO_WEATHER_STATION_CONFIGURATION = "NetatmoWeatherStationConfiguration"
+ NEXELEC_AIR_QUALITY_CONFIGURATION_TOOL = "NexelecAirQualityConfigurationTool"
+ NODE = "Node"
NOISE_SENSOR = "NoiseSensor"
- OVPGENERIC = "OVPGeneric"
+ OVP_GENERIC = "OVPGeneric"
OCCUPANCY_SENSOR = "OccupancySensor"
+ ON_OFF_COOKING = "OnOffCooking"
ON_OFF_HEATING_SYSTEM = "OnOffHeatingSystem"
+ ON_OFF_HEATING_SYSTEM_PILOT_WIRE = "OnOffHeatingSystemPilotWire"
ON_OFF_LIGHT = "OnOffLight"
ON_OFF_REMOTECONTROLLER = "OnOffRemotecontroller"
+ ON_OFF_VENTILATION_SPEED = "OnOffVentilationSpeed"
OPEN_CLOSE_GATE = "OpenCloseGate"
- OPEN_CLOSE_GATE_4T = "OpenCloseGate4T"
+ OPEN_CLOSE_GATE4_T = "OpenCloseGate4T"
OPEN_CLOSE_GATE_WITH_PEDESTRIAN_POSITION = "OpenCloseGateWithPedestrianPosition"
OPEN_CLOSE_SLIDING_GARAGE_DOOR = "OpenCloseSlidingGarageDoor"
- OPEN_CLOSE_SLIDING_GARAGE_DOOR_4T = "OpenCloseSlidingGarageDoor4T"
+ OPEN_CLOSE_SLIDING_GARAGE_DOOR4_T = "OpenCloseSlidingGarageDoor4T"
OPEN_CLOSE_SLIDING_GARAGE_DOOR_WITH_PEDESTRIAN_POSITION = (
"OpenCloseSlidingGarageDoorWithPedestrianPosition"
)
OPEN_CLOSE_SLIDING_GATE = "OpenCloseSlidingGate"
- OPEN_CLOSE_SLIDING_GATE_4T = "OpenCloseSlidingGate4T"
+ OPEN_CLOSE_SLIDING_GATE4_T = "OpenCloseSlidingGate4T"
OPEN_CLOSE_SLIDING_GATE_WITH_PEDESTRIAN_POSITION = (
"OpenCloseSlidingGateWithPedestrianPosition"
)
@@ -350,16 +367,16 @@ class UIWidget(StrEnum):
RTD_INDOOR_SIREN = "RTDIndoorSiren"
RTD_OUTDOOR_SIREN = "RTDOutdoorSiren"
RTS_GENERIC = "RTSGeneric"
- RTS_GENERIC_4T = "RTSGeneric4T"
+ RTS_GENERIC4_T = "RTSGeneric4T"
RTS_THERMOSTAT = "RTSThermostat"
RAIN_SENSOR = "RainSensor"
RELATIVE_HUMIDITY_SENSOR = "RelativeHumiditySensor"
REMOTE_CONTROLLER_ONE_WAY = "RemoteControllerOneWay"
REPEATER = "Repeater"
ROCKER_SWITCH_AUTO_MANU_UP_DOWN_CONTROLLER = "RockerSwitchAutoManuUpDownController"
- ROCKER_SWITCHX_1_CONTROLLER = "RockerSwitchx1Controller"
- ROCKER_SWITCHX_2_CONTROLLER = "RockerSwitchx2Controller"
- ROCKER_SWITCHX_4_CONTROLLER = "RockerSwitchx4Controller"
+ ROCKER_SWITCHX1_CONTROLLER = "RockerSwitchx1Controller"
+ ROCKER_SWITCHX2_CONTROLLER = "RockerSwitchx2Controller"
+ ROCKER_SWITCHX4_CONTROLLER = "RockerSwitchx4Controller"
SCENE_CONTROLLER = "SceneController"
SCHNEIDER_SWITCH_CONFIGURATION = "SchneiderSwitchConfiguration"
SIREN_STATUS = "SirenStatus"
@@ -376,6 +393,8 @@ class UIWidget(StrEnum):
STATEFUL_ALARM_CONTROLLER = "StatefulAlarmController"
STATEFUL_ON_OFF = "StatefulOnOff"
STATEFUL_ON_OFF_LIGHT = "StatefulOnOffLight"
+ STATEFUL_ON_OFF_PEAK_AND_OFF_PEAK = "StatefulOnOffPeakAndOffPeak"
+ STATEFUL_VENETIAN_SLATS = "StatefulVenetianSlats"
STATELESS_ALARM_CONTROLLER = "StatelessAlarmController"
STATELESS_EXTERIOR_HEATING = "StatelessExteriorHeating"
STATELESS_ON_OFF = "StatelessOnOff"
@@ -383,8 +402,9 @@ class UIWidget(StrEnum):
SUN_INTENSITY_SENSOR = "SunIntensitySensor"
SWIMMING_POOL = "SwimmingPool"
SWINGING_SHUTTER = "SwingingShutter"
- TSKALARM_CONTROLLER = "TSKAlarmController"
+ TSK_ALARM_CONTROLLER = "TSKAlarmController"
TEMPERATURE_SENSOR = "TemperatureSensor"
+ THERMOSTAT_DUAL_MODE_AND_FAN = "ThermostatDualModeAndFan"
THERMOSTAT_HEATING_TEMPERATURE_INTERFACE = "ThermostatHeatingTemperatureInterface"
THERMOSTAT_SET_POINT = "ThermostatSetPoint"
THERMOSTAT_ZONES_CONTROLLER = "ThermostatZonesController"
@@ -392,16 +412,19 @@ class UIWidget(StrEnum):
TILT_ONLY_VENETIAN_BLIND = "TiltOnlyVenetianBlind"
TIMED_ON_OFF = "TimedOnOff"
TIMED_ON_OFF_LIGHT = "TimedOnOffLight"
+ TOP_DOWN_BOTTOM_UP_SCREEN = "TopDownBottomUpScreen"
UNIVERSAL_SENSOR = "UniversalSensor"
UNTYPED = "Untyped"
UP_DOWN_BIOCLIMATIC_PERGOLA = "UpDownBioclimaticPergola"
+ UP_DOWN_BOTTOM_UP_SCREEN = "UpDownBottomUpScreen"
UP_DOWN_CELLULAR_SCREEN = "UpDownCellularScreen"
UP_DOWN_CURTAIN = "UpDownCurtain"
+ UP_DOWN_DROP_ARM_AWNING = "UpDownDropArmAwning"
UP_DOWN_DUAL_CURTAIN = "UpDownDualCurtain"
UP_DOWN_EXTERIOR_SCREEN = "UpDownExteriorScreen"
UP_DOWN_EXTERIOR_VENETIAN_BLIND = "UpDownExteriorVenetianBlind"
UP_DOWN_GARAGE_DOOR = "UpDownGarageDoor"
- UP_DOWN_GARAGE_DOOR_4T = "UpDownGarageDoor4T"
+ UP_DOWN_GARAGE_DOOR4_T = "UpDownGarageDoor4T"
UP_DOWN_GARAGE_DOOR_WITH_VENTILATION_POSITION = (
"UpDownGarageDoorWithVentilationPosition"
)
@@ -410,37 +433,45 @@ class UIWidget(StrEnum):
UP_DOWN_SCREEN = "UpDownScreen"
UP_DOWN_SHEER_SCREEN = "UpDownSheerScreen"
UP_DOWN_SWINGING_SHUTTER = "UpDownSwingingShutter"
+ UP_DOWN_TOP_DOWN_SCREEN = "UpDownTopDownScreen"
UP_DOWN_VENETIAN_BLIND = "UpDownVenetianBlind"
UP_DOWN_WINDOW = "UpDownWindow"
UP_DOWN_ZEBRA_SCREEN = "UpDownZebraScreen"
- VOCSENSOR = "VOCSensor"
+ VOC_SENSOR = "VOCSensor"
VALVE_HEATING_TEMPERATURE_INTERFACE = "ValveHeatingTemperatureInterface"
+ VENETIAN_SLATS = "VenetianSlats"
VENTILATION_INLET = "VentilationInlet"
VENTILATION_OUTLET = "VentilationOutlet"
VENTILATION_TRANSFER = "VentilationTransfer"
WATER_DETECTION_SENSOR = "WaterDetectionSensor"
WEATHER_FORECAST_SENSOR = "WeatherForecastSensor"
WIFI = "Wifi"
- WIND_SPEED_SENSOR = "WindSpeedSensor"
WIND_SPEED_AND_DIRECTION_SENSOR = "WindSpeedAndDirectionSensor"
+ WIND_SPEED_SENSOR = "WindSpeedSensor"
WINDOW_LOCK = "WindowLock"
WINDOW_WITH_TILT_SENSOR = "WindowWithTiltSensor"
ZWAVE_AEOTEC_CONFIGURATION = "ZWaveAeotecConfiguration"
ZWAVE_CONFIGURATION = "ZWaveConfiguration"
- ZWAVE_DANFOSS_RSLINK = "ZWaveDanfossRSLink"
+ ZWAVE_DANFOSS_RS_LINK = "ZWaveDanfossRSLink"
ZWAVE_DOOR_LOCK_CONFIGURATION = "ZWaveDoorLockConfiguration"
ZWAVE_FIBARO_ROLLER_SHUTTER_CONFIGURATION = "ZWaveFibaroRollerShutterConfiguration"
ZWAVE_HEATIT_THERMOSTAT_CONFIGURATION = "ZWaveHeatitThermostatConfiguration"
ZWAVE_NODON_CONFIGURATION = "ZWaveNodonConfiguration"
ZWAVE_QUBINO_CONFIGURATION = "ZWaveQubinoConfiguration"
- ZWAVE_SEDEVICE_CONFIGURATION = "ZWaveSEDeviceConfiguration"
+ ZWAVE_SE_DEVICE_CONFIGURATION = "ZWaveSEDeviceConfiguration"
ZWAVE_TRANSCEIVER = "ZWaveTransceiver"
ZIGBEE_NETWORK = "ZigbeeNetwork"
ZIGBEE_STACK = "ZigbeeStack"
+
+@unique
+class UIClassifier(UnknownEnumMixin, StrEnum):
+ """Enumeration of UI classifiers used to categorize device types."""
+
UNKNOWN = "unknown"
- @classmethod
- def _missing_(cls, value): # type: ignore
- _LOGGER.warning(f"Unsupported value {value} has been returned for {cls}")
- return cls.UNKNOWN
+ COOLING_SYSTEM = "coolingSystem"
+ EMITTER = "emitter"
+ GENERATOR = "generator"
+ HEATING_SYSTEM = "heatingSystem"
+ SENSOR = "sensor"
diff --git a/pyoverkiz/enums/ui_profile.py b/pyoverkiz/enums/ui_profile.py
new file mode 100644
index 00000000..559fe57c
--- /dev/null
+++ b/pyoverkiz/enums/ui_profile.py
@@ -0,0 +1,1817 @@
+"""UI Profile enums describe device capabilities through commands and states.
+
+THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.
+Run `uv run utils/generate_enums.py` to regenerate.
+"""
+
+from enum import StrEnum, unique
+
+from pyoverkiz.enums.base import UnknownEnumMixin
+
+
+@unique
+class UIProfile(UnknownEnumMixin, StrEnum):
+ """UI Profiles define device capabilities through commands and states.
+
+ Each profile describes what a device can do (commands) and what information
+ it provides (states). Form factor indicates if the profile is tied to a
+ specific physical device type.
+ """
+
+ UNKNOWN = "Unknown"
+
+ #
+ # AirFan
+ #
+ # Commands:
+ # - setFanSpeedLevel(int 0-100): Set the device fan speed level (%)
+ AIR_FAN = "AirFan"
+
+ #
+ # AirFanMode
+ #
+ # Commands:
+ # - setFanSpeedMode(string values: 'low', 'high'): Set the device fan speed mode
+ AIR_FAN_MODE = "AirFanMode"
+
+ #
+ # AirOutputLevelSensor
+ #
+ # States:
+ # - core:AirOutputLevelState (int 0-100): Air output level (%)
+ AIR_OUTPUT_LEVEL_SENSOR = "AirOutputLevelSensor"
+
+ #
+ # AirQuality
+ #
+ # States:
+ # - core:GlobalAirQualityState (string values: 'bad', 'error', 'excellent', 'fair', 'good', 'poor'): Air quality status
+ AIR_QUALITY = "AirQuality"
+
+ #
+ # Alarm
+ #
+ # Commands:
+ # - arm(): Arm the system
+ # - disarm(): Disarm the system
+ ALARM = "Alarm"
+
+ #
+ # AmbientNoiseSensor
+ #
+ # States:
+ # - core:AmbientNoiseState (float >= 0.0): Ambient noise in dB(A)
+ AMBIENT_NOISE_SENSOR = "AmbientNoiseSensor"
+
+ #
+ # BasicCloseable
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ BASIC_CLOSEABLE = "BasicCloseable"
+
+ #
+ # BasicOpenClose
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ BASIC_OPEN_CLOSE = "BasicOpenClose"
+
+ #
+ # BasicUpDown
+ #
+ # Commands:
+ # - down(): Move the device completely down
+ # - up(): Move the device completely up
+ BASIC_UP_DOWN = "BasicUpDown"
+
+ #
+ # BatteryStatus
+ #
+ # States:
+ # - core:BatteryState (string values: 'verylow', 'low', 'normal', 'full'): Device battery status
+ BATTERY_STATUS = "BatteryStatus"
+
+ #
+ # CO2Concentration
+ #
+ # States:
+ # - core:CO2ConcentrationState (float >= 0.0): Current carbon dioxide concentration (ppm)
+ CO2_CONCENTRATION = "CO2Concentration"
+
+ #
+ # COConcentration
+ #
+ # States:
+ # - core:COConcentrationState (float >= 0.0): Current carbon monoxide concentration (ppm)
+ CO_CONCENTRATION = "COConcentration"
+
+ #
+ # CODetection
+ #
+ # States:
+ # - core:CODetectionState (string values: 'detected', 'notDetected'): Indicate if carbon monoxide level is too high
+ CO_DETECTION = "CODetection"
+
+ #
+ # CardReader
+ #
+ # States:
+ # - core:CardPositionState (string values: 'inserted', 'removed'): Indicate if a security card or device was inserted or detected
+ CARD_READER = "CardReader"
+
+ #
+ # Closeable
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ CLOSEABLE = "Closeable"
+
+ #
+ # CloseableBlind
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ CLOSEABLE_BLIND = "CloseableBlind"
+
+ #
+ # CloseableCurtain
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ CLOSEABLE_CURTAIN = "CloseableCurtain"
+
+ #
+ # CloseableScreen
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ CLOSEABLE_SCREEN = "CloseableScreen"
+
+ #
+ # CloseableShutter
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ CLOSEABLE_SHUTTER = "CloseableShutter"
+
+ #
+ # CloseableWindow
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ CLOSEABLE_WINDOW = "CloseableWindow"
+
+ #
+ # ContactAndVibrationDetector
+ #
+ # States:
+ # - core:ContactState (string values: 'open', 'closed'): Contact sensor is open or closed
+ # - core:VibrationState (string values: 'detected', 'notDetected'): Indicate if strong vibrations are detected or not
+ CONTACT_AND_VIBRATION_DETECTOR = "ContactAndVibrationDetector"
+
+ #
+ # ContactDetector
+ #
+ # States:
+ # - core:ContactState (string values: 'open', 'closed'): Contact sensor is open or closed
+ CONTACT_DETECTOR = "ContactDetector"
+
+ #
+ # CoolingThermostat
+ #
+ # Commands:
+ # - setCoolingTargetTemperature(float 7.0-35.0): Set the cooling target temperature (manual set point)
+ COOLING_THERMOSTAT = "CoolingThermostat"
+
+ #
+ # Cyclic
+ #
+ # Commands:
+ # - cycle(): Do a cycle of supported motion kinematics or modes
+ CYCLIC = "Cyclic"
+
+ #
+ # CyclicGarageOpener
+ #
+ # Commands:
+ # - cycle(): Do a cycle of supported motion kinematics or modes
+ #
+ # Form factor specific: Yes
+ CYCLIC_GARAGE_OPENER = "CyclicGarageOpener"
+
+ #
+ # CyclicGateOpener
+ #
+ # Commands:
+ # - cycle(): Do a cycle of supported motion kinematics or modes
+ #
+ # Form factor specific: Yes
+ CYCLIC_GATE_OPENER = "CyclicGateOpener"
+
+ #
+ # DHWTemperature
+ #
+ # States:
+ # - core:DHWTemperatureState (float -100.0-100.0): Current water temperature (°C)
+ DHW_TEMPERATURE = "DHWTemperature"
+
+ #
+ # DHWThermostat
+ #
+ # Commands:
+ # - setTargetDHWTemperature(float 38.0-60.0): Set the new water temperature to reach for a Domestic Hot Water system
+ DHW_THERMOSTAT = "DHWThermostat"
+
+ #
+ # DHWThermostatTargetReader
+ #
+ # States:
+ # - core:TargetDHWTemperatureState (float 38.0-60.0): Domestic hot water target temperature (°C)
+ DHW_THERMOSTAT_TARGET_READER = "DHWThermostatTargetReader"
+
+ #
+ # DeployUndeploy
+ #
+ # Commands:
+ # - deploy(): Fully deploy the device
+ # - undeploy(): Fully undeploy the device
+ DEPLOY_UNDEPLOY = "DeployUndeploy"
+
+ #
+ # DeployUndeployAwning
+ #
+ # Commands:
+ # - deploy(): Fully deploy the device
+ # - undeploy(): Fully undeploy the device
+ #
+ # Form factor specific: Yes
+ DEPLOY_UNDEPLOY_AWNING = "DeployUndeployAwning"
+
+ #
+ # Deployable
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ DEPLOYABLE = "Deployable"
+
+ #
+ # DeployableAwning
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ #
+ # Form factor specific: Yes
+ DEPLOYABLE_AWNING = "DeployableAwning"
+
+ #
+ # DeployableVerticalAwning
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ #
+ # Form factor specific: Yes
+ DEPLOYABLE_VERTICAL_AWNING = "DeployableVerticalAwning"
+
+ #
+ # Dimmable
+ #
+ # Commands:
+ # - setIntensity(int 0-100): Light intensity level (100%=maximum intensity, 0%=off)
+ DIMMABLE = "Dimmable"
+
+ #
+ # DoorContactSensor
+ #
+ # States:
+ # - core:ContactState (string values: 'open', 'closed'): Contact sensor is open or closed
+ #
+ # Form factor specific: Yes
+ DOOR_CONTACT_SENSOR = "DoorContactSensor"
+
+ #
+ # DoorOpeningStatus
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ DOOR_OPENING_STATUS = "DoorOpeningStatus"
+
+ #
+ # DualThermostat
+ #
+ # Commands:
+ # - setCoolingTargetTemperature(float 7.0-35.0): Set the cooling target temperature (manual set point)
+ # - setHeatingTargetTemperature(float 7.0-35.0): Set the heating target temperature (manual set point)
+ DUAL_THERMOSTAT = "DualThermostat"
+
+ #
+ # ElectricCurrentMeter
+ #
+ # States:
+ # - core:ElectricCurrentState (float >= 0.0): Current electric current intensity (Amp)
+ ELECTRIC_CURRENT_METER = "ElectricCurrentMeter"
+
+ #
+ # ElectricEnergyAndPower
+ #
+ # States:
+ # - core:ElectricEnergyConsumptionState (int >= 0): Electric energy consumption index (Wh)
+ # - core:ElectricPowerConsumptionState (float >= 0.0): Current electric power (W)
+ ELECTRIC_ENERGY_AND_POWER = "ElectricEnergyAndPower"
+
+ #
+ # ElectricEnergyConsumption
+ #
+ # States:
+ # - core:ElectricEnergyConsumptionState (int >= 0): Electric energy consumption index (Wh)
+ ELECTRIC_ENERGY_CONSUMPTION = "ElectricEnergyConsumption"
+
+ #
+ # ElectricPowerMeter
+ #
+ # States:
+ # - core:ElectricPowerConsumptionState (float >= 0.0): Current electric power (W)
+ ELECTRIC_POWER_METER = "ElectricPowerMeter"
+
+ #
+ # FossilEnergyConsumption
+ #
+ # States:
+ # - core:FossilEnergyConsumptionState (int >= 0): Fossil energy consumption index (Wh)
+ FOSSIL_ENERGY_CONSUMPTION = "FossilEnergyConsumption"
+
+ #
+ # GasConsumption
+ #
+ # States:
+ # - core:GasConsumptionState (float >= 0.0): Gas consumption index (m^3)
+ GAS_CONSUMPTION = "GasConsumption"
+
+ #
+ # GasDetector
+ #
+ # States:
+ # - core:GasDetectionState (string values: 'detected', 'notDetected'): Indicate if a gas leak is detected or not
+ GAS_DETECTOR = "GasDetector"
+
+ # Generic (details unavailable)
+ GENERIC = "Generic"
+
+ #
+ # HeatingLevel
+ #
+ # Commands:
+ # - setHeatingLevel(string values: 'comfort', 'eco'): Sets the device heating level mode
+ HEATING_LEVEL = "HeatingLevel"
+
+ #
+ # IntrusionDetector
+ #
+ # States:
+ # - core:IntrusionState (string values: 'detected', 'notDetected'): Indicate if an intrusion was detected or not
+ INTRUSION_DETECTOR = "IntrusionDetector"
+
+ #
+ # IntrusionEventDetector
+ #
+ # States:
+ # - core:IntrusionDetectedState (boolean): Indicate each time an intrusion was detected
+ INTRUSION_EVENT_DETECTOR = "IntrusionEventDetector"
+
+ #
+ # LevelControl
+ #
+ # Commands:
+ # - setLevel(int 0-100): Generic device working level (0-100%) Functional meaning depends on device
+ LEVEL_CONTROL = "LevelControl"
+
+ #
+ # LightDimmer
+ #
+ # Commands:
+ # - setIntensity(int 0-100): Light intensity level (100%=maximum intensity, 0%=off)
+ #
+ # Form factor specific: Yes
+ LIGHT_DIMMER = "LightDimmer"
+
+ #
+ # Lock
+ #
+ # Commands:
+ # - lock(): Lock the device
+ # - unlock(): Unlock the device
+ LOCK = "Lock"
+
+ #
+ # LockStatus
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ LOCK_STATUS = "LockStatus"
+
+ #
+ # LockableOpeningStatus
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ LOCKABLE_OPENING_STATUS = "LockableOpeningStatus"
+
+ #
+ # LockableTiltableOpeningStatus
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ # - core:TiltedState (boolean): Indicate if a device is titled or straight
+ LOCKABLE_TILTABLE_OPENING_STATUS = "LockableTiltableOpeningStatus"
+
+ #
+ # LockableTiltableWindowOpeningStatus
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ # - core:TiltedState (boolean): Indicate if a device is titled or straight
+ #
+ # Form factor specific: Yes
+ LOCKABLE_TILTABLE_WINDOW_OPENING_STATUS = "LockableTiltableWindowOpeningStatus"
+
+ #
+ # Luminance
+ #
+ # States:
+ # - core:LuminanceState (int >= 0): Current illuminance value (Lux)
+ LUMINANCE = "Luminance"
+
+ #
+ # MusicPlayer
+ #
+ # Commands:
+ # - pause(): Pause current action
+ # - play(): Play media
+ # - setVolume(int 0-100): Set the device output volume
+ MUSIC_PLAYER = "MusicPlayer"
+
+ #
+ # OccupancyDetector
+ #
+ # States:
+ # - core:OccupancyState (string values: 'personInside', 'noPersonInside'): Indicate if a person or motion was detected or not
+ OCCUPANCY_DETECTOR = "OccupancyDetector"
+
+ #
+ # OnOffButton
+ #
+ # States:
+ # - core:OnOffButtonState (string values: 'on', 'off'): Position of an on/off switch
+ ON_OFF_BUTTON = "OnOffButton"
+
+ #
+ # OnOffStatus
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ ON_OFF_STATUS = "OnOffStatus"
+
+ #
+ # OpenClose
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ OPEN_CLOSE = "OpenClose"
+
+ #
+ # OpenCloseBlind
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_BLIND = "OpenCloseBlind"
+
+ #
+ # OpenCloseCameraShutter
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_CAMERA_SHUTTER = "OpenCloseCameraShutter"
+
+ #
+ # OpenCloseCurtain
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_CURTAIN = "OpenCloseCurtain"
+
+ #
+ # OpenCloseGarageOpener
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_GARAGE_OPENER = "OpenCloseGarageOpener"
+
+ #
+ # OpenCloseGateOpener
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_GATE_OPENER = "OpenCloseGateOpener"
+
+ #
+ # OpenCloseScreen
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_SCREEN = "OpenCloseScreen"
+
+ #
+ # OpenCloseShutter
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_SHUTTER = "OpenCloseShutter"
+
+ #
+ # OpenCloseShutterSwitch
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_SHUTTER_SWITCH = "OpenCloseShutterSwitch"
+
+ #
+ # OpenCloseSlidingGateOpener
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_SLIDING_GATE_OPENER = "OpenCloseSlidingGateOpener"
+
+ #
+ # OpenCloseSwingingShutter
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_SWINGING_SHUTTER = "OpenCloseSwingingShutter"
+
+ #
+ # OpenCloseWindow
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # Form factor specific: Yes
+ OPEN_CLOSE_WINDOW = "OpenCloseWindow"
+
+ #
+ # OpeningStatus
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ OPENING_STATUS = "OpeningStatus"
+
+ #
+ # OperatingModeHeating
+ #
+ # Commands:
+ # - setOperatingMode(any): Set an operating mode
+ OPERATING_MODE_HEATING = "OperatingModeHeating"
+
+ #
+ # OrientableAndCloseable
+ #
+ # Commands:
+ # - setClosureAndOrientation(int 0-100, int 0-100): Set both the closure level (0-100%) and relative slats orientation (0-100%) of the device
+ ORIENTABLE_AND_CLOSEABLE = "OrientableAndCloseable"
+
+ #
+ # OrientableOrCloseable
+ #
+ # Commands:
+ # - setClosureOrOrientation(int 0-100, int 0-100): Set device closure level (0-100%), or put the device in rocking position ('rocker') and set the relative slats orientation (0-100%)
+ ORIENTABLE_OR_CLOSEABLE = "OrientableOrCloseable"
+
+ #
+ # OrientablePlusCloseable
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ ORIENTABLE_PLUS_CLOSEABLE = "OrientablePlusCloseable"
+
+ #
+ # OrientableSlats
+ #
+ # Commands:
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ ORIENTABLE_SLATS = "OrientableSlats"
+
+ #
+ # PictureCamera
+ #
+ # Commands:
+ # - takePicture(): Take a still picture
+ PICTURE_CAMERA = "PictureCamera"
+
+ #
+ # PowerCutDetector
+ #
+ # States:
+ # - core:PowerCutDetectionState (string values: 'detected', 'notDetected'): Indicate if a main power cut was detected or not
+ POWER_CUT_DETECTOR = "PowerCutDetector"
+
+ #
+ # PushButtonSensor
+ #
+ # States:
+ # - core:ButtonState (string values: 'pressed', 'released', 'shortPressed'): Indicate if a button is pressed or released
+ PUSH_BUTTON_SENSOR = "PushButtonSensor"
+
+ #
+ # RainDetector
+ #
+ # States:
+ # - core:RainState (string values: 'detected', 'notDetected'): Indicate if rain is detected or not
+ RAIN_DETECTOR = "RainDetector"
+
+ #
+ # RelativeHumidity
+ #
+ # States:
+ # - core:RelativeHumidityState (float 0.0-100.0): Air relative humidity (0%=totally dry, 100%=fully saturated)
+ RELATIVE_HUMIDITY = "RelativeHumidity"
+
+ #
+ # RockerSwitch
+ #
+ # States:
+ # - core:RockerSwitchPushWayState (string values: 'heldDown', 'pressed', 'pressedX2', 'pressedX3', 'pressedX4', 'pressedX5', 'released'): Indicate when a rocker switch is pressed or released
+ ROCKER_SWITCH = "RockerSwitch"
+
+ #
+ # ScenarioTrigger
+ #
+ # States:
+ # - core:LaunchStatusState (string values: 'launched', 'standby'): Indicates if the scene is launched or standby. "launched" value indicates that the launch conditions have been met (ex button pressed on a remote controller). "standby" value matches with an intermediate status, typically between two launch actions (ex button released on a remote controller). "launched" value should be used during a very short time and not persist across different launch events, whereas "standby" is a more stable value.
+ SCENARIO_TRIGGER = "ScenarioTrigger"
+
+ #
+ # Siren
+ #
+ # Commands:
+ # - ring(): Ask the device to start ringing
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ SIREN = "Siren"
+
+ #
+ # SlidingPergola
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ #
+ # Form factor specific: Yes
+ SLIDING_PERGOLA = "SlidingPergola"
+
+ #
+ # SmokeDetector
+ #
+ # States:
+ # - core:SmokeState (string values: 'detected', 'notDetected'): Indicate if smoke is detected or not
+ SMOKE_DETECTOR = "SmokeDetector"
+
+ # Specific (details unavailable)
+ SPECIFIC = "Specific"
+
+ #
+ # StartStop
+ #
+ # Commands:
+ # - start(): Start the default actuator behavior (movement, sound or timer)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ START_STOP = "StartStop"
+
+ #
+ # StatefulAirFan
+ #
+ # Commands:
+ # - setFanSpeedLevel(int 0-100): Set the device fan speed level (%)
+ #
+ # States:
+ # - core:FanSpeedLevelState (int 0-100): Fan speed level (%)
+ STATEFUL_AIR_FAN = "StatefulAirFan"
+
+ #
+ # StatefulAlarm
+ #
+ # Commands:
+ # - arm(): Arm the system
+ # - disarm(): Disarm the system
+ #
+ # States:
+ # - core:ArmedState (boolean): Indicate if the security device is armed (true=armed)
+ STATEFUL_ALARM = "StatefulAlarm"
+
+ #
+ # StatefulBasicCloseable
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ STATEFUL_BASIC_CLOSEABLE = "StatefulBasicCloseable"
+
+ #
+ # StatefulBasicOpenClose
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ STATEFUL_BASIC_OPEN_CLOSE = "StatefulBasicOpenClose"
+
+ #
+ # StatefulBioClimaticPergola
+ #
+ # Commands:
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ #
+ # States:
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ #
+ # Form factor specific: Yes
+ STATEFUL_BIO_CLIMATIC_PERGOLA = "StatefulBioClimaticPergola"
+
+ #
+ # StatefulCarLockWithOpeningStatus
+ #
+ # Commands:
+ # - lock(): Lock the device
+ # - unlock(): Unlock the device
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ STATEFUL_CAR_LOCK_WITH_OPENING_STATUS = "StatefulCarLockWithOpeningStatus"
+
+ #
+ # StatefulCloseable
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ STATEFUL_CLOSEABLE = "StatefulCloseable"
+
+ #
+ # StatefulCloseableAirVent
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_AIR_VENT = "StatefulCloseableAirVent"
+
+ #
+ # StatefulCloseableBlind
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_BLIND = "StatefulCloseableBlind"
+
+ #
+ # StatefulCloseableCurtain
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_CURTAIN = "StatefulCloseableCurtain"
+
+ #
+ # StatefulCloseableGarageOpener
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_GARAGE_OPENER = "StatefulCloseableGarageOpener"
+
+ #
+ # StatefulCloseableGateOpener
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_GATE_OPENER = "StatefulCloseableGateOpener"
+
+ #
+ # StatefulCloseableScreen
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_SCREEN = "StatefulCloseableScreen"
+
+ #
+ # StatefulCloseableShutter
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_SHUTTER = "StatefulCloseableShutter"
+
+ #
+ # StatefulCloseableSlidingDoor
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_SLIDING_DOOR = "StatefulCloseableSlidingDoor"
+
+ #
+ # StatefulCloseableSlidingWindow
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_SLIDING_WINDOW = "StatefulCloseableSlidingWindow"
+
+ #
+ # StatefulCloseableSwingingShutter
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_SWINGING_SHUTTER = "StatefulCloseableSwingingShutter"
+
+ #
+ # StatefulCloseableValve
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_VALVE = "StatefulCloseableValve"
+
+ #
+ # StatefulCloseableWindow
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_CLOSEABLE_WINDOW = "StatefulCloseableWindow"
+
+ #
+ # StatefulCoolingThermostat
+ #
+ # Commands:
+ # - setCoolingTargetTemperature(float 7.0-35.0): Set the cooling target temperature (manual set point)
+ #
+ # States:
+ # - core:CoolingTargetTemperatureState (float 12.0-30.0): Room target temperature (°C) for dual heating/cooling system
+ STATEFUL_COOLING_THERMOSTAT = "StatefulCoolingThermostat"
+
+ #
+ # StatefulCoolingThermostatWithSensor
+ #
+ # Commands:
+ # - setCoolingTargetTemperature(float 7.0-35.0): Set the cooling target temperature (manual set point)
+ #
+ # States:
+ # - core:CoolingTargetTemperatureState (float 12.0-30.0): Room target temperature (°C) for dual heating/cooling system
+ # - core:TemperatureState (float -100.0-100.0): Current room temperature (°C)
+ STATEFUL_COOLING_THERMOSTAT_WITH_SENSOR = "StatefulCoolingThermostatWithSensor"
+
+ #
+ # StatefulDHWThermostat
+ #
+ # Commands:
+ # - setTargetDHWTemperature(float 38.0-60.0): Set the new water temperature to reach for a Domestic Hot Water system
+ #
+ # States:
+ # - core:TargetDHWTemperatureState (float 38.0-60.0): Domestic hot water target temperature (°C)
+ STATEFUL_DHW_THERMOSTAT = "StatefulDHWThermostat"
+
+ #
+ # StatefulDeployUndeploy
+ #
+ # Commands:
+ # - deploy(): Fully deploy the device
+ # - undeploy(): Fully undeploy the device
+ #
+ # States:
+ # - core:DeployedUndeployedState (string values: 'deployed', 'undeployed'): Indicate if the device is deployed or not
+ STATEFUL_DEPLOY_UNDEPLOY = "StatefulDeployUndeploy"
+
+ #
+ # StatefulDeployUndeployAwning
+ #
+ # Commands:
+ # - deploy(): Fully deploy the device
+ # - undeploy(): Fully undeploy the device
+ #
+ # States:
+ # - core:DeployedUndeployedState (string values: 'deployed', 'undeployed'): Indicate if the device is deployed or not
+ #
+ # Form factor specific: Yes
+ STATEFUL_DEPLOY_UNDEPLOY_AWNING = "StatefulDeployUndeployAwning"
+
+ #
+ # StatefulDeployable
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ #
+ # States:
+ # - core:DeploymentState (int 0-100): Device deployment percentage (0%=fully retracted, 100%=fully deployed)
+ STATEFUL_DEPLOYABLE = "StatefulDeployable"
+
+ #
+ # StatefulDeployableAwning
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ #
+ # States:
+ # - core:DeploymentState (int 0-100): Device deployment percentage (0%=fully retracted, 100%=fully deployed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_DEPLOYABLE_AWNING = "StatefulDeployableAwning"
+
+ #
+ # StatefulDeployableVerticalAwning
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ #
+ # States:
+ # - core:DeploymentState (int 0-100): Device deployment percentage (0%=fully retracted, 100%=fully deployed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_DEPLOYABLE_VERTICAL_AWNING = "StatefulDeployableVerticalAwning"
+
+ #
+ # StatefulDimmable
+ #
+ # Commands:
+ # - setIntensity(int 0-100): Light intensity level (100%=maximum intensity, 0%=off)
+ #
+ # States:
+ # - core:LightIntensityState (int 0-100): Light intensity percentage (0%=min intensity,100%=maximum intensity)
+ STATEFUL_DIMMABLE = "StatefulDimmable"
+
+ #
+ # StatefulDoorLock
+ #
+ # Commands:
+ # - lock(): Lock the device
+ # - unlock(): Unlock the device
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ #
+ # Form factor specific: Yes
+ STATEFUL_DOOR_LOCK = "StatefulDoorLock"
+
+ #
+ # StatefulDoorLockWithOpeningStatus
+ #
+ # Commands:
+ # - lock(): Lock the device
+ # - unlock(): Unlock the device
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ STATEFUL_DOOR_LOCK_WITH_OPENING_STATUS = "StatefulDoorLockWithOpeningStatus"
+
+ #
+ # StatefulDualThermostat
+ #
+ # Commands:
+ # - setCoolingTargetTemperature(float 7.0-35.0): Set the cooling target temperature (manual set point)
+ # - setHeatingTargetTemperature(float 7.0-35.0): Set the heating target temperature (manual set point)
+ #
+ # States:
+ # - core:CoolingTargetTemperatureState (float 12.0-30.0): Room target temperature (°C) for dual heating/cooling system
+ # - core:HeatingTargetTemperatureState (float 12.0-30.0): Room target temperature (°C) for dual heating/cooling system
+ STATEFUL_DUAL_THERMOSTAT = "StatefulDualThermostat"
+
+ #
+ # StatefulDualThermostatWithSensor
+ #
+ # Commands:
+ # - setCoolingTargetTemperature(float 7.0-35.0): Set the cooling target temperature (manual set point)
+ # - setHeatingTargetTemperature(float 7.0-35.0): Set the heating target temperature (manual set point)
+ #
+ # States:
+ # - core:CoolingTargetTemperatureState (float 12.0-30.0): Room target temperature (°C) for dual heating/cooling system
+ # - core:HeatingTargetTemperatureState (float 12.0-30.0): Room target temperature (°C) for dual heating/cooling system
+ # - core:TemperatureState (float -100.0-100.0): Current room temperature (°C)
+ STATEFUL_DUAL_THERMOSTAT_WITH_SENSOR = "StatefulDualThermostatWithSensor"
+
+ #
+ # StatefulHeatingLevel
+ #
+ # Commands:
+ # - setHeatingLevel(string values: 'comfort', 'eco'): Sets the device heating level mode
+ #
+ # States:
+ # - core:TargetHeatingLevelState (string values: 'comfort', 'eco'): Current heating level
+ STATEFUL_HEATING_LEVEL = "StatefulHeatingLevel"
+
+ #
+ # StatefulLevelControl
+ #
+ # Commands:
+ # - setLevel(int 0-100): Generic device working level (0-100%) Functional meaning depends on device
+ #
+ # States:
+ # - core:LevelState (int 0-100): Device working level percentage (0%=min level, 100%=maximum level)
+ STATEFUL_LEVEL_CONTROL = "StatefulLevelControl"
+
+ #
+ # StatefulLevelControlHeating
+ #
+ # Commands:
+ # - setLevel(int 0-100): Generic device working level (0-100%) Functional meaning depends on device
+ #
+ # States:
+ # - core:LevelState (int 0-100): Device working level percentage (0%=min level, 100%=maximum level)
+ #
+ # Form factor specific: Yes
+ STATEFUL_LEVEL_CONTROL_HEATING = "StatefulLevelControlHeating"
+
+ #
+ # StatefulLightDimmer
+ #
+ # Commands:
+ # - setIntensity(int 0-100): Light intensity level (100%=maximum intensity, 0%=off)
+ #
+ # States:
+ # - core:LightIntensityState (int 0-100): Light intensity percentage (0%=min intensity,100%=maximum intensity)
+ #
+ # Form factor specific: Yes
+ STATEFUL_LIGHT_DIMMER = "StatefulLightDimmer"
+
+ #
+ # StatefulLock
+ #
+ # Commands:
+ # - lock(): Lock the device
+ # - unlock(): Unlock the device
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ STATEFUL_LOCK = "StatefulLock"
+
+ #
+ # StatefulLockWithOpeningStatus
+ #
+ # Commands:
+ # - lock(): Lock the device
+ # - unlock(): Unlock the device
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ STATEFUL_LOCK_WITH_OPENING_STATUS = "StatefulLockWithOpeningStatus"
+
+ #
+ # StatefulOpenClose
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ STATEFUL_OPEN_CLOSE = "StatefulOpenClose"
+
+ #
+ # StatefulOpenCloseGateOpener
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ STATEFUL_OPEN_CLOSE_GATE_OPENER = "StatefulOpenCloseGateOpener"
+
+ #
+ # StatefulOpenCloseShutter
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ STATEFUL_OPEN_CLOSE_SHUTTER = "StatefulOpenCloseShutter"
+
+ #
+ # StatefulOpenCloseSwingingShutter
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ STATEFUL_OPEN_CLOSE_SWINGING_SHUTTER = "StatefulOpenCloseSwingingShutter"
+
+ #
+ # StatefulOpenCloseValve
+ #
+ # Commands:
+ # - close(): Fully close the device
+ # - open(): Fully open the device
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ STATEFUL_OPEN_CLOSE_VALVE = "StatefulOpenCloseValve"
+
+ #
+ # StatefulOperatingModeHeating
+ #
+ # Commands:
+ # - setOperatingMode(any): Set an operating mode
+ #
+ # States:
+ # - core:OperatingModeState (string values: 'antifreeze', 'auto', 'away', 'eco', 'frostprotection', 'manual', 'max', 'normal', 'off', 'on', 'prog', 'program', 'boost'): Current operating mode
+ STATEFUL_OPERATING_MODE_HEATING = "StatefulOperatingModeHeating"
+
+ #
+ # StatefulOrientableAndCloseable
+ #
+ # Commands:
+ # - setClosureAndOrientation(int 0-100, int 0-100): Set both the closure level (0-100%) and relative slats orientation (0-100%) of the device
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ STATEFUL_ORIENTABLE_AND_CLOSEABLE = "StatefulOrientableAndCloseable"
+
+ #
+ # StatefulOrientableAndCloseableShutter
+ #
+ # Commands:
+ # - setClosureAndOrientation(int 0-100, int 0-100): Set both the closure level (0-100%) and relative slats orientation (0-100%) of the device
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ #
+ # Form factor specific: Yes
+ STATEFUL_ORIENTABLE_AND_CLOSEABLE_SHUTTER = "StatefulOrientableAndCloseableShutter"
+
+ #
+ # StatefulOrientableOrCloseable
+ #
+ # Commands:
+ # - setClosureOrOrientation(int 0-100, int 0-100): Set device closure level (0-100%), or put the device in rocking position ('rocker') and set the relative slats orientation (0-100%)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ STATEFUL_ORIENTABLE_OR_CLOSEABLE = "StatefulOrientableOrCloseable"
+
+ #
+ # StatefulOrientablePlusCloseable
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ STATEFUL_ORIENTABLE_PLUS_CLOSEABLE = "StatefulOrientablePlusCloseable"
+
+ #
+ # StatefulOrientablePlusCloseablePergola
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ #
+ # Form factor specific: Yes
+ STATEFUL_ORIENTABLE_PLUS_CLOSEABLE_PERGOLA = (
+ "StatefulOrientablePlusCloseablePergola"
+ )
+
+ #
+ # StatefulOrientableShutter
+ #
+ # Commands:
+ # - setClosure(int 0-100): Closure level (100%=fully close, 0%=open)
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ #
+ # Form factor specific: Yes
+ STATEFUL_ORIENTABLE_SHUTTER = "StatefulOrientableShutter"
+
+ #
+ # StatefulOrientableSlats
+ #
+ # Commands:
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ #
+ # States:
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ STATEFUL_ORIENTABLE_SLATS = "StatefulOrientableSlats"
+
+ #
+ # StatefulRockingShutter
+ #
+ # Commands:
+ # - setClosureOrOrientation(int 0-100, int 0-100): Set device closure level (0-100%), or put the device in rocking position ('rocker') and set the relative slats orientation (0-100%)
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ #
+ # Form factor specific: Yes
+ STATEFUL_ROCKING_SHUTTER = "StatefulRockingShutter"
+
+ #
+ # StatefulSiren
+ #
+ # Commands:
+ # - ring(): Ask the device to start ringing
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ STATEFUL_SIREN = "StatefulSiren"
+
+ #
+ # StatefulSlidingPergola
+ #
+ # Commands:
+ # - setDeployment(int 0-100): Device deployment level (100%=fully deployed, 0%=fully undeployed)
+ #
+ # States:
+ # - core:DeploymentState (int 0-100): Device deployment percentage (0%=fully retracted, 100%=fully deployed)
+ #
+ # Form factor specific: Yes
+ STATEFUL_SLIDING_PERGOLA = "StatefulSlidingPergola"
+
+ #
+ # StatefulStartStop
+ #
+ # Commands:
+ # - start(): Start the default actuator behavior (movement, sound or timer)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:StartedStoppedState (string values: 'started', 'stopped'): Indicate if the sequence if started or stopped
+ STATEFUL_START_STOP = "StatefulStartStop"
+
+ #
+ # StatefulStartStopOven
+ #
+ # Commands:
+ # - start(): Start the default actuator behavior (movement, sound or timer)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:StartedStoppedState (string values: 'started', 'stopped'): Indicate if the sequence if started or stopped
+ #
+ # Form factor specific: Yes
+ STATEFUL_START_STOP_OVEN = "StatefulStartStopOven"
+
+ #
+ # StatefulStartStopWashingMachine
+ #
+ # Commands:
+ # - start(): Start the default actuator behavior (movement, sound or timer)
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ #
+ # States:
+ # - core:StartedStoppedState (string values: 'started', 'stopped'): Indicate if the sequence if started or stopped
+ #
+ # Form factor specific: Yes
+ STATEFUL_START_STOP_WASHING_MACHINE = "StatefulStartStopWashingMachine"
+
+ #
+ # StatefulSwitchable
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ STATEFUL_SWITCHABLE = "StatefulSwitchable"
+
+ #
+ # StatefulSwitchableHeating
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ #
+ # Form factor specific: Yes
+ STATEFUL_SWITCHABLE_HEATING = "StatefulSwitchableHeating"
+
+ #
+ # StatefulSwitchableLight
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ #
+ # Form factor specific: Yes
+ STATEFUL_SWITCHABLE_LIGHT = "StatefulSwitchableLight"
+
+ #
+ # StatefulSwitchablePlug
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ #
+ # Form factor specific: Yes
+ STATEFUL_SWITCHABLE_PLUG = "StatefulSwitchablePlug"
+
+ #
+ # StatefulSwitchableVentilation
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ #
+ # Form factor specific: Yes
+ STATEFUL_SWITCHABLE_VENTILATION = "StatefulSwitchableVentilation"
+
+ #
+ # StatefulThermostat
+ #
+ # Commands:
+ # - setTargetTemperature(float 12.0-30.0): Set the new air temperature to reach
+ #
+ # States:
+ # - core:TargetTemperatureState (float 12.0-30.0): Room target temperature (°C)
+ STATEFUL_THERMOSTAT = "StatefulThermostat"
+
+ #
+ # StatefulThermostatWithSensor
+ #
+ # Commands:
+ # - setTargetTemperature(float 12.0-30.0): Set the new air temperature to reach
+ #
+ # States:
+ # - core:TargetTemperatureState (float 12.0-30.0): Room target temperature (°C)
+ # - core:TemperatureState (float -100.0-100.0): Current room temperature (°C)
+ STATEFUL_THERMOSTAT_WITH_SENSOR = "StatefulThermostatWithSensor"
+
+ #
+ # StatefulVenetianBlind
+ #
+ # Commands:
+ # - setClosureAndOrientation(int 0-100, int 0-100): Set both the closure level (0-100%) and relative slats orientation (0-100%) of the device
+ #
+ # States:
+ # - core:ClosureState (int 0-100): Device closure percentage (0%=fully open, 100%=fully closed)
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ #
+ # Form factor specific: Yes
+ STATEFUL_VENETIAN_BLIND = "StatefulVenetianBlind"
+
+ #
+ # StatefulVenetianSlats
+ #
+ # Commands:
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ #
+ # States:
+ # - core:SlateOrientationState (int 0-100): Slats orientation percentage (0%=not tilted,100%=maximum tilt)
+ #
+ # Form factor specific: Yes
+ STATEFUL_VENETIAN_SLATS = "StatefulVenetianSlats"
+
+ #
+ # StatefulWindowLockWithOpeningStatus
+ #
+ # Commands:
+ # - lock(): Lock the device
+ # - unlock(): Unlock the device
+ #
+ # States:
+ # - core:LockedUnlockedState (string values: 'locked', 'unlocked'): Indicate if the device is locked or unlocked
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ STATEFUL_WINDOW_LOCK_WITH_OPENING_STATUS = "StatefulWindowLockWithOpeningStatus"
+
+ #
+ # StoppableMusicPlayer
+ #
+ # Commands:
+ # - pause(): Pause current action
+ # - play(): Play media
+ # - setVolume(int 0-100): Set the device output volume
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ STOPPABLE_MUSIC_PLAYER = "StoppableMusicPlayer"
+
+ #
+ # Switch
+ #
+ # States:
+ # - core:ActionState (string): Contains the id of current active button action (stateful information). Example, physical position of a switch.
+ # - core:AvailableActionsState (array): List of action ids available
+ SWITCH = "Switch"
+
+ #
+ # SwitchEvent
+ #
+ # States:
+ # - core:AvailableActionsState (array): List of action ids available
+ # - core:ButtonActionsEventState (string): Contains the id of triggered button action (stateless information, makes sense only punctually at a certain time, does not indicate a stable or continuous status).
+ SWITCH_EVENT = "SwitchEvent"
+
+ #
+ # Switchable
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ SWITCHABLE = "Switchable"
+
+ #
+ # SwitchableHeating
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # Form factor specific: Yes
+ SWITCHABLE_HEATING = "SwitchableHeating"
+
+ #
+ # SwitchableHeatingStatus
+ #
+ # States:
+ # - core:OnOffState (string values: 'on', 'off'): Device on/off status
+ #
+ # Form factor specific: Yes
+ SWITCHABLE_HEATING_STATUS = "SwitchableHeatingStatus"
+
+ #
+ # SwitchableLight
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # Form factor specific: Yes
+ SWITCHABLE_LIGHT = "SwitchableLight"
+
+ #
+ # SwitchablePlug
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # Form factor specific: Yes
+ SWITCHABLE_PLUG = "SwitchablePlug"
+
+ #
+ # SwitchableVentilation
+ #
+ # Commands:
+ # - off(): Turn off the device
+ # - on(): Turn on the device
+ #
+ # Form factor specific: Yes
+ SWITCHABLE_VENTILATION = "SwitchableVentilation"
+
+ #
+ # Temperature
+ #
+ # States:
+ # - core:TemperatureState (float -100.0-100.0): Current room temperature (°C)
+ TEMPERATURE = "Temperature"
+
+ #
+ # ThermalEnergyConsumption
+ #
+ # States:
+ # - core:ThermalEnergyConsumptionState (int >= 0): Thermal energy consumption index (Wh)
+ THERMAL_ENERGY_CONSUMPTION = "ThermalEnergyConsumption"
+
+ #
+ # Thermostat
+ #
+ # Commands:
+ # - setTargetTemperature(float 12.0-30.0): Set the new air temperature to reach
+ THERMOSTAT = "Thermostat"
+
+ #
+ # ThermostatOffsetReader
+ #
+ # States:
+ # - core:ThermostatOffsetState (int -5-5): Thermostat offset (°C)
+ THERMOSTAT_OFFSET_READER = "ThermostatOffsetReader"
+
+ #
+ # ThermostatTargetReader
+ #
+ # States:
+ # - core:TargetTemperatureState (float 12.0-30.0): Room target temperature (°C)
+ THERMOSTAT_TARGET_READER = "ThermostatTargetReader"
+
+ #
+ # TiltableOpeningStatus
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ # - core:TiltedState (boolean): Indicate if a device is titled or straight
+ TILTABLE_OPENING_STATUS = "TiltableOpeningStatus"
+
+ #
+ # TiltableWindowOpeningStatus
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ # - core:TiltedState (boolean): Indicate if a device is titled or straight
+ #
+ # Form factor specific: Yes
+ TILTABLE_WINDOW_OPENING_STATUS = "TiltableWindowOpeningStatus"
+
+ #
+ # TiltedStatus
+ #
+ # States:
+ # - core:TiltedState (boolean): Indicate if a device is titled or straight
+ TILTED_STATUS = "TiltedStatus"
+
+ #
+ # UpDown
+ #
+ # Commands:
+ # - down(): Move the device completely down
+ # - stop(): Stop the current actuator behavior (movement, sound or timer)
+ # - up(): Move the device completely up
+ UP_DOWN = "UpDown"
+
+ #
+ # UpdatableComponent
+ #
+ # Commands:
+ # - update(): Update the gateway software. The update may have to be downloaded first, which can take a while.
+ UPDATABLE_COMPONENT = "UpdatableComponent"
+
+ #
+ # VOCConcentration
+ #
+ # States:
+ # - core:VOCConcentrationState (float >= 0.0): Current volatile organic compounds concentration (ppm)
+ VOC_CONCENTRATION = "VOCConcentration"
+
+ #
+ # VenetianBlind
+ #
+ # Commands:
+ # - setClosureAndOrientation(int 0-100, int 0-100): Set both the closure level (0-100%) and relative slats orientation (0-100%) of the device
+ #
+ # Form factor specific: Yes
+ VENETIAN_BLIND = "VenetianBlind"
+
+ #
+ # VenetianSlats
+ #
+ # Commands:
+ # - setOrientation(int 0-100): Set the relative orientation (0-100%) of the device slats
+ #
+ # Form factor specific: Yes
+ VENETIAN_SLATS = "VenetianSlats"
+
+ #
+ # VibrationDetector
+ #
+ # States:
+ # - core:VibrationState (string values: 'detected', 'notDetected'): Indicate if strong vibrations are detected or not
+ VIBRATION_DETECTOR = "VibrationDetector"
+
+ #
+ # VolumeControl
+ #
+ # Commands:
+ # - setVolume(int 0-100): Set the device output volume
+ VOLUME_CONTROL = "VolumeControl"
+
+ #
+ # WaterConsumption
+ #
+ # States:
+ # - core:WaterConsumptionState (float >= 0.0): Water consumption index (m^3)
+ WATER_CONSUMPTION = "WaterConsumption"
+
+ #
+ # WaterDetector
+ #
+ # States:
+ # - core:WaterDetectionState (string values: 'detected', 'notDetected'): Indicate if a water leak is detected or not
+ WATER_DETECTOR = "WaterDetector"
+
+ #
+ # WindDirection
+ #
+ # States:
+ # - core:WindDirectionState (int 0-360): Wind direction (0°=North, clockwise)
+ WIND_DIRECTION = "WindDirection"
+
+ #
+ # WindSpeed
+ #
+ # States:
+ # - core:WindSpeedState (float >= 0.0): Wind speed (km/h)
+ WIND_SPEED = "WindSpeed"
+
+ #
+ # WindSpeedAndDirection
+ #
+ # States:
+ # - core:WindDirectionState (int 0-360): Wind direction (0°=North, clockwise)
+ # - core:WindSpeedState (float >= 0.0): Wind speed (km/h)
+ WIND_SPEED_AND_DIRECTION = "WindSpeedAndDirection"
+
+ #
+ # WindowContactAndVibrationSensor
+ #
+ # States:
+ # - core:ContactState (string values: 'open', 'closed'): Contact sensor is open or closed
+ # - core:VibrationState (string values: 'detected', 'notDetected'): Indicate if strong vibrations are detected or not
+ #
+ # Form factor specific: Yes
+ WINDOW_CONTACT_AND_VIBRATION_SENSOR = "WindowContactAndVibrationSensor"
+
+ #
+ # WindowOpeningStatus
+ #
+ # States:
+ # - core:OpenClosedState (string values: 'open', 'closed'): Indicate if the device is open or closed
+ #
+ # Form factor specific: Yes
+ WINDOW_OPENING_STATUS = "WindowOpeningStatus"
diff --git a/pyoverkiz/models.py b/pyoverkiz/models.py
index 929e233d..fc5a0573 100644
--- a/pyoverkiz/models.py
+++ b/pyoverkiz/models.py
@@ -22,7 +22,9 @@
UIWidget,
UpdateBoxStatus,
)
+from pyoverkiz.enums.command import OverkizCommand, OverkizCommandParam
from pyoverkiz.enums.protocol import Protocol
+from pyoverkiz.enums.server import APIType, Server
from pyoverkiz.obfuscate import obfuscate_email, obfuscate_id, obfuscate_string
from pyoverkiz.types import DATA_TYPE_TO_PYTHON, StateType
@@ -465,19 +467,44 @@ def __len__(self) -> int:
get = __getitem__
-class Command(dict):
+@define(init=False, kw_only=True)
+class Command:
"""Represents an OverKiz Command."""
- name: str
- parameters: list[str | int | float] | None
+ type: int | None = None
+ name: str | OverkizCommand
+ parameters: list[str | int | float | OverkizCommandParam] | None
def __init__(
- self, name: str, parameters: list[str | int | float] | None = None, **_: Any
+ self,
+ name: str | OverkizCommand,
+ parameters: list[str | int | float | OverkizCommandParam] | None = None,
+ type: int | None = None,
+ **_: Any,
):
"""Initialize a command instance and mirror fields into dict base class."""
self.name = name
self.parameters = parameters
- dict.__init__(self, name=name, parameters=parameters)
+ self.type = type
+
+ def to_payload(self) -> dict[str, object]:
+ """Return a JSON-serializable payload for this command.
+
+ The payload uses snake_case keys; the client will convert to camelCase
+ and apply small key fixes (like `deviceURL`) before sending.
+ """
+ payload: dict[str, object] = {"name": str(self.name)}
+
+ if self.type is not None:
+ payload["type"] = self.type
+
+ if self.parameters is not None:
+ payload["parameters"] = [
+ p if isinstance(p, (str, int, float, bool)) else str(p)
+ for p in self.parameters
+ ]
+
+ return payload
@define(init=False, kw_only=True)
@@ -574,7 +601,7 @@ class Execution:
description: str
owner: str = field(repr=obfuscate_email)
state: str
- action_group: list[dict[str, Any]]
+ action_group: ActionGroup
def __init__(
self,
@@ -582,7 +609,7 @@ def __init__(
description: str,
owner: str,
state: str,
- action_group: list[dict[str, Any]],
+ action_group: dict[str, Any],
**_: Any,
):
"""Initialize Execution object from API fields."""
@@ -590,7 +617,7 @@ def __init__(
self.description = description
self.owner = owner
self.state = state
- self.action_group = action_group
+ self.action_group = ActionGroup(**action_group)
@define(init=False, kw_only=True)
@@ -600,14 +627,26 @@ class Action:
device_url: str
commands: list[Command]
- def __init__(self, device_url: str, commands: list[dict[str, Any]]):
+ def __init__(self, device_url: str, commands: list[dict[str, Any] | Command]):
"""Initialize Action from API data and convert nested commands."""
self.device_url = device_url
- self.commands = [Command(**c) for c in commands] if commands else []
+ self.commands = [
+ c if isinstance(c, Command) else Command(**c) for c in commands
+ ]
+
+ def to_payload(self) -> dict[str, object]:
+ """Return a JSON-serializable payload for this action (snake_case).
+
+ The final camelCase conversion is handled by the client.
+ """
+ return {
+ "device_url": self.device_url,
+ "commands": [c.to_payload() for c in self.commands],
+ }
@define(init=False, kw_only=True)
-class Scenario:
+class ActionGroup:
"""An action group is composed of one or more actions.
Each action is related to a single setup device (designated by its device URL) and
@@ -615,7 +654,7 @@ class Scenario:
"""
id: str = field(repr=obfuscate_id)
- creation_time: int
+ creation_time: int | None = None
last_update_time: int | None = None
label: str = field(repr=obfuscate_string)
metadata: str | None = None
@@ -629,10 +668,11 @@ class Scenario:
def __init__(
self,
- creation_time: int,
actions: list[dict[str, Any]],
- oid: str,
+ creation_time: int | None = None,
metadata: str | None = None,
+ oid: str | None = None,
+ id: str | None = None,
last_update_time: int | None = None,
label: str | None = None,
shortcut: bool | None = None,
@@ -642,8 +682,11 @@ def __init__(
notification_title: str | None = None,
**_: Any,
) -> None:
- """Initialize Scenario (action group) from API data."""
- self.id = oid
+ """Initialize ActionGroup from API data and convert nested actions."""
+ if oid is None and id is None:
+ raise ValueError("Either 'oid' or 'id' must be provided")
+
+ self.id = cast(str, oid or id)
self.creation_time = creation_time
self.last_update_time = last_update_time
self.label = (
@@ -656,7 +699,7 @@ def __init__(
self.notification_text = notification_text
self.notification_title = notification_title
self.actions = [Action(**action) for action in actions]
- self.oid = oid
+ self.oid = cast(str, oid or id)
@define(init=False, kw_only=True)
@@ -919,25 +962,36 @@ def __init__(
@define(kw_only=True)
-class OverkizServer:
- """Class to describe an Overkiz server."""
+class ServerConfig:
+ """Connection target details for an Overkiz-compatible server."""
+ server: Server | None
name: str
endpoint: str
manufacturer: str
- configuration_url: str | None
-
+ type: APIType
+ configuration_url: str | None = None
-@define(kw_only=True)
-class LocalToken:
- """Descriptor for a local gateway token."""
-
- label: str
- gateway_id: str = field(repr=obfuscate_id, default=None)
- gateway_creation_time: int
- uuid: str
- scope: str
- expiration_time: int | None
+ def __init__(
+ self,
+ *,
+ server: Server | str | None = None,
+ name: str,
+ endpoint: str,
+ manufacturer: str,
+ type: str | APIType,
+ configuration_url: str | None = None,
+ **_: Any,
+ ) -> None:
+ """Initialize ServerConfig and convert enum fields."""
+ self.server = (
+ server if isinstance(server, Server) or server is None else Server(server)
+ )
+ self.name = name
+ self.endpoint = endpoint
+ self.manufacturer = manufacturer
+ self.type = type if isinstance(type, APIType) else APIType(type)
+ self.configuration_url = configuration_url
@define(kw_only=True)
@@ -976,3 +1030,171 @@ def __init__(
self.parameters = (
[OptionParameter(**p) for p in parameters] if parameters else []
)
+
+
+@define(init=False, kw_only=True)
+class ProtocolType:
+ """Protocol type definition from the reference API."""
+
+ id: int
+ prefix: str
+ name: str
+ label: str
+
+ def __init__(self, id: int, prefix: str, name: str, label: str, **_: Any):
+ """Initialize ProtocolType from API data."""
+ self.id = id
+ self.prefix = prefix
+ self.name = name
+ self.label = label
+
+
+@define(init=False, kw_only=True)
+class ValuePrototype:
+ """Value prototype defining parameter/state value constraints."""
+
+ type: str
+ min_value: int | float | None = None
+ max_value: int | float | None = None
+ enum_values: list[str] | None = None
+ description: str | None = None
+
+ def __init__(
+ self,
+ type: str,
+ min_value: int | float | None = None,
+ max_value: int | float | None = None,
+ enum_values: list[str] | None = None,
+ description: str | None = None,
+ **_: Any,
+ ):
+ """Initialize ValuePrototype from API data."""
+ self.type = type
+ self.min_value = min_value
+ self.max_value = max_value
+ self.enum_values = enum_values
+ self.description = description
+
+
+@define(init=False, kw_only=True)
+class CommandParameter:
+ """Command parameter definition."""
+
+ optional: bool
+ sensitive: bool
+ value_prototypes: list[ValuePrototype]
+
+ def __init__(
+ self,
+ optional: bool,
+ sensitive: bool,
+ value_prototypes: list[dict] | None = None,
+ **_: Any,
+ ):
+ """Initialize CommandParameter from API data."""
+ self.optional = optional
+ self.sensitive = sensitive
+ self.value_prototypes = (
+ [ValuePrototype(**vp) for vp in value_prototypes]
+ if value_prototypes
+ else []
+ )
+
+
+@define(init=False, kw_only=True)
+class CommandPrototype:
+ """Command prototype defining parameters."""
+
+ parameters: list[CommandParameter]
+
+ def __init__(self, parameters: list[dict] | None = None, **_: Any):
+ """Initialize CommandPrototype from API data."""
+ self.parameters = (
+ [CommandParameter(**p) for p in parameters] if parameters else []
+ )
+
+
+@define(init=False, kw_only=True)
+class UIProfileCommand:
+ """UI profile command definition."""
+
+ name: str
+ prototype: CommandPrototype | None = None
+ description: str | None = None
+
+ def __init__(
+ self,
+ name: str,
+ prototype: dict | None = None,
+ description: str | None = None,
+ **_: Any,
+ ):
+ """Initialize UIProfileCommand from API data."""
+ self.name = name
+ self.prototype = CommandPrototype(**prototype) if prototype else None
+ self.description = description
+
+
+@define(init=False, kw_only=True)
+class StatePrototype:
+ """State prototype defining value constraints."""
+
+ value_prototypes: list[ValuePrototype]
+
+ def __init__(self, value_prototypes: list[dict] | None = None, **_: Any):
+ """Initialize StatePrototype from API data."""
+ self.value_prototypes = (
+ [ValuePrototype(**vp) for vp in value_prototypes]
+ if value_prototypes
+ else []
+ )
+
+
+@define(init=False, kw_only=True)
+class UIProfileState:
+ """UI profile state definition."""
+
+ name: str
+ prototype: StatePrototype | None = None
+ description: str | None = None
+
+ def __init__(
+ self,
+ name: str,
+ prototype: dict | None = None,
+ description: str | None = None,
+ **_: Any,
+ ):
+ """Initialize UIProfileState from API data."""
+ self.name = name
+ self.prototype = StatePrototype(**prototype) if prototype else None
+ self.description = description
+
+
+@define(init=False, kw_only=True)
+class UIProfileDefinition:
+ """UI profile definition from the reference API.
+
+ Describes device capabilities through available commands and states.
+ """
+
+ name: str
+ commands: list[UIProfileCommand]
+ states: list[UIProfileState]
+ form_factor: bool
+
+ def __init__(
+ self,
+ name: str,
+ commands: list[dict] | None = None,
+ states: list[dict] | None = None,
+ form_factor: bool = False,
+ **_: Any,
+ ):
+ """Initialize UIProfileDefinition from API data."""
+ self.name = name
+ self.commands = (
+ [UIProfileCommand(**cmd) for cmd in commands] if commands else []
+ )
+ self.states = [UIProfileState(**s) for s in states] if states else []
+ self.form_factor = form_factor
diff --git a/pyoverkiz/serializers.py b/pyoverkiz/serializers.py
new file mode 100644
index 00000000..ee4dc1aa
--- /dev/null
+++ b/pyoverkiz/serializers.py
@@ -0,0 +1,39 @@
+"""Helpers for preparing API payloads.
+
+This module centralizes JSON key formatting and any small transport-specific
+fixes (like mapping "deviceUrl" -> "deviceURL"). Models should produce
+logical snake_case payloads and the client should call `prepare_payload`
+before sending the payload to Overkiz.
+"""
+
+from __future__ import annotations
+
+from typing import Any
+
+import humps
+
+# Small mapping for keys that need special casing beyond simple camelCase.
+_ABBREV_MAP: dict[str, str] = {"deviceUrl": "deviceURL"}
+
+
+def _fix_abbreviations(obj: Any) -> Any:
+ if isinstance(obj, dict):
+ out = {}
+ for k, v in obj.items():
+ k = _ABBREV_MAP.get(k, k)
+ out[k] = _fix_abbreviations(v)
+ return out
+ if isinstance(obj, list):
+ return [_fix_abbreviations(i) for i in obj]
+ return obj
+
+
+def prepare_payload(payload: Any) -> Any:
+ """Convert snake_case payload to API-ready camelCase and apply fixes.
+
+ Example:
+ payload = {"device_url": "x", "commands": [{"name": "close"}]}
+ => {"deviceURL": "x", "commands": [{"name": "close"}]}
+ """
+ camel = humps.camelize(payload)
+ return _fix_abbreviations(camel)
diff --git a/pyoverkiz/utils.py b/pyoverkiz/utils.py
index e395d0bf..23313ed9 100644
--- a/pyoverkiz/utils.py
+++ b/pyoverkiz/utils.py
@@ -5,21 +5,46 @@
import re
from pyoverkiz.const import LOCAL_API_PATH
-from pyoverkiz.models import OverkizServer
+from pyoverkiz.enums.server import APIType, Server
+from pyoverkiz.models import ServerConfig
-def generate_local_server(
+def create_local_server_config(
+ *,
host: str,
name: str = "Somfy Developer Mode",
manufacturer: str = "Somfy",
configuration_url: str | None = None,
-) -> OverkizServer:
- """Generate OverkizServer class for connection with a local API (Somfy Developer mode)."""
- return OverkizServer(
+) -> ServerConfig:
+ """Generate server configuration for a local API (Somfy Developer mode)."""
+ return create_server_config(
name=name,
endpoint=f"https://{host}{LOCAL_API_PATH}",
manufacturer=manufacturer,
+ server=Server.SOMFY_DEVELOPER_MODE,
configuration_url=configuration_url,
+ type=APIType.LOCAL,
+ )
+
+
+def create_server_config(
+ *,
+ name: str,
+ endpoint: str,
+ manufacturer: str,
+ server: Server | str | None = None,
+ type: APIType | str = APIType.CLOUD,
+ configuration_url: str | None = None,
+) -> ServerConfig:
+ """Generate server configuration with the provided endpoint and metadata."""
+ # ServerConfig.__init__ accepts str | enum types and converts them internally
+ return ServerConfig(
+ server=server, # type: ignore[arg-type]
+ name=name,
+ endpoint=endpoint,
+ manufacturer=manufacturer,
+ configuration_url=configuration_url,
+ type=type, # type: ignore[arg-type]
)
diff --git a/pyproject.toml b/pyproject.toml
index d4ac4cf1..0e1f0982 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "pyoverkiz"
-version = "1.20.0"
-description = "Async Python client to interact with internal OverKiz API (e.g. used by Somfy TaHoma)."
+version = "2.0.0"
+description = "A fully asynchronous API client for interaction with smart devices connected to OverKiz, supporting multiple vendors such as Somfy TaHoma and Atlantic Cozytouch."
readme = "README.md"
authors = [
{name = "Mick Vleeshouwer", email = "mick@imick.nl"},
@@ -9,7 +9,7 @@ authors = [
{name = "Thibaut Etienne"},
]
license = {text = "MIT"}
-requires-python = "<4.0,>=3.10"
+requires-python = "<4.0,>=3.12"
packages = [
{ include = "pyoverkiz" }
]
@@ -20,7 +20,15 @@ dependencies = [
"attrs>=21.2",
"boto3<2.0.0,>=1.18.59",
"warrant-lite<2.0.0,>=1.0.4",
- "backports-strenum<2.0.0,>=1.2.4; python_version < \"3.11\"",
+]
+
+[project.optional-dependencies]
+docs = [
+ "mkdocs>=1.5.0",
+ "mkdocs-material>=9.5.0",
+ "mkdocstrings[python]>=0.24.0",
+ "mkdocs-autorefs>=1.0.0",
+ "pymdown-extensions>=10.0",
]
[project.urls]
@@ -31,11 +39,11 @@ repository = "https://github.com/iMicknl/python-overkiz-api"
dev = [
"pytest>=8.3.2,<10.0.0",
"pytest-cov>=5,<8",
- "pre-commit<5.0,>=3.8",
"pytest-asyncio>=0.23.8,<1.4.0",
"ruff>=0.12.0",
"mypy>=1.16.1",
"ty>=0.0.8",
+ "prek>=0.2.27",
]
[tool.ruff.lint]
@@ -67,6 +75,9 @@ select = [
]
ignore = ["E501"] # Line too long
+[tool.ruff.lint.per-file-ignores]
+"tests/**" = ["S101"] # Allow assert in tests
+
[tool.ruff.lint.pydocstyle]
convention = "google" # Accepts: "google", "numpy", or "pep257".
diff --git a/tests/fixtures/exec/current-tahoma-switch.json b/tests/fixtures/exec/current-tahoma-switch.json
new file mode 100644
index 00000000..8f6691ce
--- /dev/null
+++ b/tests/fixtures/exec/current-tahoma-switch.json
@@ -0,0 +1,37 @@
+[
+ {
+ "startTime": 1767003511145,
+ "owner": "email@email.nl",
+ "actionGroup": {
+ "label": "Execution via Home Assistant",
+ "shortcut": false,
+ "notificationTypeMask": 0,
+ "notificationCondition": "NEVER",
+ "actions": [
+ {
+ "deviceURL": "rts://1234-5678-1234/12345678",
+ "commands": [
+ {
+ "type": 1,
+ "name": "close"
+ }
+ ]
+ },
+ {
+ "deviceURL": "rts://1234-5678-1234/87654321",
+ "commands": [
+ {
+ "type": 1,
+ "name": "identify"
+ }
+ ]
+ }
+ ]
+ },
+ "description": "Execution : Execution via Home Assistant",
+ "id": "699dd967-0a19-0481-7a62-99b990a2feb8",
+ "state": "TRANSMITTED",
+ "executionType": "Immediate execution",
+ "executionSubType": "MANUAL_CONTROL"
+ }
+]
diff --git a/tests/test_action_queue.py b/tests/test_action_queue.py
new file mode 100644
index 00000000..752e344d
--- /dev/null
+++ b/tests/test_action_queue.py
@@ -0,0 +1,323 @@
+"""Tests for ActionQueue."""
+
+import asyncio
+from unittest.mock import AsyncMock
+
+import pytest
+
+from pyoverkiz.action_queue import ActionQueue, QueuedExecution
+from pyoverkiz.enums import CommandMode, OverkizCommand
+from pyoverkiz.models import Action, Command
+
+
+@pytest.fixture
+def mock_executor():
+ """Create a mock executor function."""
+
+ async def executor(actions, mode, label):
+ # Return immediately, no delay
+ return f"exec-{len(actions)}-{mode}-{label}"
+
+ return AsyncMock(side_effect=executor)
+
+
+@pytest.mark.asyncio
+async def test_action_queue_single_action(mock_executor):
+ """Test queue with a single action."""
+ queue = ActionQueue(executor=mock_executor, delay=0.1)
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ queued = await queue.add([action])
+ assert isinstance(queued, QueuedExecution)
+
+ # Wait for the batch to execute
+ exec_id = await queued
+ assert exec_id.startswith("exec-1-")
+
+ # Verify executor was called
+ mock_executor.assert_called_once()
+
+
+@pytest.mark.asyncio
+async def test_action_queue_batching(mock_executor):
+ """Test that multiple actions are batched together."""
+ queue = ActionQueue(executor=mock_executor, delay=0.2)
+
+ actions = [
+ Action(
+ device_url=f"io://1234-5678-9012/{i}",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+ for i in range(3)
+ ]
+
+ # Add actions in quick succession
+ queued1 = await queue.add([actions[0]])
+ queued2 = await queue.add([actions[1]])
+ queued3 = await queue.add([actions[2]])
+
+ # All should return the same exec_id
+ exec_id1 = await queued1
+ exec_id2 = await queued2
+ exec_id3 = await queued3
+
+ assert exec_id1 == exec_id2 == exec_id3
+ assert "exec-3-" in exec_id1 # 3 actions in batch
+
+ # Executor should be called only once
+ mock_executor.assert_called_once()
+
+
+@pytest.mark.asyncio
+async def test_action_queue_max_actions_flush(mock_executor):
+ """Test that queue flushes when max actions is reached."""
+ queue = ActionQueue(executor=mock_executor, delay=10.0, max_actions=3)
+
+ actions = [
+ Action(
+ device_url=f"io://1234-5678-9012/{i}",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+ for i in range(5)
+ ]
+
+ # Add 3 actions - should trigger flush
+ queued1 = await queue.add([actions[0]])
+ queued2 = await queue.add([actions[1]])
+ queued3 = await queue.add([actions[2]])
+
+ # Wait a bit for flush to complete
+ await asyncio.sleep(0.05)
+
+ # First 3 should be done
+ assert queued1.is_done()
+ assert queued2.is_done()
+ assert queued3.is_done()
+
+ # Add 2 more - should start a new batch
+ queued4 = await queue.add([actions[3]])
+ queued5 = await queue.add([actions[4]])
+
+ # Wait for second batch
+ await queued4
+ await queued5
+
+ # Should have been called twice (2 batches)
+ assert mock_executor.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_action_queue_mode_change_flush(mock_executor):
+ """Test that queue flushes when command mode changes."""
+ queue = ActionQueue(executor=mock_executor, delay=0.5)
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ # Add action with normal mode
+ queued1 = await queue.add([action], mode=None)
+
+ # Add action with high priority - should flush previous batch
+ queued2 = await queue.add([action], mode=CommandMode.HIGH_PRIORITY)
+
+ # Wait for both batches
+ exec_id1 = await queued1
+ exec_id2 = await queued2
+
+ # Should be different exec_ids (different batches)
+ assert exec_id1 != exec_id2
+
+ # Should have been called twice
+ assert mock_executor.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_action_queue_label_change_flush(mock_executor):
+ """Test that queue flushes when label changes."""
+ queue = ActionQueue(executor=mock_executor, delay=0.5)
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ # Add action with label1
+ queued1 = await queue.add([action], label="label1")
+
+ # Add action with label2 - should flush previous batch
+ queued2 = await queue.add([action], label="label2")
+
+ # Wait for both batches
+ exec_id1 = await queued1
+ exec_id2 = await queued2
+
+ # Should be different exec_ids (different batches)
+ assert exec_id1 != exec_id2
+
+ # Should have been called twice
+ assert mock_executor.call_count == 2
+
+
+@pytest.mark.asyncio
+async def test_action_queue_duplicate_device_merge(mock_executor):
+ """Test that queue merges commands for duplicate devices."""
+ queue = ActionQueue(executor=mock_executor, delay=0.5)
+
+ action1 = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+ action2 = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.OPEN)],
+ )
+
+ queued1 = await queue.add([action1])
+ queued2 = await queue.add([action2])
+
+ exec_id1 = await queued1
+ exec_id2 = await queued2
+
+ assert exec_id1 == exec_id2
+ mock_executor.assert_called_once()
+
+
+@pytest.mark.asyncio
+async def test_action_queue_duplicate_device_merge_order(mock_executor):
+ """Test that command order is preserved when merging."""
+ queue = ActionQueue(executor=mock_executor, delay=0.1)
+
+ action1 = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+ action2 = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.OPEN)],
+ )
+
+ queued = await queue.add([action1, action2])
+ await queued
+
+ args, _ = mock_executor.call_args
+ actions = args[0]
+ assert len(actions) == 1
+ assert [command.name for command in actions[0].commands] == [
+ OverkizCommand.CLOSE,
+ OverkizCommand.OPEN,
+ ]
+
+
+@pytest.mark.asyncio
+async def test_action_queue_manual_flush(mock_executor):
+ """Test manual flush of the queue."""
+ queue = ActionQueue(executor=mock_executor, delay=10.0) # Long delay
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ queued = await queue.add([action])
+
+ # Manually flush
+ await queue.flush()
+
+ # Should be done now
+ assert queued.is_done()
+ exec_id = await queued
+ assert exec_id.startswith("exec-1-")
+
+
+@pytest.mark.asyncio
+async def test_action_queue_shutdown(mock_executor):
+ """Test that shutdown flushes pending actions."""
+ queue = ActionQueue(executor=mock_executor, delay=10.0)
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ queued = await queue.add([action])
+
+ # Shutdown should flush
+ await queue.shutdown()
+
+ # Should be done
+ assert queued.is_done()
+ mock_executor.assert_called_once()
+
+
+@pytest.mark.asyncio
+async def test_action_queue_error_propagation(mock_executor):
+ """Test that exceptions are propagated to all waiters."""
+ # Make executor raise an exception
+ mock_executor.side_effect = ValueError("API Error")
+
+ queue = ActionQueue(executor=mock_executor, delay=0.1)
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ queued1 = await queue.add([action])
+ queued2 = await queue.add([action])
+
+ # Both should raise the same exception
+ with pytest.raises(ValueError, match="API Error"):
+ await queued1
+
+ with pytest.raises(ValueError, match="API Error"):
+ await queued2
+
+
+@pytest.mark.asyncio
+async def test_action_queue_get_pending_count():
+ """Test getting pending action count."""
+ mock_executor = AsyncMock(return_value="exec-123")
+ queue = ActionQueue(executor=mock_executor, delay=0.5)
+
+ assert queue.get_pending_count() == 0
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ await queue.add([action])
+ assert queue.get_pending_count() == 1
+
+ await queue.add([action])
+ assert queue.get_pending_count() == 1
+
+ # Wait for flush
+ await asyncio.sleep(0.6)
+ assert queue.get_pending_count() == 0
+
+
+@pytest.mark.asyncio
+async def test_queued_execution_awaitable():
+ """Test that QueuedExecution is properly awaitable."""
+ queued = QueuedExecution()
+
+ # Set result in background
+ async def set_result():
+ await asyncio.sleep(0.05)
+ queued.set_result("exec-123")
+
+ task = asyncio.create_task(set_result())
+
+ # Await the result
+ result = await queued
+ assert result == "exec-123"
+
+ # Ensure background task has completed
+ await task
diff --git a/tests/test_auth.py b/tests/test_auth.py
new file mode 100644
index 00000000..f7b274bb
--- /dev/null
+++ b/tests/test_auth.py
@@ -0,0 +1,537 @@
+"""Tests for authentication module."""
+
+# ruff: noqa: S105, S106
+# S105/S106: Test credentials use dummy values.
+
+from __future__ import annotations
+
+import base64
+import datetime
+import json
+from unittest.mock import AsyncMock, MagicMock
+
+import pytest
+from aiohttp import ClientSession
+
+from pyoverkiz.auth.base import AuthContext
+from pyoverkiz.auth.credentials import (
+ LocalTokenCredentials,
+ RexelOAuthCodeCredentials,
+ TokenCredentials,
+ UsernamePasswordCredentials,
+)
+from pyoverkiz.auth.factory import (
+ _ensure_rexel,
+ _ensure_token,
+ _ensure_username_password,
+ build_auth_strategy,
+)
+from pyoverkiz.auth.strategies import (
+ BearerTokenAuthStrategy,
+ CozytouchAuthStrategy,
+ LocalTokenAuthStrategy,
+ NexityAuthStrategy,
+ RexelAuthStrategy,
+ SessionLoginStrategy,
+ SomfyAuthStrategy,
+ _decode_jwt_payload,
+)
+from pyoverkiz.enums import APIType, Server
+from pyoverkiz.exceptions import InvalidTokenException
+from pyoverkiz.models import ServerConfig
+
+
+class TestAuthContext:
+ """Test AuthContext functionality."""
+
+ def test_not_expired_no_expiration(self):
+ """Test that context without expiration is not expired."""
+ context = AuthContext(access_token="test_token")
+ assert not context.is_expired()
+
+ def test_not_expired_future_expiration(self):
+ """Test that context with future expiration is not expired."""
+ future = datetime.datetime.now(datetime.UTC) + datetime.timedelta(hours=1)
+ context = AuthContext(access_token="test_token", expires_at=future)
+ assert not context.is_expired()
+
+ def test_expired_past_expiration(self):
+ """Test that context with past expiration is expired."""
+ past = datetime.datetime.now(datetime.UTC) - datetime.timedelta(hours=1)
+ context = AuthContext(access_token="test_token", expires_at=past)
+ assert context.is_expired()
+
+ def test_expired_with_skew(self):
+ """Test that context respects skew time."""
+ # Expires in 3 seconds, but default skew is 5
+ soon = datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=3)
+ context = AuthContext(access_token="test_token", expires_at=soon)
+ assert context.is_expired()
+
+ def test_not_expired_with_custom_skew(self):
+ """Test that custom skew time can be provided."""
+ soon = datetime.datetime.now(datetime.UTC) + datetime.timedelta(seconds=3)
+ context = AuthContext(access_token="test_token", expires_at=soon)
+ assert not context.is_expired(skew_seconds=1)
+
+
+class TestCredentials:
+ """Test credential dataclasses."""
+
+ def test_username_password_credentials(self):
+ """Test UsernamePasswordCredentials creation."""
+ creds = UsernamePasswordCredentials("user@example.com", "password123")
+ assert creds.username == "user@example.com"
+ assert creds.password == "password123"
+
+ def test_token_credentials(self):
+ """Test TokenCredentials creation."""
+ creds = TokenCredentials("my_token_123")
+ assert creds.token == "my_token_123"
+
+ def test_local_token_credentials(self):
+ """Test LocalTokenCredentials creation."""
+ creds = LocalTokenCredentials("local_token_456")
+ assert creds.token == "local_token_456"
+ assert isinstance(creds, TokenCredentials)
+
+ def test_rexel_oauth_credentials(self):
+ """Test RexelOAuthCodeCredentials creation."""
+ creds = RexelOAuthCodeCredentials("auth_code_xyz", "http://redirect.uri")
+ assert creds.code == "auth_code_xyz"
+ assert creds.redirect_uri == "http://redirect.uri"
+
+
+class TestAuthFactory:
+ """Test authentication factory functions."""
+
+ def test_ensure_username_password_valid(self):
+ """Test that valid username/password credentials pass validation."""
+ creds = UsernamePasswordCredentials("user", "pass")
+ result = _ensure_username_password(creds)
+ assert result is creds
+
+ def test_ensure_username_password_invalid(self):
+ """Test that invalid credentials raise TypeError."""
+ creds = TokenCredentials("token")
+ with pytest.raises(TypeError, match="UsernamePasswordCredentials are required"):
+ _ensure_username_password(creds)
+
+ def test_ensure_token_valid(self):
+ """Test that valid token credentials pass validation."""
+ creds = TokenCredentials("token")
+ result = _ensure_token(creds)
+ assert result is creds
+
+ def test_ensure_token_local_valid(self):
+ """Test that LocalTokenCredentials also pass token validation."""
+ creds = LocalTokenCredentials("local_token")
+ result = _ensure_token(creds)
+ assert result is creds
+
+ def test_ensure_token_invalid(self):
+ """Test that invalid credentials raise TypeError."""
+ creds = UsernamePasswordCredentials("user", "pass")
+ with pytest.raises(TypeError, match="TokenCredentials are required"):
+ _ensure_token(creds)
+
+ def test_ensure_rexel_valid(self):
+ """Test that valid Rexel credentials pass validation."""
+ creds = RexelOAuthCodeCredentials("code", "uri")
+ result = _ensure_rexel(creds)
+ assert result is creds
+
+ def test_ensure_rexel_invalid(self):
+ """Test that invalid credentials raise TypeError."""
+ creds = UsernamePasswordCredentials("user", "pass")
+ with pytest.raises(TypeError, match="RexelOAuthCodeCredentials are required"):
+ _ensure_rexel(creds)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_somfy(self):
+ """Test building Somfy auth strategy."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_EUROPE,
+ name="Somfy",
+ endpoint="https://api.somfy.com",
+ manufacturer="Somfy",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, SomfyAuthStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_cozytouch(self):
+ """Test building Cozytouch auth strategy."""
+ server_config = ServerConfig(
+ server=Server.ATLANTIC_COZYTOUCH,
+ name="Cozytouch",
+ endpoint="https://api.cozytouch.com",
+ manufacturer="Atlantic",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, CozytouchAuthStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_nexity(self):
+ """Test building Nexity auth strategy."""
+ server_config = ServerConfig(
+ server=Server.NEXITY,
+ name="Nexity",
+ endpoint="https://api.nexity.com",
+ manufacturer="Nexity",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, NexityAuthStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_rexel(self):
+ """Test building Rexel auth strategy."""
+ server_config = ServerConfig(
+ server=Server.REXEL,
+ name="Rexel",
+ endpoint="https://api.rexel.com",
+ manufacturer="Rexel",
+ type=APIType.CLOUD,
+ )
+ credentials = RexelOAuthCodeCredentials("code", "http://redirect.uri")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, RexelAuthStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_local_token(self):
+ """Test building local token auth strategy."""
+ server_config = ServerConfig(
+ server=None,
+ name="Local",
+ endpoint="https://gateway.local",
+ manufacturer="Overkiz",
+ type=APIType.LOCAL,
+ )
+ credentials = LocalTokenCredentials("local_token")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, LocalTokenAuthStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_local_bearer(self):
+ """Test building local bearer token auth strategy."""
+ server_config = ServerConfig(
+ server=None,
+ name="Local",
+ endpoint="https://gateway.local",
+ manufacturer="Overkiz",
+ type=APIType.LOCAL,
+ )
+ credentials = TokenCredentials("bearer_token")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, BearerTokenAuthStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_cloud_bearer(self):
+ """Test building cloud bearer token auth strategy."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_OCEANIA,
+ name="Somfy Oceania",
+ endpoint="https://api.somfy.com.au",
+ manufacturer="Somfy",
+ type=APIType.CLOUD,
+ )
+ credentials = TokenCredentials("bearer_token")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, BearerTokenAuthStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_session_login(self):
+ """Test building generic session login auth strategy."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_OCEANIA,
+ name="Somfy Oceania",
+ endpoint="https://api.somfy.com.au",
+ manufacturer="Somfy",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+ assert isinstance(strategy, SessionLoginStrategy)
+
+ @pytest.mark.asyncio
+ async def test_build_auth_strategy_wrong_credentials_type(self):
+ """Test that wrong credentials type raises TypeError."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_EUROPE,
+ name="Somfy",
+ endpoint="https://api.somfy.com",
+ manufacturer="Somfy",
+ type=APIType.CLOUD,
+ )
+ credentials = TokenCredentials("token") # Wrong type for Somfy
+ session = AsyncMock(spec=ClientSession)
+
+ with pytest.raises(TypeError, match="UsernamePasswordCredentials are required"):
+ build_auth_strategy(
+ server_config=server_config,
+ credentials=credentials,
+ session=session,
+ ssl_context=True,
+ )
+
+
+class TestSessionLoginStrategy:
+ """Test SessionLoginStrategy."""
+
+ @pytest.mark.asyncio
+ async def test_login_success(self):
+ """Test successful login with 200 response."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_OCEANIA,
+ name="Test",
+ endpoint="https://api.test.com/",
+ manufacturer="Test",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ mock_response = MagicMock()
+ mock_response.status = 200
+ mock_response.json = AsyncMock(return_value={"success": True})
+ mock_response.__aenter__ = AsyncMock(return_value=mock_response)
+ mock_response.__aexit__ = AsyncMock(return_value=None)
+ session.post = MagicMock(return_value=mock_response)
+
+ strategy = SessionLoginStrategy(
+ credentials, session, server_config, True, APIType.CLOUD
+ )
+ await strategy.login()
+
+ session.post.assert_called_once()
+
+ @pytest.mark.asyncio
+ async def test_login_204_no_content(self):
+ """Test login with 204 No Content response."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_OCEANIA,
+ name="Test",
+ endpoint="https://api.test.com/",
+ manufacturer="Test",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ mock_response = MagicMock()
+ mock_response.status = 204
+ mock_response.json = AsyncMock()
+ mock_response.__aenter__ = AsyncMock(return_value=mock_response)
+ mock_response.__aexit__ = AsyncMock(return_value=None)
+ session.post = MagicMock(return_value=mock_response)
+
+ strategy = SessionLoginStrategy(
+ credentials, session, server_config, True, APIType.CLOUD
+ )
+ await strategy.login()
+
+ # Should not call json() for 204 response
+ assert not mock_response.json.called
+
+ @pytest.mark.asyncio
+ async def test_refresh_if_needed_no_refresh(self):
+ """Test that refresh_if_needed returns False when no refresh needed."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_OCEANIA,
+ name="Test",
+ endpoint="https://api.test.com/",
+ manufacturer="Test",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = SessionLoginStrategy(
+ credentials, session, server_config, True, APIType.CLOUD
+ )
+ result = await strategy.refresh_if_needed()
+
+ assert not result
+
+ def test_auth_headers_no_token(self):
+ """Test that auth headers return empty dict when no token."""
+ server_config = ServerConfig(
+ server=Server.SOMFY_OCEANIA,
+ name="Test",
+ endpoint="https://api.test.com/",
+ manufacturer="Test",
+ type=APIType.CLOUD,
+ )
+ credentials = UsernamePasswordCredentials("user", "pass")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = SessionLoginStrategy(
+ credentials, session, server_config, True, APIType.CLOUD
+ )
+ headers = strategy.auth_headers()
+
+ assert headers == {}
+
+
+class TestBearerTokenAuthStrategy:
+ """Test BearerTokenAuthStrategy."""
+
+ @pytest.mark.asyncio
+ async def test_login_no_op(self):
+ """Test that login is a no-op for bearer tokens."""
+ server_config = ServerConfig(
+ server=None,
+ name="Test",
+ endpoint="https://api.test.com/",
+ manufacturer="Test",
+ type=APIType.CLOUD,
+ )
+ credentials = TokenCredentials("my_bearer_token")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = BearerTokenAuthStrategy(
+ credentials, session, server_config, True, APIType.CLOUD
+ )
+ result = await strategy.login()
+
+ # Login should be a no-op
+ assert result is None
+
+ def test_auth_headers_with_token(self):
+ """Test that auth headers include Bearer token."""
+ server_config = ServerConfig(
+ server=None,
+ name="Test",
+ endpoint="https://api.test.com/",
+ manufacturer="Test",
+ type=APIType.CLOUD,
+ )
+ credentials = TokenCredentials("my_bearer_token")
+ session = AsyncMock(spec=ClientSession)
+
+ strategy = BearerTokenAuthStrategy(
+ credentials, session, server_config, True, APIType.CLOUD
+ )
+ headers = strategy.auth_headers()
+
+ assert headers == {"Authorization": "Bearer my_bearer_token"}
+
+
+class TestRexelAuthStrategy:
+ """Tests for Rexel auth specifics."""
+
+ @pytest.mark.asyncio
+ async def test_exchange_token_error_response(self):
+ """Ensure OAuth error payloads raise InvalidTokenException before parsing access token."""
+ server_config = ServerConfig(
+ server=Server.REXEL,
+ name="Rexel",
+ endpoint="https://api.rexel.com",
+ manufacturer="Rexel",
+ type=APIType.CLOUD,
+ )
+ credentials = RexelOAuthCodeCredentials("code", "https://redirect")
+ session = AsyncMock(spec=ClientSession)
+
+ mock_response = MagicMock()
+ mock_response.status = 400
+ mock_response.json = AsyncMock(
+ return_value={"error": "invalid_grant", "error_description": "bad grant"}
+ )
+ mock_response.__aenter__ = AsyncMock(return_value=mock_response)
+ mock_response.__aexit__ = AsyncMock(return_value=None)
+ session.post = MagicMock(return_value=mock_response)
+
+ strategy = RexelAuthStrategy(
+ credentials, session, server_config, True, APIType.CLOUD
+ )
+
+ with pytest.raises(InvalidTokenException, match="bad grant"):
+ await strategy._exchange_token({"grant_type": "authorization_code"})
+
+ def test_ensure_consent_missing(self):
+ """Raising when JWT consent claim is missing or incorrect."""
+ payload_segment = (
+ base64.urlsafe_b64encode(json.dumps({"consent": "other"}).encode())
+ .decode()
+ .rstrip("=")
+ )
+ token = f"header.{payload_segment}.sig"
+
+ with pytest.raises(InvalidTokenException, match="Consent is missing"):
+ RexelAuthStrategy._ensure_consent(token)
+
+ def test_decode_jwt_payload_invalid_format(self):
+ """Malformed tokens raise InvalidTokenException during decoding."""
+ with pytest.raises(InvalidTokenException):
+ _decode_jwt_payload("invalid.token")
diff --git a/tests/test_client.py b/tests/test_client.py
index bd70a0bb..0276d3f4 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -1,7 +1,7 @@
"""Unit tests for the high-level OverkizClient behaviour and responses."""
-# ruff: noqa: S101, ASYNC230
-# S101: Tests use assert statements
+# ruff: noqa: ASYNC230, S106
+# S106: Test credentials use dummy values.
# ASYNC230: Blocking open() is acceptable for reading test fixtures
from __future__ import annotations
@@ -15,11 +15,14 @@
from pytest_asyncio import fixture
from pyoverkiz import exceptions
+from pyoverkiz.auth.credentials import (
+ LocalTokenCredentials,
+ UsernamePasswordCredentials,
+)
from pyoverkiz.client import OverkizClient
-from pyoverkiz.const import SUPPORTED_SERVERS
-from pyoverkiz.enums import APIType, DataType
+from pyoverkiz.enums import APIType, DataType, Server
from pyoverkiz.models import Option
-from pyoverkiz.utils import generate_local_server
+from pyoverkiz.utils import create_local_server_config
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -30,26 +33,28 @@ class TestOverkizClient:
@fixture
async def client(self):
"""Fixture providing an OverkizClient configured for the cloud server."""
- return OverkizClient("username", "password", SUPPORTED_SERVERS["somfy_europe"])
+ return OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("username", "password"),
+ )
@fixture
async def local_client(self):
"""Fixture providing an OverkizClient configured for a local (developer) server."""
return OverkizClient(
- "username",
- "password",
- generate_local_server("gateway-1234-5678-1243.local:8443"),
+ server=create_local_server_config(host="gateway-1234-5678-1243.local:8443"),
+ credentials=LocalTokenCredentials(token="token"),
)
@pytest.mark.asyncio
async def test_get_api_type_cloud(self, client: OverkizClient):
"""Verify that a cloud-configured client reports APIType.CLOUD."""
- assert client.api_type == APIType.CLOUD
+ assert client.server_config.type == APIType.CLOUD
@pytest.mark.asyncio
async def test_get_api_type_local(self, local_client: OverkizClient):
"""Verify that a local-configured client reports APIType.LOCAL."""
- assert local_client.api_type == APIType.LOCAL
+ assert local_client.server_config.type == APIType.LOCAL
@pytest.mark.asyncio
async def test_get_devices_basic(self, client: OverkizClient):
@@ -470,6 +475,41 @@ async def test_get_setup_options(
for option in options:
assert isinstance(option, Option)
+ @pytest.mark.asyncio
+ async def test_execute_action_group_omits_none_fields(self, client: OverkizClient):
+ """Ensure `type` and `parameters` that are None are omitted from the request payload."""
+ from pyoverkiz.enums.command import OverkizCommand
+ from pyoverkiz.models import Action, Command
+
+ action = Action(
+ "rts://2025-8464-6867/16756006",
+ [Command(name=OverkizCommand.CLOSE, parameters=None, type=None)],
+ )
+
+ resp = MockResponse('{"execId": "exec-123"}')
+
+ with patch.object(aiohttp.ClientSession, "post") as mock_post:
+ mock_post.return_value = resp
+
+ exec_id = await client.execute_action_group([action])
+
+ assert exec_id == "exec-123"
+
+ assert mock_post.called
+ _, kwargs = mock_post.call_args
+ sent_json = kwargs.get("json")
+ assert sent_json is not None
+
+ # The client should have converted payload to camelCase and applied
+ # abbreviation fixes (deviceURL) before sending.
+ action_sent = sent_json["actions"][0]
+ assert action_sent.get("deviceURL") == action.device_url
+
+ cmd = action_sent["commands"][0]
+ assert "type" not in cmd
+ assert "parameters" not in cmd
+ assert cmd["name"] == "close"
+
@pytest.mark.parametrize(
"fixture_name, option_name, instance",
[
@@ -514,7 +554,7 @@ async def test_get_setup_option(
],
)
@pytest.mark.asyncio
- async def test_get_scenarios(
+ async def test_get_action_groups(
self,
client: OverkizClient,
fixture_name: str,
@@ -528,16 +568,16 @@ async def test_get_scenarios(
resp = MockResponse(action_group_mock.read())
with patch.object(aiohttp.ClientSession, "get", return_value=resp):
- scenarios = await client.get_scenarios()
+ action_groups = await client.get_action_groups()
- assert len(scenarios) == scenario_count
+ assert len(action_groups) == scenario_count
- for scenario in scenarios:
- assert scenario.oid
- assert scenario.label is not None
- assert scenario.actions
+ for action_group in action_groups:
+ assert action_group.oid
+ assert action_group.label is not None
+ assert action_group.actions
- for action in scenario.actions:
+ for action in action_group.actions:
assert action.device_url
assert action.commands
diff --git a/tests/test_client_queue_integration.py b/tests/test_client_queue_integration.py
new file mode 100644
index 00000000..01df9d12
--- /dev/null
+++ b/tests/test_client_queue_integration.py
@@ -0,0 +1,223 @@
+"""Integration tests for OverkizClient with ActionQueue."""
+
+import asyncio
+from unittest.mock import AsyncMock, patch
+
+import pytest
+
+from pyoverkiz.action_queue import ActionQueueSettings
+from pyoverkiz.auth import UsernamePasswordCredentials
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.enums import OverkizCommand, Server
+from pyoverkiz.models import Action, Command
+
+
+@pytest.mark.asyncio
+async def test_client_without_queue_executes_immediately():
+ """Test that client without queue executes actions immediately."""
+ client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("test@example.com", "test"),
+ action_queue=False,
+ )
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ # Mock the internal execution
+ with patch.object(
+ client, "_OverkizClient__post", new_callable=AsyncMock
+ ) as mock_post:
+ mock_post.return_value = {"execId": "exec-123"}
+
+ result = await client.execute_action_group([action])
+
+ # Should return exec_id directly (string)
+ assert isinstance(result, str)
+ assert result == "exec-123"
+
+ # Should have called API immediately
+ mock_post.assert_called_once()
+
+ await client.close()
+
+
+@pytest.mark.asyncio
+async def test_client_with_queue_batches_actions():
+ """Test that client with queue batches multiple actions."""
+ client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("test@example.com", "test"),
+ action_queue=ActionQueueSettings(delay=0.1),
+ )
+
+ actions = [
+ Action(
+ device_url=f"io://1234-5678-9012/{i}",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+ for i in range(3)
+ ]
+
+ with patch.object(
+ client, "_OverkizClient__post", new_callable=AsyncMock
+ ) as mock_post:
+ mock_post.return_value = {"execId": "exec-batched"}
+
+ # Queue multiple actions quickly - start them as tasks to allow batching
+ task1 = asyncio.create_task(client.execute_action_group([actions[0]]))
+ task2 = asyncio.create_task(client.execute_action_group([actions[1]]))
+ task3 = asyncio.create_task(client.execute_action_group([actions[2]]))
+
+ # Give them a moment to queue
+ await asyncio.sleep(0.01)
+
+ # Should have 3 actions pending
+ assert client.get_pending_actions_count() == 3
+
+ # Wait for all to execute
+ exec_id1 = await task1
+ exec_id2 = await task2
+ exec_id3 = await task3
+
+ # All should have the same exec_id (batched together)
+ assert exec_id1 == exec_id2 == exec_id3 == "exec-batched"
+
+ # Should have called API only once (batched)
+ mock_post.assert_called_once()
+
+ # Check that all 3 actions were in the batch
+ call_args = mock_post.call_args
+ payload = call_args[0][1] # Second argument is the payload
+ assert len(payload["actions"]) == 3
+
+ await client.close()
+
+
+@pytest.mark.asyncio
+async def test_client_manual_flush():
+ """Test manually flushing the queue."""
+ client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("test@example.com", "test"),
+ action_queue=ActionQueueSettings(delay=10.0), # Long delay
+ )
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ with patch.object(
+ client, "_OverkizClient__post", new_callable=AsyncMock
+ ) as mock_post:
+ mock_post.return_value = {"execId": "exec-flushed"}
+
+ # Start execution as a task to allow checking pending count
+ exec_task = asyncio.create_task(client.execute_action_group([action]))
+
+ # Give it a moment to queue
+ await asyncio.sleep(0.01)
+
+ # Should have 1 action pending
+ assert client.get_pending_actions_count() == 1
+
+ # Manually flush
+ await client.flush_action_queue()
+
+ # Should be executed now
+ assert client.get_pending_actions_count() == 0
+
+ exec_id = await exec_task
+ assert exec_id == "exec-flushed"
+
+ mock_post.assert_called_once()
+
+ await client.close()
+
+
+@pytest.mark.asyncio
+async def test_client_close_flushes_queue():
+ """Test that closing the client flushes pending actions."""
+ client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("test@example.com", "test"),
+ action_queue=ActionQueueSettings(delay=10.0),
+ )
+
+ action = Action(
+ device_url="io://1234-5678-9012/1",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+
+ with patch.object(
+ client, "_OverkizClient__post", new_callable=AsyncMock
+ ) as mock_post:
+ mock_post.return_value = {"execId": "exec-closed"}
+
+ # Start execution as a task
+ exec_task = asyncio.create_task(client.execute_action_group([action]))
+
+ # Give it a moment to queue
+ await asyncio.sleep(0.01)
+
+ # Close should flush
+ await client.close()
+
+ # Should be executed
+ exec_id = await exec_task
+ assert exec_id == "exec-closed"
+
+ mock_post.assert_called_once()
+
+
+@pytest.mark.asyncio
+async def test_client_queue_respects_max_actions():
+ """Test that queue flushes when max actions is reached."""
+ client = OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials("test@example.com", "test"),
+ action_queue=ActionQueueSettings(
+ delay=10.0,
+ max_actions=2, # Max 2 actions
+ ),
+ )
+
+ actions = [
+ Action(
+ device_url=f"io://1234-5678-9012/{i}",
+ commands=[Command(name=OverkizCommand.CLOSE)],
+ )
+ for i in range(3)
+ ]
+
+ with patch.object(
+ client, "_OverkizClient__post", new_callable=AsyncMock
+ ) as mock_post:
+ mock_post.return_value = {"execId": "exec-123"}
+
+ # Add 2 actions as tasks to trigger flush
+ task1 = asyncio.create_task(client.execute_action_group([actions[0]]))
+ task2 = asyncio.create_task(client.execute_action_group([actions[1]]))
+
+ # Wait a bit for flush
+ await asyncio.sleep(0.05)
+
+ # First 2 should be done
+ exec_id1 = await task1
+ exec_id2 = await task2
+ assert exec_id1 == "exec-123"
+ assert exec_id2 == "exec-123"
+
+ # Add third action - starts new batch
+ exec_id3 = await client.execute_action_group([actions[2]])
+
+ # Should have exec_id directly (waited for batch to complete)
+ assert exec_id3 == "exec-123"
+
+ # Should have been called twice (2 batches)
+ assert mock_post.call_count == 2
+
+ await client.close()
diff --git a/tests/test_enums.py b/tests/test_enums.py
index b693231a..79abfd8b 100644
--- a/tests/test_enums.py
+++ b/tests/test_enums.py
@@ -1,8 +1,5 @@
"""Tests for enum helper behaviour and expected values."""
-# ruff: noqa: S101
-# Tests use assert statements
-
from pyoverkiz.enums import (
EventName,
ExecutionSubType,
diff --git a/tests/test_models.py b/tests/test_models.py
index 19ef17a4..5f76c32f 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,8 +1,5 @@
"""Unit tests for models (Device, State and States helpers)."""
-# ruff: noqa: S101
-# Tests use assert statements
-
from __future__ import annotations
import humps
@@ -303,3 +300,33 @@ def test_bad_list_value(self):
state = State(name="state", type=DataType.BOOLEAN, value=False)
with pytest.raises(TypeError):
assert state.value_as_list
+
+
+def test_command_to_payload_omits_none():
+ """Command.to_payload omits None fields from the resulting payload."""
+ from pyoverkiz.enums.command import OverkizCommand
+ from pyoverkiz.models import Command
+
+ cmd = Command(name=OverkizCommand.CLOSE, parameters=None, type=None)
+ payload = cmd.to_payload()
+
+ assert payload == {"name": "close"}
+
+
+def test_action_to_payload_and_parameters_conversion():
+ """Action.to_payload converts nested Command enums to primitives."""
+ from pyoverkiz.enums.command import OverkizCommand, OverkizCommandParam
+ from pyoverkiz.models import Action, Command
+
+ cmd = Command(
+ name=OverkizCommand.SET_LEVEL, parameters=[10, OverkizCommandParam.A], type=1
+ )
+ action = Action("rts://2025-8464-6867/16756006", [cmd])
+
+ payload = action.to_payload()
+
+ assert payload["device_url"] == "rts://2025-8464-6867/16756006"
+ assert payload["commands"][0]["name"] == "setLevel"
+ assert payload["commands"][0]["type"] == 1
+ # parameters should be converted to primitives (enum -> str)
+ assert payload["commands"][0]["parameters"] == [10, "A"]
diff --git a/tests/test_obfuscate.py b/tests/test_obfuscate.py
index 41063946..ec129460 100644
--- a/tests/test_obfuscate.py
+++ b/tests/test_obfuscate.py
@@ -1,8 +1,5 @@
"""Tests for the obfuscation utilities used in fixtures and logging."""
-# ruff: noqa: S101
-# Tests use assert statements
-
import pytest
from pyoverkiz.obfuscate import obfuscate_email, obfuscate_sensitive_data
diff --git a/tests/test_serializers.py b/tests/test_serializers.py
new file mode 100644
index 00000000..603132e2
--- /dev/null
+++ b/tests/test_serializers.py
@@ -0,0 +1,37 @@
+"""Tests for pyoverkiz.serializers."""
+
+from __future__ import annotations
+
+from pyoverkiz.serializers import prepare_payload
+
+
+def test_prepare_payload_camelizes_and_fixes_device_url():
+ """Test that prepare_payload converts snake_case to camelCase and fixes abbreviations."""
+ payload = {
+ "label": "test",
+ "actions": [{"device_url": "rts://1/2", "commands": [{"name": "close"}]}],
+ }
+
+ final = prepare_payload(payload)
+
+ assert final["label"] == "test"
+ assert "deviceURL" in final["actions"][0]
+ assert final["actions"][0]["deviceURL"] == "rts://1/2"
+
+
+def test_prepare_payload_nested_lists_and_dicts():
+ """Test that prepare_payload handles nested lists and dicts correctly."""
+ payload = {
+ "actions": [
+ {
+ "device_url": "rts://1/2",
+ "commands": [{"name": "setLevel", "parameters": [10, "A"]}],
+ }
+ ]
+ }
+
+ final = prepare_payload(payload)
+
+ cmd = final["actions"][0]["commands"][0]
+ assert cmd["name"] == "setLevel"
+ assert cmd["parameters"] == [10, "A"]
diff --git a/tests/test_ui_profile.py b/tests/test_ui_profile.py
new file mode 100644
index 00000000..df88bc72
--- /dev/null
+++ b/tests/test_ui_profile.py
@@ -0,0 +1,146 @@
+"""Tests for UIProfileDefinition models."""
+
+from pyoverkiz.models import (
+ CommandParameter,
+ UIProfileCommand,
+ UIProfileDefinition,
+ UIProfileState,
+ ValuePrototype,
+)
+
+
+def test_value_prototype_with_range():
+ """Test ValuePrototype with min/max range."""
+ vp = ValuePrototype(type="INT", min_value=0, max_value=100)
+ assert vp.type == "INT"
+ assert vp.min_value == 0
+ assert vp.max_value == 100
+ assert vp.enum_values is None
+
+
+def test_value_prototype_with_enum():
+ """Test ValuePrototype with enum values."""
+ vp = ValuePrototype(
+ type="STRING", enum_values=["low", "high"], description="Fan speed mode"
+ )
+ assert vp.type == "STRING"
+ assert vp.enum_values == ["low", "high"]
+ assert vp.description == "Fan speed mode"
+
+
+def test_command_parameter():
+ """Test CommandParameter with value prototypes."""
+ param = CommandParameter(
+ optional=False,
+ sensitive=False,
+ value_prototypes=[{"type": "INT", "min_value": 0, "max_value": 100}],
+ )
+ assert param.optional is False
+ assert param.sensitive is False
+ assert len(param.value_prototypes) == 1
+ assert param.value_prototypes[0].type == "INT"
+
+
+def test_ui_profile_command():
+ """Test UIProfileCommand with prototype."""
+ cmd = UIProfileCommand(
+ name="setFanSpeedLevel",
+ prototype={
+ "parameters": [
+ {
+ "optional": False,
+ "sensitive": False,
+ "value_prototypes": [
+ {"type": "INT", "min_value": 0, "max_value": 100}
+ ],
+ }
+ ]
+ },
+ description="Set the device fan speed level",
+ )
+ assert cmd.name == "setFanSpeedLevel"
+ assert cmd.description == "Set the device fan speed level"
+ assert cmd.prototype is not None
+ assert len(cmd.prototype.parameters) == 1
+
+
+def test_ui_profile_state():
+ """Test UIProfileState with prototype."""
+ state = UIProfileState(
+ name="core:TemperatureState",
+ prototype={
+ "value_prototypes": [
+ {"type": "FLOAT", "min_value": -100.0, "max_value": 100.0}
+ ]
+ },
+ description="Current room temperature",
+ )
+ assert state.name == "core:TemperatureState"
+ assert state.description == "Current room temperature"
+ assert state.prototype is not None
+ assert len(state.prototype.value_prototypes) == 1
+
+
+def test_ui_profile_definition():
+ """Test complete UIProfileDefinition."""
+ profile = UIProfileDefinition(
+ name="AirFan",
+ commands=[
+ {
+ "name": "setFanSpeedLevel",
+ "prototype": {
+ "parameters": [
+ {
+ "optional": False,
+ "sensitive": False,
+ "value_prototypes": [
+ {"type": "INT", "min_value": 0, "max_value": 100}
+ ],
+ }
+ ]
+ },
+ "description": "Set fan speed",
+ }
+ ],
+ states=[
+ {
+ "name": "core:FanSpeedState",
+ "prototype": {
+ "value_prototypes": [
+ {"type": "INT", "min_value": 0, "max_value": 100}
+ ]
+ },
+ "description": "Current fan speed",
+ }
+ ],
+ form_factor=False,
+ )
+
+ assert profile.name == "AirFan"
+ assert len(profile.commands) == 1
+ assert len(profile.states) == 1
+ assert profile.form_factor is False
+
+ # Verify command structure
+ cmd = profile.commands[0]
+ assert cmd.name == "setFanSpeedLevel"
+ assert cmd.description == "Set fan speed"
+ assert cmd.prototype is not None
+ assert len(cmd.prototype.parameters) == 1
+
+ # Verify state structure
+ state = profile.states[0]
+ assert state.name == "core:FanSpeedState"
+ assert state.description == "Current fan speed"
+ assert state.prototype is not None
+ assert len(state.prototype.value_prototypes) == 1
+
+
+def test_ui_profile_definition_minimal():
+ """Test UIProfileDefinition with minimal data."""
+ profile = UIProfileDefinition(name="MinimalProfile")
+
+ assert profile.name == "MinimalProfile"
+ assert profile.commands == []
+ assert profile.states == []
+ assert profile.form_factor is False
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 22738e16..8b33b8f7 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,11 +1,8 @@
"""Tests for utility helper functions like server generation and gateway checks."""
-# ruff: noqa: S101
-# Tests use assert statements
-
import pytest
-from pyoverkiz.utils import generate_local_server, is_overkiz_gateway
+from pyoverkiz.utils import create_local_server_config, is_overkiz_gateway
LOCAL_HOST = "gateway-1234-5678-1243.local:8443"
LOCAL_HOST_BY_IP = "192.168.1.105:8443"
@@ -14,9 +11,9 @@
class TestUtils:
"""Tests for utility helpers like local server generation and gateway checks."""
- def test_generate_local_server(self):
+ def test_create_local_server_config(self):
"""Create a local server descriptor using the host and default values."""
- local_server = generate_local_server(host=LOCAL_HOST)
+ local_server = create_local_server_config(host=LOCAL_HOST)
assert local_server
assert (
@@ -27,9 +24,9 @@ def test_generate_local_server(self):
assert local_server.name == "Somfy Developer Mode"
assert local_server.configuration_url is None
- def test_generate_local_server_by_ip(self):
+ def test_create_local_server_config_by_ip(self):
"""Create a local server descriptor using an IP host and custom fields."""
- local_server = generate_local_server(
+ local_server = create_local_server_config(
host=LOCAL_HOST_BY_IP,
manufacturer="Test Manufacturer",
name="Test Name",
diff --git a/utils/generate_enums.py b/utils/generate_enums.py
new file mode 100644
index 00000000..fe768d94
--- /dev/null
+++ b/utils/generate_enums.py
@@ -0,0 +1,695 @@
+"""Generate enum files from the Overkiz API reference data."""
+
+# ruff: noqa: T201
+
+from __future__ import annotations
+
+import asyncio
+import os
+import re
+from pathlib import Path
+from typing import cast
+
+from pyoverkiz.auth.credentials import UsernamePasswordCredentials
+from pyoverkiz.client import OverkizClient
+from pyoverkiz.enums import Server
+from pyoverkiz.exceptions import OverkizException
+from pyoverkiz.models import UIProfileDefinition, ValuePrototype
+
+# Hardcoded protocols that may not be available on all servers
+# Format: (name, prefix)
+ADDITIONAL_PROTOCOLS = [
+ ("HLRR_WIFI", "hlrrwifi"),
+ ("MODBUSLINK", "modbuslink"),
+ ("RTN", "rtn"),
+]
+
+# Hardcoded widgets that may not be available on all servers
+# Format: (enum_name, value)
+ADDITIONAL_WIDGETS = [
+ ("ALARM_PANEL_CONTROLLER", "AlarmPanelController"),
+ ("CYCLIC_GARAGE_DOOR", "CyclicGarageDoor"),
+ ("CYCLIC_SWINGING_GATE_OPENER", "CyclicSwingingGateOpener"),
+ ("DISCRETE_GATE_WITH_PEDESTRIAN_POSITION", "DiscreteGateWithPedestrianPosition"),
+ ("HLRR_WIFI_BRIDGE", "HLRRWifiBridge"),
+ ("NODE", "Node"),
+]
+
+
+async def generate_protocol_enum() -> None:
+ """Generate the Protocol enum from the Overkiz API."""
+ username = os.environ["OVERKIZ_USERNAME"]
+ password = os.environ["OVERKIZ_PASSWORD"]
+
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials(username, password),
+ ) as client:
+ await client.login()
+
+ protocol_types = await client.get_reference_protocol_types()
+
+ # Build list of protocol entries (name, prefix, id, label)
+ protocols: list[tuple[str, str, int | None, str | None]] = [
+ (p.name, p.prefix, p.id, p.label) for p in protocol_types
+ ]
+
+ # Add hardcoded protocols that may not be on all servers (avoid duplicates)
+ fetched_prefixes = {p.prefix for p in protocol_types}
+ for name, prefix in ADDITIONAL_PROTOCOLS:
+ if prefix not in fetched_prefixes:
+ protocols.append((name, prefix, None, None))
+
+ # Sort by name for consistent output
+ protocols.sort(key=lambda p: p[0])
+
+ # Generate the enum file content
+ lines = [
+ '"""Protocol enums describe device URL schemes used by Overkiz.',
+ "",
+ "THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.",
+ "Run `uv run utils/generate_enums.py` to regenerate.",
+ '"""',
+ "",
+ "from enum import StrEnum, unique",
+ "",
+ "from pyoverkiz.enums.base import UnknownEnumMixin",
+ "",
+ "",
+ "@unique",
+ "class Protocol(UnknownEnumMixin, StrEnum):",
+ ' """Protocol used by Overkiz.',
+ "",
+ " Values have been retrieved from /reference/protocolTypes",
+ ' """',
+ "",
+ ' UNKNOWN = "unknown"',
+ "",
+ ]
+
+ # Add each protocol as an enum value with label comment
+ for name, prefix, protocol_id, label in protocols:
+ if protocol_id is not None:
+ lines.append(f' {name} = "{prefix}" # {protocol_id}: {label}')
+ else:
+ lines.append(f' {name} = "{prefix}"')
+
+ lines.append("") # End with newline
+
+ # Write to the protocol.py file
+ output_path = (
+ Path(__file__).parent.parent / "pyoverkiz" / "enums" / "protocol.py"
+ )
+ output_path.write_text("\n".join(lines))
+
+ fetched_count = len(protocol_types)
+ additional_count = len(
+ [p for p in ADDITIONAL_PROTOCOLS if p[1] not in fetched_prefixes]
+ )
+
+ print(f"✓ Generated {output_path}")
+ print(f"✓ Added {fetched_count} protocols from API")
+ print(f"✓ Added {additional_count} additional hardcoded protocols")
+ print(f"✓ Total: {len(protocols)} protocols")
+
+
+async def generate_ui_enums() -> None:
+ """Generate the UIClass and UIWidget enums from the Overkiz API."""
+ username = os.environ["OVERKIZ_USERNAME"]
+ password = os.environ["OVERKIZ_PASSWORD"]
+
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials(username, password),
+ ) as client:
+ await client.login()
+
+ ui_classes = cast(list[str], await client.get_reference_ui_classes())
+ ui_widgets = cast(list[str], await client.get_reference_ui_widgets())
+
+ # Convert camelCase to SCREAMING_SNAKE_CASE for enum names
+ def to_enum_name(value: str) -> str:
+ # Handle special cases first
+ name = value.replace("ZWave", "ZWAVE_")
+ name = name.replace("OTherm", "OTHERM_")
+
+ # Insert underscore before uppercase letters
+ name = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", name)
+ name = re.sub(r"([a-z\d])([A-Z])", r"\1_\2", name)
+
+ # Fix specific cases after general conversion
+ name = name.replace("APCDHW", "APC_DHW")
+
+ # Clean up any double underscores and trailing underscores
+ name = re.sub(r"__+", "_", name)
+ name = name.rstrip("_")
+
+ return name.upper()
+
+ # Generate the enum file content
+ lines = [
+ '"""UI enums for classes and widgets used to interpret device UI metadata.',
+ "",
+ "THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.",
+ "Run `uv run utils/generate_enums.py` to regenerate.",
+ '"""',
+ "",
+ "# ruff: noqa: S105",
+ '# Enum values contain "PASS" in API names (e.g. PassAPC), not passwords',
+ "",
+ "from enum import StrEnum, unique",
+ "",
+ "from pyoverkiz.enums.base import UnknownEnumMixin",
+ "",
+ "",
+ "@unique",
+ "class UIClass(UnknownEnumMixin, StrEnum):",
+ ' """Enumeration of UI classes used to describe device categories and behaviors."""',
+ "",
+ ' UNKNOWN = "Unknown"',
+ "",
+ ]
+
+ # Add UI classes
+ sorted_classes = sorted(ui_classes)
+ for ui_class in sorted_classes:
+ enum_name = to_enum_name(ui_class)
+ lines.append(f' {enum_name} = "{ui_class}"')
+
+ lines.append("")
+ lines.append("")
+ lines.append("@unique")
+ lines.append("class UIWidget(UnknownEnumMixin, StrEnum):")
+ lines.append(
+ ' """Enumeration of UI widgets used by Overkiz for device presentation."""'
+ )
+ lines.append("")
+ lines.append(' UNKNOWN = "Unknown"')
+ lines.append("")
+
+ # Add UI widgets
+ sorted_widgets = sorted(ui_widgets)
+
+ # Add hardcoded widgets that may not be on all servers (avoid duplicates)
+ fetched_widget_values = set(ui_widgets)
+ for _enum_name, widget_value in ADDITIONAL_WIDGETS:
+ if widget_value not in fetched_widget_values:
+ sorted_widgets.append(widget_value)
+
+ sorted_widgets = sorted(sorted_widgets)
+
+ for ui_widget in sorted_widgets:
+ enum_name = to_enum_name(ui_widget)
+ lines.append(f' {enum_name} = "{ui_widget}"')
+
+ lines.append("") # End with newline
+
+ # Fetch and add UI classifiers
+ ui_classifiers = cast(list[str], await client.get_reference_ui_classifiers())
+
+ lines.append("")
+ lines.append("@unique")
+ lines.append("class UIClassifier(UnknownEnumMixin, StrEnum):")
+ lines.append(
+ ' """Enumeration of UI classifiers used to categorize device types."""'
+ )
+ lines.append("")
+ lines.append(' UNKNOWN = "unknown"')
+ lines.append("")
+
+ # Add UI classifiers
+ sorted_classifiers = sorted(ui_classifiers)
+ for ui_classifier in sorted_classifiers:
+ enum_name = to_enum_name(ui_classifier)
+ lines.append(f' {enum_name} = "{ui_classifier}"')
+
+ lines.append("") # End with newline
+
+ # Write to the ui.py file
+ output_path = Path(__file__).parent.parent / "pyoverkiz" / "enums" / "ui.py"
+ output_path.write_text("\n".join(lines))
+
+ additional_widget_count = len(
+ [w for w in ADDITIONAL_WIDGETS if w[1] not in fetched_widget_values]
+ )
+
+ print(f"✓ Generated {output_path}")
+ print(f"✓ Added {len(ui_classes)} UI classes")
+ print(f"✓ Added {len(ui_widgets)} UI widgets from API")
+ print(f"✓ Added {additional_widget_count} additional hardcoded UI widgets")
+ print(f"✓ Total: {len(sorted_widgets)} UI widgets")
+ print(f"✓ Added {len(sorted_classifiers)} UI classifiers")
+
+
+async def generate_ui_profiles() -> None:
+ """Generate the UIProfile enum from the Overkiz API."""
+ username = os.environ["OVERKIZ_USERNAME"]
+ password = os.environ["OVERKIZ_PASSWORD"]
+
+ async with OverkizClient(
+ server=Server.SOMFY_EUROPE,
+ credentials=UsernamePasswordCredentials(username, password),
+ ) as client:
+ await client.login()
+
+ ui_profile_names = await client.get_reference_ui_profile_names()
+
+ # Fetch details for all profiles
+ profiles_with_details: list[tuple[str, UIProfileDefinition | None]] = []
+
+ for profile_name in ui_profile_names:
+ print(f"Fetching {profile_name}...")
+ try:
+ details = await client.get_reference_ui_profile(profile_name)
+ profiles_with_details.append((profile_name, details))
+ except OverkizException:
+ print(f" ! Could not fetch details for {profile_name}")
+ profiles_with_details.append((profile_name, None))
+
+ # Convert camelCase to SCREAMING_SNAKE_CASE for enum names
+ def to_enum_name(value: str) -> str:
+ # Insert underscore before uppercase letters
+ name = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", value)
+ name = re.sub(r"([a-z\d])([A-Z])", r"\1_\2", name)
+
+ # Clean up any double underscores
+ name = re.sub(r"__+", "_", name)
+
+ return name.upper()
+
+ def format_value_prototype(vp: ValuePrototype) -> str:
+ """Format a value prototype into a readable string."""
+ type_str = vp.type.lower()
+ parts = [type_str]
+
+ if vp.min_value is not None and vp.max_value is not None:
+ parts.append(f"{vp.min_value}-{vp.max_value}")
+ elif vp.min_value is not None:
+ parts.append(f">= {vp.min_value}")
+ elif vp.max_value is not None:
+ parts.append(f"<= {vp.max_value}")
+
+ if vp.enum_values:
+ enum_vals = ", ".join(f"'{v}'" for v in vp.enum_values)
+ parts.append(f"values: {enum_vals}")
+
+ return " ".join(parts)
+
+ def clean_description(desc: str) -> str:
+ """Clean description text to fit in a single-line comment."""
+ # Remove newlines and excessive whitespace
+ cleaned = " ".join(desc.split())
+ return cleaned.strip()
+
+ # Generate the enum file content
+ lines = [
+ '"""UI Profile enums describe device capabilities through commands and states.',
+ "",
+ "THIS FILE IS AUTO-GENERATED. DO NOT EDIT MANUALLY.",
+ "Run `uv run utils/generate_enums.py` to regenerate.",
+ '"""',
+ "",
+ "from enum import StrEnum, unique",
+ "",
+ "from pyoverkiz.enums.base import UnknownEnumMixin",
+ "",
+ "",
+ "@unique",
+ "class UIProfile(UnknownEnumMixin, StrEnum):",
+ ' """',
+ " UI Profiles define device capabilities through commands and states.",
+ " ",
+ " Each profile describes what a device can do (commands) and what information",
+ " it provides (states). Form factor indicates if the profile is tied to a",
+ " specific physical device type.",
+ ' """',
+ "",
+ ' UNKNOWN = "Unknown"',
+ "",
+ ]
+
+ # Sort profiles by name for consistent output
+ profiles_with_details.sort(key=lambda p: p[0])
+
+ # Add each profile with detailed comments
+ for profile_name, details_obj in profiles_with_details:
+ enum_name = to_enum_name(profile_name)
+
+ if details_obj is None:
+ # No details available
+ lines.append(f" # {profile_name} (details unavailable)")
+ lines.append(f' {enum_name} = "{profile_name}"')
+ lines.append("")
+ continue
+
+ # Build multi-line comment
+ comment_lines = []
+
+ # Add commands if present
+ if details_obj.commands:
+ comment_lines.append("Commands:")
+ for cmd in details_obj.commands:
+ cmd_name = cmd.name
+ desc = clean_description(cmd.description or "")
+
+ # Get parameter info
+ if cmd.prototype and cmd.prototype.parameters:
+ param_strs = []
+ for param in cmd.prototype.parameters:
+ if param.value_prototypes:
+ param_strs.append(
+ format_value_prototype(param.value_prototypes[0])
+ )
+ param_info = (
+ f"({', '.join(param_strs)})" if param_strs else "()"
+ )
+ else:
+ param_info = "()"
+
+ if desc:
+ comment_lines.append(f" - {cmd_name}{param_info}: {desc}")
+ else:
+ comment_lines.append(f" - {cmd_name}{param_info}")
+
+ # Add states if present
+ if details_obj.states:
+ if comment_lines:
+ comment_lines.append("")
+ comment_lines.append("States:")
+ for state in details_obj.states:
+ state_name = state.name
+ desc = clean_description(state.description or "")
+
+ # Get value prototype info
+ if state.prototype and state.prototype.value_prototypes:
+ type_info = f" ({format_value_prototype(state.prototype.value_prototypes[0])})"
+ else:
+ type_info = ""
+
+ if desc:
+ comment_lines.append(f" - {state_name}{type_info}: {desc}")
+ else:
+ comment_lines.append(f" - {state_name}{type_info}")
+
+ # Add form factor info
+ if details_obj.form_factor:
+ if comment_lines:
+ comment_lines.append("")
+ comment_lines.append("Form factor specific: Yes")
+
+ # If we have any details, add the comment block
+ if comment_lines:
+ lines.append(" #")
+ lines.append(f" # {profile_name}")
+ lines.append(" #")
+ for comment_line in comment_lines:
+ if comment_line:
+ lines.append(f" # {comment_line}")
+ else:
+ lines.append(" #")
+ else:
+ # Simple single-line comment
+ lines.append(f" # {profile_name}")
+
+ lines.append(f' {enum_name} = "{profile_name}"')
+ lines.append("")
+
+ # Write to the ui_profile.py file
+ output_path = (
+ Path(__file__).parent.parent / "pyoverkiz" / "enums" / "ui_profile.py"
+ )
+ output_path.write_text("\n".join(lines))
+
+ print(f"\n✓ Generated {output_path}")
+ print(f"✓ Added {len(profiles_with_details)} UI profiles")
+ print(
+ f"✓ Profiles with details: {sum(1 for _, d in profiles_with_details if d is not None)}"
+ )
+ print(
+ f"✓ Profiles without details: {sum(1 for _, d in profiles_with_details if d is None)}"
+ )
+
+
+def extract_commands_from_fixtures(fixtures_dir: Path) -> set[str]:
+ """Extract all commands from fixture files in the given directory.
+
+ Reads all JSON fixture files and collects unique command names from device
+ definitions. Commands are returned as camelCase values.
+ """
+ import json
+
+ commands: set[str] = set()
+
+ for fixture_file in fixtures_dir.glob("*.json"):
+ try:
+ data = json.loads(fixture_file.read_text())
+ if "devices" not in data:
+ continue
+
+ for device in data["devices"]:
+ if "definition" not in device:
+ continue
+
+ definition = device["definition"]
+ if "commands" not in definition:
+ continue
+
+ for command in definition["commands"]:
+ if "commandName" in command:
+ commands.add(command["commandName"])
+ except (json.JSONDecodeError, KeyError, TypeError):
+ # Skip files that can't be parsed or have unexpected structure
+ continue
+
+ return commands
+
+
+def extract_state_values_from_fixtures(fixtures_dir: Path) -> set[str]:
+ """Extract all state values from fixture files in the given directory.
+
+ Reads all JSON fixture files and collects unique state values from device
+ definitions. Values are extracted from DiscreteState types.
+ """
+ import json
+
+ values: set[str] = set()
+
+ for fixture_file in fixtures_dir.glob("*.json"):
+ try:
+ data = json.loads(fixture_file.read_text())
+ if "devices" not in data:
+ continue
+
+ for device in data["devices"]:
+ if "definition" not in device:
+ continue
+
+ definition = device["definition"]
+ if "states" not in definition:
+ continue
+
+ for state in definition["states"]:
+ # Extract values from DiscreteState
+ if state.get("type") == "DiscreteState" and "values" in state:
+ for value in state["values"]:
+ if isinstance(value, str):
+ values.add(value)
+ except (json.JSONDecodeError, KeyError, TypeError):
+ # Skip files that can't be parsed or have unexpected structure
+ continue
+
+ return values
+
+
+def command_to_enum_name(command_name: str) -> str:
+ """Convert a command name (camelCase) to an ENUM_NAME (SCREAMING_SNAKE_CASE).
+
+ Example: "setTargetTemperature" -> "SET_TARGET_TEMPERATURE"
+ Spaces are converted to underscores: "long peak" -> "LONG_PEAK"
+ """
+ # First, replace spaces with underscores
+ name = command_name.replace(" ", "_")
+ # Insert underscore before uppercase letters
+ name = re.sub(r"([a-z\d])([A-Z])", r"\1_\2", name)
+ return name.upper()
+
+
+async def generate_command_enums() -> None:
+ """Generate the OverkizCommand enum and update OverkizCommandParam from fixture files."""
+ fixtures_dir = Path(__file__).parent.parent / "tests" / "fixtures" / "setup"
+
+ # Extract commands and state values from fixtures
+ fixture_commands = extract_commands_from_fixtures(fixtures_dir)
+ fixture_state_values = extract_state_values_from_fixtures(fixtures_dir)
+
+ # Read existing commands from the command.py file
+ command_file = Path(__file__).parent.parent / "pyoverkiz" / "enums" / "command.py"
+ content = command_file.read_text()
+
+ # Find the OverkizCommandParam class
+ param_class_start_idx = content.find("@unique\nclass OverkizCommandParam")
+ command_mode_class_start_idx = content.find("@unique\nclass CommandMode")
+
+ # Parse existing commands from OverkizCommand
+ existing_commands: dict[str, str] = {}
+ in_overkiz_command = False
+ lines_before_param = content[:param_class_start_idx].split("\n")
+
+ for line in lines_before_param:
+ if "class OverkizCommand" in line:
+ in_overkiz_command = True
+ continue
+ if in_overkiz_command and line.strip() and not line.startswith(" "):
+ break
+ if in_overkiz_command and " = " in line and not line.strip().startswith("#"):
+ parts = line.strip().split(" = ")
+ if len(parts) == 2:
+ enum_name = parts[0].strip()
+ value_part = parts[1].split("#")[0].strip()
+ if value_part.startswith('"') and value_part.endswith('"'):
+ command_value = value_part[1:-1]
+ existing_commands[command_value] = enum_name
+
+ # Parse existing parameters from OverkizCommandParam
+ existing_params: dict[str, str] = {}
+ in_param_class = False
+ lines_param_section = content[
+ param_class_start_idx:command_mode_class_start_idx
+ ].split("\n")
+
+ for line in lines_param_section:
+ if "class OverkizCommandParam" in line:
+ in_param_class = True
+ continue
+ if in_param_class and line.strip() and not line.startswith(" "):
+ break
+ if in_param_class and " = " in line and not line.strip().startswith("#"):
+ parts = line.strip().split(" = ")
+ if len(parts) == 2:
+ enum_name = parts[0].strip()
+ value_part = parts[1].split("#")[0].strip()
+ if value_part.startswith('"') and value_part.endswith('"'):
+ param_value = value_part[1:-1]
+ existing_params[param_value] = enum_name
+
+ # Merge: keep existing commands and add new ones from fixtures
+ all_command_values = set(existing_commands.keys()) | fixture_commands
+
+ # Convert to list of tuples for commands: (enum_name, command_value)
+ # Track enum names to detect duplicates
+ command_enum_names: set[str] = set()
+ command_tuples: list[tuple[str, str]] = []
+ for cmd_value in sorted(all_command_values):
+ if cmd_value in existing_commands:
+ enum_name = existing_commands[cmd_value]
+ else:
+ enum_name = command_to_enum_name(cmd_value)
+
+ # Skip if this enum_name already exists (avoid duplicates)
+ if enum_name not in command_enum_names:
+ command_tuples.append((enum_name, cmd_value))
+ command_enum_names.add(enum_name)
+
+ # Sort alphabetically by enum name
+ command_tuples.sort(key=lambda x: x[0])
+
+ # Merge: keep existing params and add new ones from fixture state values
+ all_param_values = set(existing_params.keys()) | fixture_state_values
+
+ # Convert to list of tuples for params: (enum_name, param_value)
+ # Track enum names to detect duplicates
+ param_enum_names: set[str] = set()
+ param_tuples: list[tuple[str, str]] = []
+ for param_value in sorted(all_param_values):
+ if param_value in existing_params:
+ enum_name = existing_params[param_value]
+ else:
+ enum_name = command_to_enum_name(param_value)
+
+ # Skip if this enum_name already exists (avoid duplicates)
+ if enum_name not in param_enum_names:
+ param_tuples.append((enum_name, param_value))
+ param_enum_names.add(enum_name)
+
+ # Sort alphabetically by enum name
+ param_tuples.sort(key=lambda x: x[0])
+
+ # Sort alphabetically by enum name
+ param_tuples.sort(key=lambda x: x[0])
+
+ # Generate the enum file content
+ lines = [
+ '"""Command-related enums and parameters used by device commands."""',
+ "",
+ "# ruff: noqa: S105",
+ '# Enum values contain "PASS" in API names (e.g. PassAPC), not passwords',
+ "",
+ "from enum import StrEnum, unique",
+ "",
+ "",
+ "@unique",
+ "class OverkizCommand(StrEnum):",
+ ' """Device commands used by Overkiz."""',
+ "",
+ ]
+
+ # Add each command
+ for enum_name, cmd_value in command_tuples:
+ if " " in cmd_value:
+ lines.append(f' {enum_name} = "{cmd_value}" # value with space')
+ else:
+ lines.append(f' {enum_name} = "{cmd_value}"')
+
+ lines.append("")
+ lines.append("")
+ lines.append("@unique")
+ lines.append("class OverkizCommandParam(StrEnum):")
+ lines.append(' """Parameter used by Overkiz commands and/or states."""')
+ lines.append("")
+
+ # Add each param
+ for enum_name, param_value in param_tuples:
+ if " " in param_value:
+ lines.append(f' {enum_name} = "{param_value}" # value with space')
+ else:
+ lines.append(f' {enum_name} = "{param_value}"')
+
+ lines.append("")
+ lines.append("")
+
+ # Append CommandMode class
+ command_mode_start = content.find("@unique\nclass CommandMode")
+ if command_mode_start != -1:
+ lines.append(content[command_mode_start:].rstrip())
+ lines.append("")
+
+ # Write to the command.py file
+ command_file.write_text("\n".join(lines))
+
+ print(f"✓ Generated {command_file}")
+ print(f"✓ Added {len(existing_commands)} existing commands")
+ print(f"✓ Found {len(fixture_commands)} total commands in fixtures")
+ new_commands_count = len(fixture_commands - set(existing_commands.keys()))
+ print(f"✓ Added {new_commands_count} new commands from fixtures")
+ print(f"✓ Total: {len(all_command_values)} commands")
+ print()
+ print(f"✓ Added {len(existing_params)} existing parameters")
+ print(f"✓ Found {len(fixture_state_values)} total state values in fixtures")
+ new_params_count = len(fixture_state_values - set(existing_params.keys()))
+ print(f"✓ Added {new_params_count} new parameters from fixtures")
+ print(f"✓ Total: {len(all_param_values)} parameters")
+
+
+async def generate_all() -> None:
+ """Generate all enums from the Overkiz API."""
+ await generate_protocol_enum()
+ print()
+ await generate_ui_enums()
+ print()
+ await generate_ui_profiles()
+ print()
+ await generate_command_enums()
+
+
+if __name__ == "__main__":
+ asyncio.run(generate_all())
diff --git a/uv.lock b/uv.lock
index fc70a0fe..1c832b1a 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,6 +1,6 @@
version = 1
revision = 3
-requires-python = ">=3.10, <4.0"
+requires-python = ">=3.12, <4.0"
[[package]]
name = "aiohappyeyeballs"
@@ -18,7 +18,6 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohappyeyeballs" },
{ name = "aiosignal" },
- { name = "async-timeout", marker = "python_full_version < '3.11'" },
{ name = "attrs" },
{ name = "frozenlist" },
{ name = "multidict" },
@@ -27,40 +26,6 @@ dependencies = [
]
sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" },
- { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" },
- { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" },
- { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" },
- { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" },
- { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" },
- { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" },
- { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" },
- { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" },
- { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" },
- { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" },
- { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" },
- { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" },
- { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" },
- { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" },
- { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" },
- { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" },
- { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" },
- { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" },
- { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" },
- { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" },
- { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" },
- { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" },
- { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" },
- { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" },
- { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" },
- { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" },
- { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" },
- { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" },
- { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" },
- { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" },
- { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" },
- { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" },
- { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" },
{ url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" },
{ url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" },
{ url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" },
@@ -144,15 +109,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
]
-[[package]]
-name = "async-timeout"
-version = "5.0.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" },
-]
-
[[package]]
name = "attrs"
version = "25.4.0"
@@ -163,30 +119,35 @@ wheels = [
]
[[package]]
-name = "backoff"
-version = "2.2.1"
+name = "babel"
+version = "2.17.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" },
]
[[package]]
-name = "backports-asyncio-runner"
-version = "1.2.0"
+name = "backoff"
+version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
+ { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" },
]
[[package]]
-name = "backports-strenum"
-version = "1.3.1"
+name = "backrefs"
+version = "6.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/35/c7/2ed54c32fed313591ffb21edbd48db71e68827d43a61938e5a0bc2b6ec91/backports_strenum-1.3.1.tar.gz", hash = "sha256:77c52407342898497714f0596e86188bb7084f89063226f4ba66863482f42414", size = 7257, upload-time = "2023-12-09T14:36:40.937Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/86/e3/bb3a439d5cb255c4774724810ad8073830fac9c9dee123555820c1bcc806/backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231", size = 7011962, upload-time = "2025-11-15T14:52:08.323Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d6/50/56cf20e2ee5127b603b81d5a69580a1a325083e2b921aa8f067da83927c0/backports_strenum-1.3.1-py3-none-any.whl", hash = "sha256:cdcfe36dc897e2615dc793b7d3097f54d359918fc448754a517e6f23044ccf83", size = 8304, upload-time = "2023-12-09T14:36:39.905Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/ee/c216d52f58ea75b5e1841022bbae24438b19834a29b163cb32aa3a2a7c6e/backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1", size = 381059, upload-time = "2025-11-15T14:51:59.758Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7", size = 392854, upload-time = "2025-11-15T14:52:01.194Z" },
+ { url = "https://files.pythonhosted.org/packages/37/c9/fd117a6f9300c62bbc33bc337fd2b3c6bfe28b6e9701de336b52d7a797ad/backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a", size = 398770, upload-time = "2025-11-15T14:52:02.584Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-none-any.whl", hash = "sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05", size = 400726, upload-time = "2025-11-15T14:52:04.093Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/72/6296bad135bfafd3254ae3648cd152980a424bd6fed64a101af00cc7ba31/backrefs-6.1-py314-none-any.whl", hash = "sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853", size = 412584, upload-time = "2025-11-15T14:52:05.233Z" },
+ { url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058, upload-time = "2025-11-15T14:52:06.698Z" },
]
[[package]]
@@ -219,81 +180,80 @@ wheels = [
[[package]]
name = "certifi"
-version = "2025.6.15"
+version = "2026.1.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
]
[[package]]
-name = "cfgv"
-version = "3.4.0"
+name = "charset-normalizer"
+version = "3.4.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
+ { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
+ { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
+ { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
+ { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
+ { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
]
[[package]]
-name = "charset-normalizer"
-version = "3.4.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" },
- { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" },
- { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" },
- { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" },
- { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" },
- { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" },
- { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" },
- { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" },
- { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" },
- { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" },
- { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" },
- { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" },
- { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" },
- { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" },
- { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" },
- { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" },
- { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" },
- { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" },
- { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" },
- { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" },
- { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" },
- { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" },
- { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" },
- { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" },
- { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" },
- { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" },
- { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" },
- { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" },
- { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" },
- { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" },
- { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" },
- { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" },
- { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" },
- { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" },
- { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" },
- { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" },
- { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" },
- { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" },
- { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" },
- { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" },
- { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" },
- { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" },
- { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" },
- { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" },
- { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" },
- { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" },
- { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" },
- { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" },
- { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" },
- { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" },
- { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" },
- { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" },
- { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" },
+name = "click"
+version = "8.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
]
[[package]]
@@ -307,115 +267,76 @@ wheels = [
[[package]]
name = "coverage"
-version = "7.13.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b6/45/2c665ca77ec32ad67e25c77daf1cee28ee4558f3bc571cdbaf88a00b9f23/coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936", size = 820905, upload-time = "2025-12-08T13:14:38.055Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/db/08/bdd7ccca14096f7eb01412b87ac11e5d16e4cb54b6e328afc9dee8bdaec1/coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070", size = 217979, upload-time = "2025-12-08T13:12:14.505Z" },
- { url = "https://files.pythonhosted.org/packages/fa/f0/d1302e3416298a28b5663ae1117546a745d9d19fde7e28402b2c5c3e2109/coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98", size = 218496, upload-time = "2025-12-08T13:12:16.237Z" },
- { url = "https://files.pythonhosted.org/packages/07/26/d36c354c8b2a320819afcea6bffe72839efd004b98d1d166b90801d49d57/coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5", size = 245237, upload-time = "2025-12-08T13:12:17.858Z" },
- { url = "https://files.pythonhosted.org/packages/91/52/be5e85631e0eec547873d8b08dd67a5f6b111ecfe89a86e40b89b0c1c61c/coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e", size = 247061, upload-time = "2025-12-08T13:12:19.132Z" },
- { url = "https://files.pythonhosted.org/packages/0f/45/a5e8fa0caf05fbd8fa0402470377bff09cc1f026d21c05c71e01295e55ab/coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33", size = 248928, upload-time = "2025-12-08T13:12:20.702Z" },
- { url = "https://files.pythonhosted.org/packages/f5/42/ffb5069b6fd1b95fae482e02f3fecf380d437dd5a39bae09f16d2e2e7e01/coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791", size = 245931, upload-time = "2025-12-08T13:12:22.243Z" },
- { url = "https://files.pythonhosted.org/packages/95/6e/73e809b882c2858f13e55c0c36e94e09ce07e6165d5644588f9517efe333/coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032", size = 246968, upload-time = "2025-12-08T13:12:23.52Z" },
- { url = "https://files.pythonhosted.org/packages/87/08/64ebd9e64b6adb8b4a4662133d706fbaccecab972e0b3ccc23f64e2678ad/coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9", size = 244972, upload-time = "2025-12-08T13:12:24.781Z" },
- { url = "https://files.pythonhosted.org/packages/12/97/f4d27c6fe0cb375a5eced4aabcaef22de74766fb80a3d5d2015139e54b22/coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f", size = 245241, upload-time = "2025-12-08T13:12:28.041Z" },
- { url = "https://files.pythonhosted.org/packages/0c/94/42f8ae7f633bf4c118bf1038d80472f9dade88961a466f290b81250f7ab7/coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8", size = 245847, upload-time = "2025-12-08T13:12:29.337Z" },
- { url = "https://files.pythonhosted.org/packages/a8/2f/6369ca22b6b6d933f4f4d27765d313d8914cc4cce84f82a16436b1a233db/coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f", size = 220573, upload-time = "2025-12-08T13:12:30.905Z" },
- { url = "https://files.pythonhosted.org/packages/f1/dc/a6a741e519acceaeccc70a7f4cfe5d030efc4b222595f0677e101af6f1f3/coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303", size = 221509, upload-time = "2025-12-08T13:12:32.09Z" },
- { url = "https://files.pythonhosted.org/packages/f1/dc/888bf90d8b1c3d0b4020a40e52b9f80957d75785931ec66c7dfaccc11c7d/coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820", size = 218104, upload-time = "2025-12-08T13:12:33.333Z" },
- { url = "https://files.pythonhosted.org/packages/8d/ea/069d51372ad9c380214e86717e40d1a743713a2af191cfba30a0911b0a4a/coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f", size = 218606, upload-time = "2025-12-08T13:12:34.498Z" },
- { url = "https://files.pythonhosted.org/packages/68/09/77b1c3a66c2aa91141b6c4471af98e5b1ed9b9e6d17255da5eb7992299e3/coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96", size = 248999, upload-time = "2025-12-08T13:12:36.02Z" },
- { url = "https://files.pythonhosted.org/packages/0a/32/2e2f96e9d5691eaf1181d9040f850b8b7ce165ea10810fd8e2afa534cef7/coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259", size = 250925, upload-time = "2025-12-08T13:12:37.221Z" },
- { url = "https://files.pythonhosted.org/packages/7b/45/b88ddac1d7978859b9a39a8a50ab323186148f1d64bc068f86fc77706321/coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb", size = 253032, upload-time = "2025-12-08T13:12:38.763Z" },
- { url = "https://files.pythonhosted.org/packages/71/cb/e15513f94c69d4820a34b6bf3d2b1f9f8755fa6021be97c7065442d7d653/coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9", size = 249134, upload-time = "2025-12-08T13:12:40.382Z" },
- { url = "https://files.pythonhosted.org/packages/09/61/d960ff7dc9e902af3310ce632a875aaa7860f36d2bc8fc8b37ee7c1b82a5/coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030", size = 250731, upload-time = "2025-12-08T13:12:41.992Z" },
- { url = "https://files.pythonhosted.org/packages/98/34/c7c72821794afc7c7c2da1db8f00c2c98353078aa7fb6b5ff36aac834b52/coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833", size = 248795, upload-time = "2025-12-08T13:12:43.331Z" },
- { url = "https://files.pythonhosted.org/packages/0a/5b/e0f07107987a43b2def9aa041c614ddb38064cbf294a71ef8c67d43a0cdd/coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8", size = 248514, upload-time = "2025-12-08T13:12:44.546Z" },
- { url = "https://files.pythonhosted.org/packages/71/c2/c949c5d3b5e9fc6dd79e1b73cdb86a59ef14f3709b1d72bf7668ae12e000/coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753", size = 249424, upload-time = "2025-12-08T13:12:45.759Z" },
- { url = "https://files.pythonhosted.org/packages/11/f1/bbc009abd6537cec0dffb2cc08c17a7f03de74c970e6302db4342a6e05af/coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b", size = 220597, upload-time = "2025-12-08T13:12:47.378Z" },
- { url = "https://files.pythonhosted.org/packages/c4/f6/d9977f2fb51c10fbaed0718ce3d0a8541185290b981f73b1d27276c12d91/coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe", size = 221536, upload-time = "2025-12-08T13:12:48.7Z" },
- { url = "https://files.pythonhosted.org/packages/be/ad/3fcf43fd96fb43e337a3073dea63ff148dcc5c41ba7a14d4c7d34efb2216/coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7", size = 220206, upload-time = "2025-12-08T13:12:50.365Z" },
- { url = "https://files.pythonhosted.org/packages/9b/f1/2619559f17f31ba00fc40908efd1fbf1d0a5536eb75dc8341e7d660a08de/coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf", size = 218274, upload-time = "2025-12-08T13:12:52.095Z" },
- { url = "https://files.pythonhosted.org/packages/2b/11/30d71ae5d6e949ff93b2a79a2c1b4822e00423116c5c6edfaeef37301396/coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f", size = 218638, upload-time = "2025-12-08T13:12:53.418Z" },
- { url = "https://files.pythonhosted.org/packages/79/c2/fce80fc6ded8d77e53207489d6065d0fed75db8951457f9213776615e0f5/coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb", size = 250129, upload-time = "2025-12-08T13:12:54.744Z" },
- { url = "https://files.pythonhosted.org/packages/5b/b6/51b5d1eb6fcbb9a1d5d6984e26cbe09018475c2922d554fd724dd0f056ee/coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621", size = 252885, upload-time = "2025-12-08T13:12:56.401Z" },
- { url = "https://files.pythonhosted.org/packages/0d/f8/972a5affea41de798691ab15d023d3530f9f56a72e12e243f35031846ff7/coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74", size = 253974, upload-time = "2025-12-08T13:12:57.718Z" },
- { url = "https://files.pythonhosted.org/packages/8a/56/116513aee860b2c7968aa3506b0f59b22a959261d1dbf3aea7b4450a7520/coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57", size = 250538, upload-time = "2025-12-08T13:12:59.254Z" },
- { url = "https://files.pythonhosted.org/packages/d6/75/074476d64248fbadf16dfafbf93fdcede389ec821f74ca858d7c87d2a98c/coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8", size = 251912, upload-time = "2025-12-08T13:13:00.604Z" },
- { url = "https://files.pythonhosted.org/packages/f2/d2/aa4f8acd1f7c06024705c12609d8698c51b27e4d635d717cd1934c9668e2/coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d", size = 250054, upload-time = "2025-12-08T13:13:01.892Z" },
- { url = "https://files.pythonhosted.org/packages/19/98/8df9e1af6a493b03694a1e8070e024e7d2cdc77adedc225a35e616d505de/coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b", size = 249619, upload-time = "2025-12-08T13:13:03.236Z" },
- { url = "https://files.pythonhosted.org/packages/d8/71/f8679231f3353018ca66ef647fa6fe7b77e6bff7845be54ab84f86233363/coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd", size = 251496, upload-time = "2025-12-08T13:13:04.511Z" },
- { url = "https://files.pythonhosted.org/packages/04/86/9cb406388034eaf3c606c22094edbbb82eea1fa9d20c0e9efadff20d0733/coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef", size = 220808, upload-time = "2025-12-08T13:13:06.422Z" },
- { url = "https://files.pythonhosted.org/packages/1c/59/af483673df6455795daf5f447c2f81a3d2fcfc893a22b8ace983791f6f34/coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae", size = 221616, upload-time = "2025-12-08T13:13:07.95Z" },
- { url = "https://files.pythonhosted.org/packages/64/b0/959d582572b30a6830398c60dd419c1965ca4b5fb38ac6b7093a0d50ca8d/coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080", size = 220261, upload-time = "2025-12-08T13:13:09.581Z" },
- { url = "https://files.pythonhosted.org/packages/7c/cc/bce226595eb3bf7d13ccffe154c3c487a22222d87ff018525ab4dd2e9542/coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf", size = 218297, upload-time = "2025-12-08T13:13:10.977Z" },
- { url = "https://files.pythonhosted.org/packages/3b/9f/73c4d34600aae03447dff3d7ad1d0ac649856bfb87d1ca7d681cfc913f9e/coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a", size = 218673, upload-time = "2025-12-08T13:13:12.562Z" },
- { url = "https://files.pythonhosted.org/packages/63/ab/8fa097db361a1e8586535ae5073559e6229596b3489ec3ef2f5b38df8cb2/coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74", size = 249652, upload-time = "2025-12-08T13:13:13.909Z" },
- { url = "https://files.pythonhosted.org/packages/90/3a/9bfd4de2ff191feb37ef9465855ca56a6f2f30a3bca172e474130731ac3d/coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6", size = 252251, upload-time = "2025-12-08T13:13:15.553Z" },
- { url = "https://files.pythonhosted.org/packages/df/61/b5d8105f016e1b5874af0d7c67542da780ccd4a5f2244a433d3e20ceb1ad/coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b", size = 253492, upload-time = "2025-12-08T13:13:16.849Z" },
- { url = "https://files.pythonhosted.org/packages/f3/b8/0fad449981803cc47a4694768b99823fb23632150743f9c83af329bb6090/coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232", size = 249850, upload-time = "2025-12-08T13:13:18.142Z" },
- { url = "https://files.pythonhosted.org/packages/9a/e9/8d68337c3125014d918cf4327d5257553a710a2995a6a6de2ac77e5aa429/coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971", size = 251633, upload-time = "2025-12-08T13:13:19.56Z" },
- { url = "https://files.pythonhosted.org/packages/55/14/d4112ab26b3a1bc4b3c1295d8452dcf399ed25be4cf649002fb3e64b2d93/coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d", size = 249586, upload-time = "2025-12-08T13:13:20.883Z" },
- { url = "https://files.pythonhosted.org/packages/2c/a9/22b0000186db663b0d82f86c2f1028099ae9ac202491685051e2a11a5218/coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137", size = 249412, upload-time = "2025-12-08T13:13:22.22Z" },
- { url = "https://files.pythonhosted.org/packages/a1/2e/42d8e0d9e7527fba439acdc6ed24a2b97613b1dc85849b1dd935c2cffef0/coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511", size = 251191, upload-time = "2025-12-08T13:13:23.899Z" },
- { url = "https://files.pythonhosted.org/packages/a4/af/8c7af92b1377fd8860536aadd58745119252aaaa71a5213e5a8e8007a9f5/coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1", size = 220829, upload-time = "2025-12-08T13:13:25.182Z" },
- { url = "https://files.pythonhosted.org/packages/58/f9/725e8bf16f343d33cbe076c75dc8370262e194ff10072c0608b8e5cf33a3/coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a", size = 221640, upload-time = "2025-12-08T13:13:26.836Z" },
- { url = "https://files.pythonhosted.org/packages/8a/ff/e98311000aa6933cc79274e2b6b94a2fe0fe3434fca778eba82003675496/coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6", size = 220269, upload-time = "2025-12-08T13:13:28.116Z" },
- { url = "https://files.pythonhosted.org/packages/cf/cf/bbaa2e1275b300343ea865f7d424cc0a2e2a1df6925a070b2b2d5d765330/coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a", size = 218990, upload-time = "2025-12-08T13:13:29.463Z" },
- { url = "https://files.pythonhosted.org/packages/21/1d/82f0b3323b3d149d7672e7744c116e9c170f4957e0c42572f0366dbb4477/coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8", size = 219340, upload-time = "2025-12-08T13:13:31.524Z" },
- { url = "https://files.pythonhosted.org/packages/fb/e3/fe3fd4702a3832a255f4d43013eacb0ef5fc155a5960ea9269d8696db28b/coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053", size = 260638, upload-time = "2025-12-08T13:13:32.965Z" },
- { url = "https://files.pythonhosted.org/packages/ad/01/63186cb000307f2b4da463f72af9b85d380236965574c78e7e27680a2593/coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071", size = 262705, upload-time = "2025-12-08T13:13:34.378Z" },
- { url = "https://files.pythonhosted.org/packages/7c/a1/c0dacef0cc865f2455d59eed3548573ce47ed603205ffd0735d1d78b5906/coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e", size = 265125, upload-time = "2025-12-08T13:13:35.73Z" },
- { url = "https://files.pythonhosted.org/packages/ef/92/82b99223628b61300bd382c205795533bed021505eab6dd86e11fb5d7925/coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493", size = 259844, upload-time = "2025-12-08T13:13:37.69Z" },
- { url = "https://files.pythonhosted.org/packages/cf/2c/89b0291ae4e6cd59ef042708e1c438e2290f8c31959a20055d8768349ee2/coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0", size = 262700, upload-time = "2025-12-08T13:13:39.525Z" },
- { url = "https://files.pythonhosted.org/packages/bf/f9/a5f992efae1996245e796bae34ceb942b05db275e4b34222a9a40b9fbd3b/coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e", size = 260321, upload-time = "2025-12-08T13:13:41.172Z" },
- { url = "https://files.pythonhosted.org/packages/4c/89/a29f5d98c64fedbe32e2ac3c227fbf78edc01cc7572eee17d61024d89889/coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c", size = 259222, upload-time = "2025-12-08T13:13:43.282Z" },
- { url = "https://files.pythonhosted.org/packages/b3/c3/940fe447aae302a6701ee51e53af7e08b86ff6eed7631e5740c157ee22b9/coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e", size = 261411, upload-time = "2025-12-08T13:13:44.72Z" },
- { url = "https://files.pythonhosted.org/packages/eb/31/12a4aec689cb942a89129587860ed4d0fd522d5fda81237147fde554b8ae/coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46", size = 221505, upload-time = "2025-12-08T13:13:46.332Z" },
- { url = "https://files.pythonhosted.org/packages/65/8c/3b5fe3259d863572d2b0827642c50c3855d26b3aefe80bdc9eba1f0af3b0/coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39", size = 222569, upload-time = "2025-12-08T13:13:47.79Z" },
- { url = "https://files.pythonhosted.org/packages/b0/39/f71fa8316a96ac72fc3908839df651e8eccee650001a17f2c78cdb355624/coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e", size = 220841, upload-time = "2025-12-08T13:13:49.243Z" },
- { url = "https://files.pythonhosted.org/packages/f8/4b/9b54bedda55421449811dcd5263a2798a63f48896c24dfb92b0f1b0845bd/coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256", size = 218343, upload-time = "2025-12-08T13:13:50.811Z" },
- { url = "https://files.pythonhosted.org/packages/59/df/c3a1f34d4bba2e592c8979f924da4d3d4598b0df2392fbddb7761258e3dc/coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a", size = 218672, upload-time = "2025-12-08T13:13:52.284Z" },
- { url = "https://files.pythonhosted.org/packages/07/62/eec0659e47857698645ff4e6ad02e30186eb8afd65214fd43f02a76537cb/coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9", size = 249715, upload-time = "2025-12-08T13:13:53.791Z" },
- { url = "https://files.pythonhosted.org/packages/23/2d/3c7ff8b2e0e634c1f58d095f071f52ed3c23ff25be524b0ccae8b71f99f8/coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19", size = 252225, upload-time = "2025-12-08T13:13:55.274Z" },
- { url = "https://files.pythonhosted.org/packages/aa/ac/fb03b469d20e9c9a81093575003f959cf91a4a517b783aab090e4538764b/coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be", size = 253559, upload-time = "2025-12-08T13:13:57.161Z" },
- { url = "https://files.pythonhosted.org/packages/29/62/14afa9e792383c66cc0a3b872a06ded6e4ed1079c7d35de274f11d27064e/coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb", size = 249724, upload-time = "2025-12-08T13:13:58.692Z" },
- { url = "https://files.pythonhosted.org/packages/31/b7/333f3dab2939070613696ab3ee91738950f0467778c6e5a5052e840646b7/coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8", size = 251582, upload-time = "2025-12-08T13:14:00.642Z" },
- { url = "https://files.pythonhosted.org/packages/81/cb/69162bda9381f39b2287265d7e29ee770f7c27c19f470164350a38318764/coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b", size = 249538, upload-time = "2025-12-08T13:14:02.556Z" },
- { url = "https://files.pythonhosted.org/packages/e0/76/350387b56a30f4970abe32b90b2a434f87d29f8b7d4ae40d2e8a85aacfb3/coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9", size = 249349, upload-time = "2025-12-08T13:14:04.015Z" },
- { url = "https://files.pythonhosted.org/packages/86/0d/7f6c42b8d59f4c7e43ea3059f573c0dcfed98ba46eb43c68c69e52ae095c/coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927", size = 251011, upload-time = "2025-12-08T13:14:05.505Z" },
- { url = "https://files.pythonhosted.org/packages/d7/f1/4bb2dff379721bb0b5c649d5c5eaf438462cad824acf32eb1b7ca0c7078e/coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f", size = 221091, upload-time = "2025-12-08T13:14:07.127Z" },
- { url = "https://files.pythonhosted.org/packages/ba/44/c239da52f373ce379c194b0ee3bcc121020e397242b85f99e0afc8615066/coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc", size = 221904, upload-time = "2025-12-08T13:14:08.542Z" },
- { url = "https://files.pythonhosted.org/packages/89/1f/b9f04016d2a29c2e4a0307baefefad1a4ec5724946a2b3e482690486cade/coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b", size = 220480, upload-time = "2025-12-08T13:14:10.958Z" },
- { url = "https://files.pythonhosted.org/packages/16/d4/364a1439766c8e8647860584171c36010ca3226e6e45b1753b1b249c5161/coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28", size = 219074, upload-time = "2025-12-08T13:14:13.345Z" },
- { url = "https://files.pythonhosted.org/packages/ce/f4/71ba8be63351e099911051b2089662c03d5671437a0ec2171823c8e03bec/coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe", size = 219342, upload-time = "2025-12-08T13:14:15.02Z" },
- { url = "https://files.pythonhosted.org/packages/5e/25/127d8ed03d7711a387d96f132589057213e3aef7475afdaa303412463f22/coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657", size = 260713, upload-time = "2025-12-08T13:14:16.907Z" },
- { url = "https://files.pythonhosted.org/packages/fd/db/559fbb6def07d25b2243663b46ba9eb5a3c6586c0c6f4e62980a68f0ee1c/coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff", size = 262825, upload-time = "2025-12-08T13:14:18.68Z" },
- { url = "https://files.pythonhosted.org/packages/37/99/6ee5bf7eff884766edb43bd8736b5e1c5144d0fe47498c3779326fe75a35/coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3", size = 265233, upload-time = "2025-12-08T13:14:20.55Z" },
- { url = "https://files.pythonhosted.org/packages/d8/90/92f18fe0356ea69e1f98f688ed80cec39f44e9f09a1f26a1bbf017cc67f2/coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b", size = 259779, upload-time = "2025-12-08T13:14:22.367Z" },
- { url = "https://files.pythonhosted.org/packages/90/5d/b312a8b45b37a42ea7d27d7d3ff98ade3a6c892dd48d1d503e773503373f/coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d", size = 262700, upload-time = "2025-12-08T13:14:24.309Z" },
- { url = "https://files.pythonhosted.org/packages/63/f8/b1d0de5c39351eb71c366f872376d09386640840a2e09b0d03973d791e20/coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e", size = 260302, upload-time = "2025-12-08T13:14:26.068Z" },
- { url = "https://files.pythonhosted.org/packages/aa/7c/d42f4435bc40c55558b3109a39e2d456cddcec37434f62a1f1230991667a/coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940", size = 259136, upload-time = "2025-12-08T13:14:27.604Z" },
- { url = "https://files.pythonhosted.org/packages/b8/d3/23413241dc04d47cfe19b9a65b32a2edd67ecd0b817400c2843ebc58c847/coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2", size = 261467, upload-time = "2025-12-08T13:14:29.09Z" },
- { url = "https://files.pythonhosted.org/packages/13/e6/6e063174500eee216b96272c0d1847bf215926786f85c2bd024cf4d02d2f/coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7", size = 221875, upload-time = "2025-12-08T13:14:31.106Z" },
- { url = "https://files.pythonhosted.org/packages/3b/46/f4fb293e4cbe3620e3ac2a3e8fd566ed33affb5861a9b20e3dd6c1896cbc/coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc", size = 222982, upload-time = "2025-12-08T13:14:33.1Z" },
- { url = "https://files.pythonhosted.org/packages/68/62/5b3b9018215ed9733fbd1ae3b2ed75c5de62c3b55377a52cae732e1b7805/coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a", size = 221016, upload-time = "2025-12-08T13:14:34.601Z" },
- { url = "https://files.pythonhosted.org/packages/8d/4c/1968f32fb9a2604645827e11ff84a31e59d532e01995f904723b4f5328b3/coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904", size = 210068, upload-time = "2025-12-08T13:14:36.236Z" },
-]
-
-[package.optional-dependencies]
-toml = [
- { name = "tomli", marker = "python_full_version <= '3.11'" },
-]
-
-[[package]]
-name = "distlib"
-version = "0.3.9"
+version = "7.13.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ad/49/349848445b0e53660e258acbcc9b0d014895b6739237920886672240f84b/coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3", size = 826523, upload-time = "2026-01-25T13:00:04.889Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" },
+ { url = "https://files.pythonhosted.org/packages/46/39/e92a35f7800222d3f7b2cbb7bbc3b65672ae8d501cb31801b2d2bd7acdf1/coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d", size = 219142, upload-time = "2026-01-25T12:58:00.448Z" },
+ { url = "https://files.pythonhosted.org/packages/45/7a/8bf9e9309c4c996e65c52a7c5a112707ecdd9fbaf49e10b5a705a402bbb4/coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3", size = 219503, upload-time = "2026-01-25T12:58:02.451Z" },
+ { url = "https://files.pythonhosted.org/packages/87/93/17661e06b7b37580923f3f12406ac91d78aeed293fb6da0b69cc7957582f/coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99", size = 251006, upload-time = "2026-01-25T12:58:04.059Z" },
+ { url = "https://files.pythonhosted.org/packages/12/f0/f9e59fb8c310171497f379e25db060abef9fa605e09d63157eebec102676/coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f", size = 253750, upload-time = "2026-01-25T12:58:05.574Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/b1/1935e31add2232663cf7edd8269548b122a7d100047ff93475dbaaae673e/coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f", size = 254862, upload-time = "2026-01-25T12:58:07.647Z" },
+ { url = "https://files.pythonhosted.org/packages/af/59/b5e97071ec13df5f45da2b3391b6cdbec78ba20757bc92580a5b3d5fa53c/coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa", size = 251420, upload-time = "2026-01-25T12:58:09.309Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/75/9495932f87469d013dc515fb0ce1aac5fa97766f38f6b1a1deb1ee7b7f3a/coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce", size = 252786, upload-time = "2026-01-25T12:58:10.909Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/59/af550721f0eb62f46f7b8cb7e6f1860592189267b1c411a4e3a057caacee/coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94", size = 250928, upload-time = "2026-01-25T12:58:12.449Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/b1/21b4445709aae500be4ab43bbcfb4e53dc0811c3396dcb11bf9f23fd0226/coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5", size = 250496, upload-time = "2026-01-25T12:58:14.047Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/b1/0f5d89dfe0392990e4f3980adbde3eb34885bc1effb2dc369e0bf385e389/coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b", size = 252373, upload-time = "2026-01-25T12:58:15.976Z" },
+ { url = "https://files.pythonhosted.org/packages/01/c9/0cf1a6a57a9968cc049a6b896693faa523c638a5314b1fc374eb2b2ac904/coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41", size = 221696, upload-time = "2026-01-25T12:58:17.517Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/05/d7540bf983f09d32803911afed135524570f8c47bb394bf6206c1dc3a786/coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e", size = 222504, upload-time = "2026-01-25T12:58:19.115Z" },
+ { url = "https://files.pythonhosted.org/packages/15/8b/1a9f037a736ced0a12aacf6330cdaad5008081142a7070bc58b0f7930cbc/coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894", size = 221120, upload-time = "2026-01-25T12:58:21.334Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/f0/3d3eac7568ab6096ff23791a526b0048a1ff3f49d0e236b2af6fb6558e88/coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6", size = 219168, upload-time = "2026-01-25T12:58:23.376Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/a6/f8b5cfeddbab95fdef4dcd682d82e5dcff7a112ced57a959f89537ee9995/coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc", size = 219537, upload-time = "2026-01-25T12:58:24.932Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/e6/8d8e6e0c516c838229d1e41cadcec91745f4b1031d4db17ce0043a0423b4/coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f", size = 250528, upload-time = "2026-01-25T12:58:26.567Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/78/befa6640f74092b86961f957f26504c8fba3d7da57cc2ab7407391870495/coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1", size = 253132, upload-time = "2026-01-25T12:58:28.251Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/10/1630db1edd8ce675124a2ee0f7becc603d2bb7b345c2387b4b95c6907094/coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9", size = 254374, upload-time = "2026-01-25T12:58:30.294Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/1d/0d9381647b1e8e6d310ac4140be9c428a0277330991e0c35bdd751e338a4/coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c", size = 250762, upload-time = "2026-01-25T12:58:32.036Z" },
+ { url = "https://files.pythonhosted.org/packages/43/e4/5636dfc9a7c871ee8776af83ee33b4c26bc508ad6cee1e89b6419a366582/coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5", size = 252502, upload-time = "2026-01-25T12:58:33.961Z" },
+ { url = "https://files.pythonhosted.org/packages/02/2a/7ff2884d79d420cbb2d12fed6fff727b6d0ef27253140d3cdbbd03187ee0/coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4", size = 250463, upload-time = "2026-01-25T12:58:35.529Z" },
+ { url = "https://files.pythonhosted.org/packages/91/c0/ba51087db645b6c7261570400fc62c89a16278763f36ba618dc8657a187b/coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c", size = 250288, upload-time = "2026-01-25T12:58:37.226Z" },
+ { url = "https://files.pythonhosted.org/packages/03/07/44e6f428551c4d9faf63ebcefe49b30e5c89d1be96f6a3abd86a52da9d15/coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31", size = 252063, upload-time = "2026-01-25T12:58:38.821Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/67/35b730ad7e1859dd57e834d1bc06080d22d2f87457d53f692fce3f24a5a9/coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8", size = 221716, upload-time = "2026-01-25T12:58:40.484Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/82/e5fcf5a97c72f45fc14829237a6550bf49d0ab882ac90e04b12a69db76b4/coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb", size = 222522, upload-time = "2026-01-25T12:58:43.247Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/f1/25d7b2f946d239dd2d6644ca2cc060d24f97551e2af13b6c24c722ae5f97/coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557", size = 221145, upload-time = "2026-01-25T12:58:45Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f7/080376c029c8f76fadfe43911d0daffa0cbdc9f9418a0eead70c56fb7f4b/coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e", size = 219861, upload-time = "2026-01-25T12:58:46.586Z" },
+ { url = "https://files.pythonhosted.org/packages/42/11/0b5e315af5ab35f4c4a70e64d3314e4eec25eefc6dec13be3a7d5ffe8ac5/coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7", size = 220207, upload-time = "2026-01-25T12:58:48.277Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/0c/0874d0318fb1062117acbef06a09cf8b63f3060c22265adaad24b36306b7/coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3", size = 261504, upload-time = "2026-01-25T12:58:49.904Z" },
+ { url = "https://files.pythonhosted.org/packages/83/5e/1cd72c22ecb30751e43a72f40ba50fcef1b7e93e3ea823bd9feda8e51f9a/coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3", size = 263582, upload-time = "2026-01-25T12:58:51.582Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/da/8acf356707c7a42df4d0657020308e23e5a07397e81492640c186268497c/coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421", size = 266008, upload-time = "2026-01-25T12:58:53.234Z" },
+ { url = "https://files.pythonhosted.org/packages/41/41/ea1730af99960309423c6ea8d6a4f1fa5564b2d97bd1d29dda4b42611f04/coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5", size = 260762, upload-time = "2026-01-25T12:58:55.372Z" },
+ { url = "https://files.pythonhosted.org/packages/22/fa/02884d2080ba71db64fdc127b311db60e01fe6ba797d9c8363725e39f4d5/coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23", size = 263571, upload-time = "2026-01-25T12:58:57.52Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/6b/4083aaaeba9b3112f55ac57c2ce7001dc4d8fa3fcc228a39f09cc84ede27/coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c", size = 261200, upload-time = "2026-01-25T12:58:59.255Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/d2/aea92fa36d61955e8c416ede9cf9bf142aa196f3aea214bb67f85235a050/coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f", size = 260095, upload-time = "2026-01-25T12:59:01.066Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/ae/04ffe96a80f107ea21b22b2367175c621da920063260a1c22f9452fd7866/coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573", size = 262284, upload-time = "2026-01-25T12:59:02.802Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/7a/6f354dcd7dfc41297791d6fb4e0d618acb55810bde2c1fd14b3939e05c2b/coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343", size = 222389, upload-time = "2026-01-25T12:59:04.563Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/d5/080ad292a4a3d3daf411574be0a1f56d6dee2c4fdf6b005342be9fac807f/coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47", size = 223450, upload-time = "2026-01-25T12:59:06.677Z" },
+ { url = "https://files.pythonhosted.org/packages/88/96/df576fbacc522e9fb8d1c4b7a7fc62eb734be56e2cba1d88d2eabe08ea3f/coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7", size = 221707, upload-time = "2026-01-25T12:59:08.363Z" },
+ { url = "https://files.pythonhosted.org/packages/55/53/1da9e51a0775634b04fcc11eb25c002fc58ee4f92ce2e8512f94ac5fc5bf/coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef", size = 219213, upload-time = "2026-01-25T12:59:11.909Z" },
+ { url = "https://files.pythonhosted.org/packages/46/35/b3caac3ebbd10230fea5a33012b27d19e999a17c9285c4228b4b2e35b7da/coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f", size = 219549, upload-time = "2026-01-25T12:59:13.638Z" },
+ { url = "https://files.pythonhosted.org/packages/76/9c/e1cf7def1bdc72c1907e60703983a588f9558434a2ff94615747bd73c192/coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5", size = 250586, upload-time = "2026-01-25T12:59:15.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/49/f54ec02ed12be66c8d8897270505759e057b0c68564a65c429ccdd1f139e/coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4", size = 253093, upload-time = "2026-01-25T12:59:17.491Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/5e/aaf86be3e181d907e23c0f61fccaeb38de8e6f6b47aed92bf57d8fc9c034/coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27", size = 254446, upload-time = "2026-01-25T12:59:19.752Z" },
+ { url = "https://files.pythonhosted.org/packages/28/c8/a5fa01460e2d75b0c853b392080d6829d3ca8b5ab31e158fa0501bc7c708/coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548", size = 250615, upload-time = "2026-01-25T12:59:21.928Z" },
+ { url = "https://files.pythonhosted.org/packages/86/0b/6d56315a55f7062bb66410732c24879ccb2ec527ab6630246de5fe45a1df/coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660", size = 252452, upload-time = "2026-01-25T12:59:23.592Z" },
+ { url = "https://files.pythonhosted.org/packages/30/19/9bc550363ebc6b0ea121977ee44d05ecd1e8bf79018b8444f1028701c563/coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92", size = 250418, upload-time = "2026-01-25T12:59:25.392Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/53/580530a31ca2f0cc6f07a8f2ab5460785b02bb11bdf815d4c4d37a4c5169/coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82", size = 250231, upload-time = "2026-01-25T12:59:27.888Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/42/dd9093f919dc3088cb472893651884bd675e3df3d38a43f9053656dca9a2/coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892", size = 251888, upload-time = "2026-01-25T12:59:29.636Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/a6/0af4053e6e819774626e133c3d6f70fae4d44884bfc4b126cb647baee8d3/coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe", size = 221968, upload-time = "2026-01-25T12:59:31.424Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/cc/5aff1e1f80d55862442855517bb8ad8ad3a68639441ff6287dde6a58558b/coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859", size = 222783, upload-time = "2026-01-25T12:59:33.118Z" },
+ { url = "https://files.pythonhosted.org/packages/de/20/09abafb24f84b3292cc658728803416c15b79f9ee5e68d25238a895b07d9/coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6", size = 221348, upload-time = "2026-01-25T12:59:34.939Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/60/a3820c7232db63be060e4019017cd3426751c2699dab3c62819cdbcea387/coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b", size = 219950, upload-time = "2026-01-25T12:59:36.624Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/37/e4ef5975fdeb86b1e56db9a82f41b032e3d93a840ebaf4064f39e770d5c5/coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417", size = 220209, upload-time = "2026-01-25T12:59:38.339Z" },
+ { url = "https://files.pythonhosted.org/packages/54/df/d40e091d00c51adca1e251d3b60a8b464112efa3004949e96a74d7c19a64/coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee", size = 261576, upload-time = "2026-01-25T12:59:40.446Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/44/5259c4bed54e3392e5c176121af9f71919d96dde853386e7730e705f3520/coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1", size = 263704, upload-time = "2026-01-25T12:59:42.346Z" },
+ { url = "https://files.pythonhosted.org/packages/16/bd/ae9f005827abcbe2c70157459ae86053971c9fa14617b63903abbdce26d9/coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d", size = 266109, upload-time = "2026-01-25T12:59:44.073Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/c0/8e279c1c0f5b1eaa3ad9b0fb7a5637fc0379ea7d85a781c0fe0bb3cfc2ab/coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6", size = 260686, upload-time = "2026-01-25T12:59:45.804Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/47/3a8112627e9d863e7cddd72894171c929e94491a597811725befdcd76bce/coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a", size = 263568, upload-time = "2026-01-25T12:59:47.929Z" },
+ { url = "https://files.pythonhosted.org/packages/92/bc/7ea367d84afa3120afc3ce6de294fd2dcd33b51e2e7fbe4bbfd200f2cb8c/coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04", size = 261174, upload-time = "2026-01-25T12:59:49.717Z" },
+ { url = "https://files.pythonhosted.org/packages/33/b7/f1092dcecb6637e31cc2db099581ee5c61a17647849bae6b8261a2b78430/coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f", size = 260017, upload-time = "2026-01-25T12:59:51.463Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/cd/f3d07d4b95fbe1a2ef0958c15da614f7e4f557720132de34d2dc3aa7e911/coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f", size = 262337, upload-time = "2026-01-25T12:59:53.407Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/db/b0d5b2873a07cb1e06a55d998697c0a5a540dcefbf353774c99eb3874513/coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3", size = 222749, upload-time = "2026-01-25T12:59:56.316Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/2f/838a5394c082ac57d85f57f6aba53093b30d9089781df72412126505716f/coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba", size = 223857, upload-time = "2026-01-25T12:59:58.201Z" },
+ { url = "https://files.pythonhosted.org/packages/44/d4/b608243e76ead3a4298824b50922b89ef793e50069ce30316a65c1b4d7ef/coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c", size = 221881, upload-time = "2026-01-25T13:00:00.449Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/db/d291e30fdf7ea617a335531e72294e0c723356d7fdde8fba00610a76bda9/coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5", size = 210943, upload-time = "2026-01-25T13:00:02.388Z" },
]
[[package]]
@@ -440,329 +361,506 @@ wheels = [
]
[[package]]
-name = "exceptiongroup"
-version = "1.3.0"
+name = "frozenlist"
+version = "1.8.0"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
+ { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" },
+ { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" },
+ { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" },
+ { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" },
+ { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" },
+ { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" },
+ { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" },
+ { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" },
+ { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" },
+ { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" },
+ { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" },
+ { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" },
+ { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" },
+ { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" },
+ { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" },
+ { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" },
+ { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" },
+ { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
]
[[package]]
-name = "filelock"
-version = "3.20.3"
+name = "ghp-import"
+version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" }
+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, upload-time = "2022-05-02T15:47:16.11Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
]
[[package]]
-name = "frozenlist"
-version = "1.7.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a", size = 81304, upload-time = "2025-06-09T22:59:46.226Z" },
- { url = "https://files.pythonhosted.org/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61", size = 47735, upload-time = "2025-06-09T22:59:48.133Z" },
- { url = "https://files.pythonhosted.org/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d", size = 46775, upload-time = "2025-06-09T22:59:49.564Z" },
- { url = "https://files.pythonhosted.org/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e", size = 224644, upload-time = "2025-06-09T22:59:51.35Z" },
- { url = "https://files.pythonhosted.org/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9", size = 222125, upload-time = "2025-06-09T22:59:52.884Z" },
- { url = "https://files.pythonhosted.org/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c", size = 233455, upload-time = "2025-06-09T22:59:54.74Z" },
- { url = "https://files.pythonhosted.org/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981", size = 227339, upload-time = "2025-06-09T22:59:56.187Z" },
- { url = "https://files.pythonhosted.org/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615", size = 212969, upload-time = "2025-06-09T22:59:57.604Z" },
- { url = "https://files.pythonhosted.org/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50", size = 222862, upload-time = "2025-06-09T22:59:59.498Z" },
- { url = "https://files.pythonhosted.org/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa", size = 222492, upload-time = "2025-06-09T23:00:01.026Z" },
- { url = "https://files.pythonhosted.org/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577", size = 238250, upload-time = "2025-06-09T23:00:03.401Z" },
- { url = "https://files.pythonhosted.org/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59", size = 218720, upload-time = "2025-06-09T23:00:05.282Z" },
- { url = "https://files.pythonhosted.org/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e", size = 232585, upload-time = "2025-06-09T23:00:07.962Z" },
- { url = "https://files.pythonhosted.org/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd", size = 234248, upload-time = "2025-06-09T23:00:09.428Z" },
- { url = "https://files.pythonhosted.org/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718", size = 221621, upload-time = "2025-06-09T23:00:11.32Z" },
- { url = "https://files.pythonhosted.org/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e", size = 39578, upload-time = "2025-06-09T23:00:13.526Z" },
- { url = "https://files.pythonhosted.org/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464", size = 43830, upload-time = "2025-06-09T23:00:14.98Z" },
- { url = "https://files.pythonhosted.org/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251, upload-time = "2025-06-09T23:00:16.279Z" },
- { url = "https://files.pythonhosted.org/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183, upload-time = "2025-06-09T23:00:17.698Z" },
- { url = "https://files.pythonhosted.org/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107, upload-time = "2025-06-09T23:00:18.952Z" },
- { url = "https://files.pythonhosted.org/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333, upload-time = "2025-06-09T23:00:20.275Z" },
- { url = "https://files.pythonhosted.org/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724, upload-time = "2025-06-09T23:00:21.705Z" },
- { url = "https://files.pythonhosted.org/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842, upload-time = "2025-06-09T23:00:23.148Z" },
- { url = "https://files.pythonhosted.org/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767, upload-time = "2025-06-09T23:00:25.103Z" },
- { url = "https://files.pythonhosted.org/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130, upload-time = "2025-06-09T23:00:27.061Z" },
- { url = "https://files.pythonhosted.org/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301, upload-time = "2025-06-09T23:00:29.02Z" },
- { url = "https://files.pythonhosted.org/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606, upload-time = "2025-06-09T23:00:30.514Z" },
- { url = "https://files.pythonhosted.org/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372, upload-time = "2025-06-09T23:00:31.966Z" },
- { url = "https://files.pythonhosted.org/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860, upload-time = "2025-06-09T23:00:33.375Z" },
- { url = "https://files.pythonhosted.org/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893, upload-time = "2025-06-09T23:00:35.002Z" },
- { url = "https://files.pythonhosted.org/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323, upload-time = "2025-06-09T23:00:36.468Z" },
- { url = "https://files.pythonhosted.org/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149, upload-time = "2025-06-09T23:00:37.963Z" },
- { url = "https://files.pythonhosted.org/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565, upload-time = "2025-06-09T23:00:39.753Z" },
- { url = "https://files.pythonhosted.org/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019, upload-time = "2025-06-09T23:00:40.988Z" },
- { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424, upload-time = "2025-06-09T23:00:42.24Z" },
- { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952, upload-time = "2025-06-09T23:00:43.481Z" },
- { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688, upload-time = "2025-06-09T23:00:44.793Z" },
- { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084, upload-time = "2025-06-09T23:00:46.125Z" },
- { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524, upload-time = "2025-06-09T23:00:47.73Z" },
- { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493, upload-time = "2025-06-09T23:00:49.742Z" },
- { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116, upload-time = "2025-06-09T23:00:51.352Z" },
- { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557, upload-time = "2025-06-09T23:00:52.855Z" },
- { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820, upload-time = "2025-06-09T23:00:54.43Z" },
- { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542, upload-time = "2025-06-09T23:00:56.409Z" },
- { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350, upload-time = "2025-06-09T23:00:58.468Z" },
- { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093, upload-time = "2025-06-09T23:01:00.015Z" },
- { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482, upload-time = "2025-06-09T23:01:01.474Z" },
- { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590, upload-time = "2025-06-09T23:01:02.961Z" },
- { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785, upload-time = "2025-06-09T23:01:05.095Z" },
- { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487, upload-time = "2025-06-09T23:01:06.54Z" },
- { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874, upload-time = "2025-06-09T23:01:07.752Z" },
- { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" },
- { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" },
- { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" },
- { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" },
- { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" },
- { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" },
- { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" },
- { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" },
- { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" },
- { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" },
- { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" },
- { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" },
- { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" },
- { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" },
- { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" },
- { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" },
- { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" },
- { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" },
- { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" },
- { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" },
- { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" },
- { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" },
- { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" },
- { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" },
- { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" },
- { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" },
- { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" },
- { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" },
- { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" },
- { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" },
- { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" },
- { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" },
- { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" },
- { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" },
- { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" },
-]
-
-[[package]]
-name = "identify"
-version = "2.6.12"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" },
+name = "griffe"
+version = "1.15.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/0c/3a471b6e31951dce2360477420d0a8d1e00dea6cf33b70f3e8c3ab6e28e1/griffe-1.15.0.tar.gz", hash = "sha256:7726e3afd6f298fbc3696e67958803e7ac843c1cfe59734b6251a40cdbfb5eea", size = 424112, upload-time = "2025-11-10T15:03:15.52Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl", hash = "sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3", size = 150705, upload-time = "2025-11-10T15:03:13.549Z" },
]
[[package]]
name = "idna"
-version = "3.10"
+version = "3.11"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
]
[[package]]
name = "iniconfig"
-version = "2.1.0"
+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, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "jmespath"
-version = "1.0.1"
+version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843, upload-time = "2022-06-17T18:00:12.224Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256, upload-time = "2022-06-17T18:00:10.251Z" },
+ { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" },
]
[[package]]
name = "librt"
-version = "0.6.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/37/c3/cdff3c10e2e608490dc0a310ccf11ba777b3943ad4fcead2a2ade98c21e1/librt-0.6.3.tar.gz", hash = "sha256:c724a884e642aa2bbad52bb0203ea40406ad742368a5f90da1b220e970384aae", size = 54209, upload-time = "2025-11-29T14:01:56.058Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a6/84/859df8db21dedab2538ddfbe1d486dda3eb66a98c6ad7ba754a99e25e45e/librt-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1", size = 27294, upload-time = "2025-11-29T14:00:35.053Z" },
- { url = "https://files.pythonhosted.org/packages/f7/01/ec3971cf9c4f827f17de6729bdfdbf01a67493147334f4ef8fac68936e3a/librt-0.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf", size = 27635, upload-time = "2025-11-29T14:00:36.496Z" },
- { url = "https://files.pythonhosted.org/packages/b4/f9/3efe201df84dd26388d2e0afa4c4dc668c8e406a3da7b7319152faf835a1/librt-0.6.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c5b31bed2c2f2fa1fcb4815b75f931121ae210dc89a3d607fb1725f5907f1437", size = 81768, upload-time = "2025-11-29T14:00:37.451Z" },
- { url = "https://files.pythonhosted.org/packages/0a/13/f63e60bc219b17f3d8f3d13423cd4972e597b0321c51cac7bfbdd5e1f7b9/librt-0.6.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f8ed5053ef9fb08d34f1fd80ff093ccbd1f67f147633a84cf4a7d9b09c0f089", size = 85884, upload-time = "2025-11-29T14:00:38.433Z" },
- { url = "https://files.pythonhosted.org/packages/c2/42/0068f14f39a79d1ce8a19d4988dd07371df1d0a7d3395fbdc8a25b1c9437/librt-0.6.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f0e4bd9bcb0ee34fa3dbedb05570da50b285f49e52c07a241da967840432513", size = 85830, upload-time = "2025-11-29T14:00:39.418Z" },
- { url = "https://files.pythonhosted.org/packages/14/1c/87f5af3a9e6564f09e50c72f82fc3057fd42d1facc8b510a707d0438c4ad/librt-0.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8f89c8d20dfa648a3f0a56861946eb00e5b00d6b00eea14bc5532b2fcfa8ef1", size = 88086, upload-time = "2025-11-29T14:00:40.555Z" },
- { url = "https://files.pythonhosted.org/packages/05/e5/22153b98b88a913b5b3f266f12e57df50a2a6960b3f8fcb825b1a0cfe40a/librt-0.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecc2c526547eacd20cb9fbba19a5268611dbc70c346499656d6cf30fae328977", size = 86470, upload-time = "2025-11-29T14:00:41.827Z" },
- { url = "https://files.pythonhosted.org/packages/18/3c/ea1edb587799b1edcc22444e0630fa422e32d7aaa5bfb5115b948acc2d1c/librt-0.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fbedeb9b48614d662822ee514567d2d49a8012037fc7b4cd63f282642c2f4b7d", size = 89079, upload-time = "2025-11-29T14:00:42.882Z" },
- { url = "https://files.pythonhosted.org/packages/73/ad/50bb4ae6b07c9f3ab19653e0830a210533b30eb9a18d515efb5a2b9d0c7c/librt-0.6.3-cp310-cp310-win32.whl", hash = "sha256:0765b0fe0927d189ee14b087cd595ae636bef04992e03fe6dfdaa383866c8a46", size = 19820, upload-time = "2025-11-29T14:00:44.211Z" },
- { url = "https://files.pythonhosted.org/packages/7a/12/7426ee78f3b1dbe11a90619d54cb241ca924ca3c0ff9ade3992178e9b440/librt-0.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:8c659f9fb8a2f16dc4131b803fa0144c1dadcb3ab24bb7914d01a6da58ae2457", size = 21332, upload-time = "2025-11-29T14:00:45.427Z" },
- { url = "https://files.pythonhosted.org/packages/8b/80/bc60fd16fe24910bf5974fb914778a2e8540cef55385ab2cb04a0dfe42c4/librt-0.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:61348cc488b18d1b1ff9f3e5fcd5ac43ed22d3e13e862489d2267c2337285c08", size = 27285, upload-time = "2025-11-29T14:00:46.626Z" },
- { url = "https://files.pythonhosted.org/packages/88/3c/26335536ed9ba097c79cffcee148393592e55758fe76d99015af3e47a6d0/librt-0.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64645b757d617ad5f98c08e07620bc488d4bced9ced91c6279cec418f16056fa", size = 27629, upload-time = "2025-11-29T14:00:47.863Z" },
- { url = "https://files.pythonhosted.org/packages/af/fd/2dcedeacfedee5d2eda23e7a49c1c12ce6221b5d58a13555f053203faafc/librt-0.6.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:26b8026393920320bb9a811b691d73c5981385d537ffc5b6e22e53f7b65d4122", size = 82039, upload-time = "2025-11-29T14:00:49.131Z" },
- { url = "https://files.pythonhosted.org/packages/48/ff/6aa11914b83b0dc2d489f7636942a8e3322650d0dba840db9a1b455f3caa/librt-0.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d998b432ed9ffccc49b820e913c8f327a82026349e9c34fa3690116f6b70770f", size = 86560, upload-time = "2025-11-29T14:00:50.403Z" },
- { url = "https://files.pythonhosted.org/packages/76/a1/d25af61958c2c7eb978164aeba0350719f615179ba3f428b682b9a5fdace/librt-0.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e18875e17ef69ba7dfa9623f2f95f3eda6f70b536079ee6d5763ecdfe6cc9040", size = 86494, upload-time = "2025-11-29T14:00:51.383Z" },
- { url = "https://files.pythonhosted.org/packages/7d/4b/40e75d3b258c801908e64b39788f9491635f9554f8717430a491385bd6f2/librt-0.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a218f85081fc3f70cddaed694323a1ad7db5ca028c379c214e3a7c11c0850523", size = 88914, upload-time = "2025-11-29T14:00:52.688Z" },
- { url = "https://files.pythonhosted.org/packages/97/6d/0070c81aba8a169224301c75fb5fb6c3c25ca67e6ced086584fc130d5a67/librt-0.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1ef42ff4edd369e84433ce9b188a64df0837f4f69e3d34d3b34d4955c599d03f", size = 86944, upload-time = "2025-11-29T14:00:53.768Z" },
- { url = "https://files.pythonhosted.org/packages/a6/94/809f38887941b7726692e0b5a083dbdc87dbb8cf893e3b286550c5f0b129/librt-0.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e0f2b79993fec23a685b3e8107ba5f8675eeae286675a216da0b09574fa1e47", size = 89852, upload-time = "2025-11-29T14:00:54.71Z" },
- { url = "https://files.pythonhosted.org/packages/58/a3/b0e5b1cda675b91f1111d8ba941da455d8bfaa22f4d2d8963ba96ccb5b12/librt-0.6.3-cp311-cp311-win32.whl", hash = "sha256:fd98cacf4e0fabcd4005c452cb8a31750258a85cab9a59fb3559e8078da408d7", size = 19948, upload-time = "2025-11-29T14:00:55.989Z" },
- { url = "https://files.pythonhosted.org/packages/cc/73/70011c2b37e3be3ece3affd3abc8ebe5cda482b03fd6b3397906321a901e/librt-0.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:e17b5b42c8045867ca9d1f54af00cc2275198d38de18545edaa7833d7e9e4ac8", size = 21406, upload-time = "2025-11-29T14:00:56.874Z" },
- { url = "https://files.pythonhosted.org/packages/91/ee/119aa759290af6ca0729edf513ca390c1afbeae60f3ecae9b9d56f25a8a9/librt-0.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:87597e3d57ec0120a3e1d857a708f80c02c42ea6b00227c728efbc860f067c45", size = 20875, upload-time = "2025-11-29T14:00:57.752Z" },
- { url = "https://files.pythonhosted.org/packages/b4/2c/b59249c566f98fe90e178baf59e83f628d6c38fb8bc78319301fccda0b5e/librt-0.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74418f718083009108dc9a42c21bf2e4802d49638a1249e13677585fcc9ca176", size = 27841, upload-time = "2025-11-29T14:00:58.925Z" },
- { url = "https://files.pythonhosted.org/packages/40/e8/9db01cafcd1a2872b76114c858f81cc29ce7ad606bc102020d6dabf470fb/librt-0.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:514f3f363d1ebc423357d36222c37e5c8e6674b6eae8d7195ac9a64903722057", size = 27844, upload-time = "2025-11-29T14:01:00.2Z" },
- { url = "https://files.pythonhosted.org/packages/59/4d/da449d3a7d83cc853af539dee42adc37b755d7eea4ad3880bacfd84b651d/librt-0.6.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cf1115207a5049d1f4b7b4b72de0e52f228d6c696803d94843907111cbf80610", size = 84091, upload-time = "2025-11-29T14:01:01.118Z" },
- { url = "https://files.pythonhosted.org/packages/ea/6c/f90306906fb6cc6eaf4725870f0347115de05431e1f96d35114392d31fda/librt-0.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad8ba80cdcea04bea7b78fcd4925bfbf408961e9d8397d2ee5d3ec121e20c08c", size = 88239, upload-time = "2025-11-29T14:01:02.11Z" },
- { url = "https://files.pythonhosted.org/packages/e7/ae/473ce7b423cfac2cb503851a89d9d2195bf615f534d5912bf86feeebbee7/librt-0.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4018904c83eab49c814e2494b4e22501a93cdb6c9f9425533fe693c3117126f9", size = 88815, upload-time = "2025-11-29T14:01:03.114Z" },
- { url = "https://files.pythonhosted.org/packages/c4/6d/934df738c87fb9617cabefe4891eece585a06abe6def25b4bca3b174429d/librt-0.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8983c5c06ac9c990eac5eb97a9f03fe41dc7e9d7993df74d9e8682a1056f596c", size = 90598, upload-time = "2025-11-29T14:01:04.071Z" },
- { url = "https://files.pythonhosted.org/packages/72/89/eeaa124f5e0f431c2b39119550378ae817a4b1a3c93fd7122f0639336fff/librt-0.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7769c579663a6f8dbf34878969ac71befa42067ce6bf78e6370bf0d1194997c", size = 88603, upload-time = "2025-11-29T14:01:05.02Z" },
- { url = "https://files.pythonhosted.org/packages/4d/ed/c60b3c1cfc27d709bc0288af428ce58543fcb5053cf3eadbc773c24257f5/librt-0.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d3c9a07eafdc70556f8c220da4a538e715668c0c63cabcc436a026e4e89950bf", size = 92112, upload-time = "2025-11-29T14:01:06.304Z" },
- { url = "https://files.pythonhosted.org/packages/c1/ab/f56169be5f716ef4ab0277be70bcb1874b4effc262e655d85b505af4884d/librt-0.6.3-cp312-cp312-win32.whl", hash = "sha256:38320386a48a15033da295df276aea93a92dfa94a862e06893f75ea1d8bbe89d", size = 20127, upload-time = "2025-11-29T14:01:07.283Z" },
- { url = "https://files.pythonhosted.org/packages/ff/8d/222750ce82bf95125529eaab585ac7e2829df252f3cfc05d68792fb1dd2c/librt-0.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:c0ecf4786ad0404b072196b5df774b1bb23c8aacdcacb6c10b4128bc7b00bd01", size = 21545, upload-time = "2025-11-29T14:01:08.184Z" },
- { url = "https://files.pythonhosted.org/packages/72/c9/f731ddcfb72f446a92a8674c6b8e1e2242773cce43a04f41549bd8b958ff/librt-0.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:9f2a6623057989ebc469cd9cc8fe436c40117a0147627568d03f84aef7854c55", size = 20946, upload-time = "2025-11-29T14:01:09.384Z" },
- { url = "https://files.pythonhosted.org/packages/dd/aa/3055dd440f8b8b3b7e8624539a0749dd8e1913e978993bcca9ce7e306231/librt-0.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9e716f9012148a81f02f46a04fc4c663420c6fbfeacfac0b5e128cf43b4413d3", size = 27874, upload-time = "2025-11-29T14:01:10.615Z" },
- { url = "https://files.pythonhosted.org/packages/ef/93/226d7dd455eaa4c26712b5ccb2dfcca12831baa7f898c8ffd3a831e29fda/librt-0.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:669ff2495728009a96339c5ad2612569c6d8be4474e68f3f3ac85d7c3261f5f5", size = 27852, upload-time = "2025-11-29T14:01:11.535Z" },
- { url = "https://files.pythonhosted.org/packages/4e/8b/db9d51191aef4e4cc06285250affe0bb0ad8b2ed815f7ca77951655e6f02/librt-0.6.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:349b6873ebccfc24c9efd244e49da9f8a5c10f60f07575e248921aae2123fc42", size = 84264, upload-time = "2025-11-29T14:01:12.461Z" },
- { url = "https://files.pythonhosted.org/packages/8d/53/297c96bda3b5a73bdaf748f1e3ae757edd29a0a41a956b9c10379f193417/librt-0.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c74c26736008481c9f6d0adf1aedb5a52aff7361fea98276d1f965c0256ee70", size = 88432, upload-time = "2025-11-29T14:01:13.405Z" },
- { url = "https://files.pythonhosted.org/packages/54/3a/c005516071123278e340f22de72fa53d51e259d49215295c212da16c4dc2/librt-0.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:408a36ddc75e91918cb15b03460bdc8a015885025d67e68c6f78f08c3a88f522", size = 89014, upload-time = "2025-11-29T14:01:14.373Z" },
- { url = "https://files.pythonhosted.org/packages/8e/9b/ea715f818d926d17b94c80a12d81a79e95c44f52848e61e8ca1ff29bb9a9/librt-0.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e61ab234624c9ffca0248a707feffe6fac2343758a36725d8eb8a6efef0f8c30", size = 90807, upload-time = "2025-11-29T14:01:15.377Z" },
- { url = "https://files.pythonhosted.org/packages/f0/fc/4e2e4c87e002fa60917a8e474fd13c4bac9a759df82be3778573bb1ab954/librt-0.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:324462fe7e3896d592b967196512491ec60ca6e49c446fe59f40743d08c97917", size = 88890, upload-time = "2025-11-29T14:01:16.633Z" },
- { url = "https://files.pythonhosted.org/packages/70/7f/c7428734fbdfd4db3d5b9237fc3a857880b2ace66492836f6529fef25d92/librt-0.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36b2ec8c15030002c7f688b4863e7be42820d7c62d9c6eece3db54a2400f0530", size = 92300, upload-time = "2025-11-29T14:01:17.658Z" },
- { url = "https://files.pythonhosted.org/packages/f9/0c/738c4824fdfe74dc0f95d5e90ef9e759d4ecf7fd5ba964d54a7703322251/librt-0.6.3-cp313-cp313-win32.whl", hash = "sha256:25b1b60cb059471c0c0c803e07d0dfdc79e41a0a122f288b819219ed162672a3", size = 20159, upload-time = "2025-11-29T14:01:18.61Z" },
- { url = "https://files.pythonhosted.org/packages/f2/95/93d0e61bc617306ecf4c54636b5cbde4947d872563565c4abdd9d07a39d3/librt-0.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:10a95ad074e2a98c9e4abc7f5b7d40e5ecbfa84c04c6ab8a70fabf59bd429b88", size = 21484, upload-time = "2025-11-29T14:01:19.506Z" },
- { url = "https://files.pythonhosted.org/packages/10/23/abd7ace79ab54d1dbee265f13529266f686a7ce2d21ab59a992f989009b6/librt-0.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:17000df14f552e86877d67e4ab7966912224efc9368e998c96a6974a8d609bf9", size = 20935, upload-time = "2025-11-29T14:01:20.415Z" },
- { url = "https://files.pythonhosted.org/packages/83/14/c06cb31152182798ed98be73f54932ab984894f5a8fccf9b73130897a938/librt-0.6.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8e695f25d1a425ad7a272902af8ab8c8d66c1998b177e4b5f5e7b4e215d0c88a", size = 27566, upload-time = "2025-11-29T14:01:21.609Z" },
- { url = "https://files.pythonhosted.org/packages/0c/b1/ce83ca7b057b06150519152f53a0b302d7c33c8692ce2f01f669b5a819d9/librt-0.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3e84a4121a7ae360ca4da436548a9c1ca8ca134a5ced76c893cc5944426164bd", size = 27753, upload-time = "2025-11-29T14:01:22.558Z" },
- { url = "https://files.pythonhosted.org/packages/3b/ec/739a885ef0a2839b6c25f1b01c99149d2cb6a34e933ffc8c051fcd22012e/librt-0.6.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:05f385a414de3f950886ea0aad8f109650d4b712cf9cc14cc17f5f62a9ab240b", size = 83178, upload-time = "2025-11-29T14:01:23.555Z" },
- { url = "https://files.pythonhosted.org/packages/db/bd/dc18bb1489d48c0911b9f4d72eae2d304ea264e215ba80f1e6ba4a9fc41d/librt-0.6.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36a8e337461150b05ca2c7bdedb9e591dfc262c5230422cea398e89d0c746cdc", size = 87266, upload-time = "2025-11-29T14:01:24.532Z" },
- { url = "https://files.pythonhosted.org/packages/94/f3/d0c5431b39eef15e48088b2d739ad84b17c2f1a22c0345c6d4c4a42b135e/librt-0.6.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcbe48f6a03979384f27086484dc2a14959be1613cb173458bd58f714f2c48f3", size = 87623, upload-time = "2025-11-29T14:01:25.798Z" },
- { url = "https://files.pythonhosted.org/packages/3b/15/9a52e90834e4bd6ee16cdbaf551cb32227cbaad27398391a189c489318bc/librt-0.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4bca9e4c260233fba37b15c4ec2f78aa99c1a79fbf902d19dd4a763c5c3fb751", size = 89436, upload-time = "2025-11-29T14:01:26.769Z" },
- { url = "https://files.pythonhosted.org/packages/c3/8a/a7e78e46e8486e023c50f21758930ef4793999115229afd65de69e94c9cc/librt-0.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:760c25ed6ac968e24803eb5f7deb17ce026902d39865e83036bacbf5cf242aa8", size = 87540, upload-time = "2025-11-29T14:01:27.756Z" },
- { url = "https://files.pythonhosted.org/packages/49/01/93799044a1cccac31f1074b07c583e181829d240539657e7f305ae63ae2a/librt-0.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4a93a353ccff20df6e34fa855ae8fd788832c88f40a9070e3ddd3356a9f0e", size = 90597, upload-time = "2025-11-29T14:01:29.35Z" },
- { url = "https://files.pythonhosted.org/packages/a7/29/00c7f58b8f8eb1bad6529ffb6c9cdcc0890a27dac59ecda04f817ead5277/librt-0.6.3-cp314-cp314-win32.whl", hash = "sha256:cb92741c2b4ea63c09609b064b26f7f5d9032b61ae222558c55832ec3ad0bcaf", size = 18955, upload-time = "2025-11-29T14:01:30.325Z" },
- { url = "https://files.pythonhosted.org/packages/d7/13/2739e6e197a9f751375a37908a6a5b0bff637b81338497a1bcb5817394da/librt-0.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:fdcd095b1b812d756fa5452aca93b962cf620694c0cadb192cec2bb77dcca9a2", size = 20263, upload-time = "2025-11-29T14:01:31.287Z" },
- { url = "https://files.pythonhosted.org/packages/e1/73/393868fc2158705ea003114a24e73bb10b03bda31e9ad7b5c5ec6575338b/librt-0.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:822ca79e28720a76a935c228d37da6579edef048a17cd98d406a2484d10eda78", size = 19575, upload-time = "2025-11-29T14:01:32.229Z" },
- { url = "https://files.pythonhosted.org/packages/48/6d/3c8ff3dec21bf804a205286dd63fd28dcdbe00b8dd7eb7ccf2e21a40a0b0/librt-0.6.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:078cd77064d1640cb7b0650871a772956066174d92c8aeda188a489b58495179", size = 28732, upload-time = "2025-11-29T14:01:33.165Z" },
- { url = "https://files.pythonhosted.org/packages/f4/90/e214b8b4aa34ed3d3f1040719c06c4d22472c40c5ef81a922d5af7876eb4/librt-0.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5cc22f7f5c0cc50ed69f4b15b9c51d602aabc4500b433aaa2ddd29e578f452f7", size = 29065, upload-time = "2025-11-29T14:01:34.088Z" },
- { url = "https://files.pythonhosted.org/packages/ab/90/ef61ed51f0a7770cc703422d907a757bbd8811ce820c333d3db2fd13542a/librt-0.6.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:14b345eb7afb61b9fdcdfda6738946bd11b8e0f6be258666b0646af3b9bb5916", size = 93703, upload-time = "2025-11-29T14:01:35.057Z" },
- { url = "https://files.pythonhosted.org/packages/a8/ae/c30bb119c35962cbe9a908a71da99c168056fc3f6e9bbcbc157d0b724d89/librt-0.6.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d46aa46aa29b067f0b8b84f448fd9719aaf5f4c621cc279164d76a9dc9ab3e8", size = 98890, upload-time = "2025-11-29T14:01:36.031Z" },
- { url = "https://files.pythonhosted.org/packages/d1/96/47a4a78d252d36f072b79d592df10600d379a895c3880c8cbd2ac699f0ad/librt-0.6.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b51ba7d9d5d9001494769eca8c0988adce25d0a970c3ba3f2eb9df9d08036fc", size = 98255, upload-time = "2025-11-29T14:01:37.058Z" },
- { url = "https://files.pythonhosted.org/packages/e5/28/779b5cc3cd9987683884eb5f5672e3251676bebaaae6b7da1cf366eb1da1/librt-0.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ced0925a18fddcff289ef54386b2fc230c5af3c83b11558571124bfc485b8c07", size = 100769, upload-time = "2025-11-29T14:01:38.413Z" },
- { url = "https://files.pythonhosted.org/packages/28/d7/771755e57c375cb9d25a4e106f570607fd856e2cb91b02418db1db954796/librt-0.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6bac97e51f66da2ca012adddbe9fd656b17f7368d439de30898f24b39512f40f", size = 98580, upload-time = "2025-11-29T14:01:39.459Z" },
- { url = "https://files.pythonhosted.org/packages/d0/ec/8b157eb8fbc066339a2f34b0aceb2028097d0ed6150a52e23284a311eafe/librt-0.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b2922a0e8fa97395553c304edc3bd36168d8eeec26b92478e292e5d4445c1ef0", size = 101706, upload-time = "2025-11-29T14:01:40.474Z" },
- { url = "https://files.pythonhosted.org/packages/82/a8/4aaead9a06c795a318282aebf7d3e3e578fa889ff396e1b640c3be4c7806/librt-0.6.3-cp314-cp314t-win32.whl", hash = "sha256:f33462b19503ba68d80dac8a1354402675849259fb3ebf53b67de86421735a3a", size = 19465, upload-time = "2025-11-29T14:01:41.77Z" },
- { url = "https://files.pythonhosted.org/packages/3a/61/b7e6a02746c1731670c19ba07d86da90b1ae45d29e405c0b5615abf97cde/librt-0.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:04f8ce401d4f6380cfc42af0f4e67342bf34c820dae01343f58f472dbac75dcf", size = 21042, upload-time = "2025-11-29T14:01:42.865Z" },
- { url = "https://files.pythonhosted.org/packages/0e/3d/72cc9ec90bb80b5b1a65f0bb74a0f540195837baaf3b98c7fa4a7aa9718e/librt-0.6.3-cp314-cp314t-win_arm64.whl", hash = "sha256:afb39550205cc5e5c935762c6bf6a2bb34f7d21a68eadb25e2db7bf3593fecc0", size = 20246, upload-time = "2025-11-29T14:01:44.13Z" },
+version = "0.7.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/56/04/79d8fcb43cae376c7adbab7b2b9f65e48432c9eced62ac96703bcc16e09b/librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63", size = 57472, upload-time = "2026-01-14T12:55:08.528Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/ba/60b96e93043d3d659da91752689023a73981336446ae82078cddf706249e/librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93", size = 58986, upload-time = "2026-01-14T12:55:09.466Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/26/5215e4cdcc26e7be7eee21955a7e13cbf1f6d7d7311461a6014544596fac/librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592", size = 168422, upload-time = "2026-01-14T12:55:10.499Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/84/e8d1bc86fa0159bfc24f3d798d92cafd3897e84c7fea7fe61b3220915d76/librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850", size = 177478, upload-time = "2026-01-14T12:55:11.577Z" },
+ { url = "https://files.pythonhosted.org/packages/57/11/d0268c4b94717a18aa91df1100e767b010f87b7ae444dafaa5a2d80f33a6/librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62", size = 192439, upload-time = "2026-01-14T12:55:12.7Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/56/1e8e833b95fe684f80f8894ae4d8b7d36acc9203e60478fcae599120a975/librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b", size = 191483, upload-time = "2026-01-14T12:55:13.838Z" },
+ { url = "https://files.pythonhosted.org/packages/17/48/f11cf28a2cb6c31f282009e2208312aa84a5ee2732859f7856ee306176d5/librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714", size = 185376, upload-time = "2026-01-14T12:55:15.017Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/6a/d7c116c6da561b9155b184354a60a3d5cdbf08fc7f3678d09c95679d13d9/librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449", size = 206234, upload-time = "2026-01-14T12:55:16.571Z" },
+ { url = "https://files.pythonhosted.org/packages/61/de/1975200bb0285fc921c5981d9978ce6ce11ae6d797df815add94a5a848a3/librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac", size = 44057, upload-time = "2026-01-14T12:55:18.077Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/cd/724f2d0b3461426730d4877754b65d39f06a41ac9d0a92d5c6840f72b9ae/librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708", size = 50293, upload-time = "2026-01-14T12:55:19.179Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/cf/7e899acd9ee5727ad8160fdcc9994954e79fab371c66535c60e13b968ffc/librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0", size = 43574, upload-time = "2026-01-14T12:55:20.185Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/fe/b1f9de2829cf7fc7649c1dcd202cfd873837c5cc2fc9e526b0e7f716c3d2/librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc", size = 57500, upload-time = "2026-01-14T12:55:21.219Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d4/4a60fbe2e53b825f5d9a77325071d61cd8af8506255067bf0c8527530745/librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2", size = 59019, upload-time = "2026-01-14T12:55:22.256Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/37/61ff80341ba5159afa524445f2d984c30e2821f31f7c73cf166dcafa5564/librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3", size = 169015, upload-time = "2026-01-14T12:55:23.24Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/86/13d4f2d6a93f181ebf2fc953868826653ede494559da8268023fe567fca3/librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6", size = 178161, upload-time = "2026-01-14T12:55:24.826Z" },
+ { url = "https://files.pythonhosted.org/packages/88/26/e24ef01305954fc4d771f1f09f3dd682f9eb610e1bec188ffb719374d26e/librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d", size = 193015, upload-time = "2026-01-14T12:55:26.04Z" },
+ { url = "https://files.pythonhosted.org/packages/88/a0/92b6bd060e720d7a31ed474d046a69bd55334ec05e9c446d228c4b806ae3/librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e", size = 192038, upload-time = "2026-01-14T12:55:27.208Z" },
+ { url = "https://files.pythonhosted.org/packages/06/bb/6f4c650253704279c3a214dad188101d1b5ea23be0606628bc6739456624/librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca", size = 186006, upload-time = "2026-01-14T12:55:28.594Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/00/1c409618248d43240cadf45f3efb866837fa77e9a12a71481912135eb481/librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93", size = 206888, upload-time = "2026-01-14T12:55:30.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/83/b2cfe8e76ff5c1c77f8a53da3d5de62d04b5ebf7cf913e37f8bca43b5d07/librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951", size = 44126, upload-time = "2026-01-14T12:55:31.44Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/0b/c59d45de56a51bd2d3a401fc63449c0ac163e4ef7f523ea8b0c0dee86ec5/librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34", size = 50262, upload-time = "2026-01-14T12:55:33.01Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" },
+ { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" },
+ { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" },
+ { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" },
+ { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" },
+ { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" },
+ { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" },
+ { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" },
+ { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" },
]
[[package]]
-name = "multidict"
-version = "6.5.0"
+name = "markdown"
+version = "3.10.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b7/b1/af95bcae8549f1f3fd70faacb29075826a0d689a27f232e8cee315efa053/markdown-3.10.1.tar.gz", hash = "sha256:1c19c10bd5c14ac948c53d0d762a04e2fa35a6d58a6b7b1e6bfcbe6fefc0001a", size = 365402, upload-time = "2026-01-21T18:09:28.206Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl", hash = "sha256:867d788939fe33e4b736426f5b9f651ad0c0ae0ecf89df0ca5d1176c70812fe3", size = 107684, upload-time = "2026-01-21T18:09:27.203Z" },
+]
+
+[[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, upload-time = "2025-09-27T18:37:40.426Z" }
+wheels = [
+ { 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, upload-time = "2025-09-27T18:36:30.854Z" },
+ { 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, upload-time = "2025-09-27T18:36:31.971Z" },
+ { 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, upload-time = "2025-09-27T18:36:32.813Z" },
+ { 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, upload-time = "2025-09-27T18:36:33.86Z" },
+ { 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, upload-time = "2025-09-27T18:36:35.099Z" },
+ { 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, upload-time = "2025-09-27T18:36:36.001Z" },
+ { 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, upload-time = "2025-09-27T18:36:36.906Z" },
+ { 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, upload-time = "2025-09-27T18:36:37.868Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
+ { 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, upload-time = "2025-09-27T18:36:41.777Z" },
+ { 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, upload-time = "2025-09-27T18:36:43.257Z" },
+ { 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, upload-time = "2025-09-27T18:36:44.508Z" },
+ { 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, upload-time = "2025-09-27T18:36:45.385Z" },
+ { 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, upload-time = "2025-09-27T18:36:46.916Z" },
+ { 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, upload-time = "2025-09-27T18:36:47.884Z" },
+ { 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, upload-time = "2025-09-27T18:36:48.82Z" },
+ { 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, upload-time = "2025-09-27T18:36:49.797Z" },
+ { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
+ { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
+ { 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, upload-time = "2025-09-27T18:36:54.819Z" },
+ { 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, upload-time = "2025-09-27T18:36:55.714Z" },
+ { 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, upload-time = "2025-09-27T18:36:56.908Z" },
+ { 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, upload-time = "2025-09-27T18:36:57.913Z" },
+ { 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, upload-time = "2025-09-27T18:36:58.833Z" },
+ { 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, upload-time = "2025-09-27T18:36:59.739Z" },
+ { 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, upload-time = "2025-09-27T18:37:00.719Z" },
+ { 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, upload-time = "2025-09-27T18:37:01.673Z" },
+ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
+ { 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, upload-time = "2025-09-27T18:37:06.342Z" },
+ { 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, upload-time = "2025-09-27T18:37:07.213Z" },
+ { 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, upload-time = "2025-09-27T18:37:09.572Z" },
+ { 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, upload-time = "2025-09-27T18:37:10.58Z" },
+ { 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, upload-time = "2025-09-27T18:37:11.547Z" },
+ { 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, upload-time = "2025-09-27T18:37:12.48Z" },
+ { 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, upload-time = "2025-09-27T18:37:13.485Z" },
+ { 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, upload-time = "2025-09-27T18:37:14.408Z" },
+ { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
+ { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
+ { 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, upload-time = "2025-09-27T18:37:18.453Z" },
+ { 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, upload-time = "2025-09-27T18:37:19.332Z" },
+ { 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, upload-time = "2025-09-27T18:37:20.245Z" },
+ { 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, upload-time = "2025-09-27T18:37:21.177Z" },
+ { 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, upload-time = "2025-09-27T18:37:22.167Z" },
+ { 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, upload-time = "2025-09-27T18:37:23.296Z" },
+ { 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, upload-time = "2025-09-27T18:37:24.237Z" },
+ { 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, upload-time = "2025-09-27T18:37:25.271Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
+]
+
+[[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, upload-time = "2021-02-05T18:55:30.623Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" },
+]
+
+[[package]]
+name = "mkdocs"
+version = "1.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { 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, upload-time = "2024-08-30T12:24:06.899Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" },
+]
+
+[[package]]
+name = "mkdocs-autorefs"
+version = "1.4.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown" },
+ { name = "markupsafe" },
+ { name = "mkdocs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/51/fa/9124cd63d822e2bcbea1450ae68cdc3faf3655c69b455f3a7ed36ce6c628/mkdocs_autorefs-1.4.3.tar.gz", hash = "sha256:beee715b254455c4aa93b6ef3c67579c399ca092259cc41b7d9342573ff1fc75", size = 55425, upload-time = "2025-08-26T14:23:17.223Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/4d/7123b6fa2278000688ebd338e2a06d16870aaf9eceae6ba047ea05f92df1/mkdocs_autorefs-1.4.3-py3-none-any.whl", hash = "sha256:469d85eb3114801d08e9cc55d102b3ba65917a869b893403b8987b601cf55dc9", size = 25034, upload-time = "2025-08-26T14:23:15.906Z" },
+]
+
+[[package]]
+name = "mkdocs-get-deps"
+version = "0.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mergedeep" },
+ { name = "platformdirs" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" },
+]
+
+[[package]]
+name = "mkdocs-material"
+version = "9.7.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "babel" },
+ { name = "backrefs" },
+ { 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/27/e2/2ffc356cd72f1473d07c7719d82a8f2cbd261666828614ecb95b12169f41/mkdocs_material-9.7.1.tar.gz", hash = "sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8", size = 4094392, upload-time = "2025-12-18T09:49:00.308Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-py3-none-any.whl", hash = "sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c", size = 9297166, upload-time = "2025-12-18T09:48:56.664Z" },
+]
+
+[[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, upload-time = "2023-11-22T19:09:45.208Z" }
+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, upload-time = "2023-11-22T19:09:43.465Z" },
+]
+
+[[package]]
+name = "mkdocstrings"
+version = "1.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/46/b5/59f27b4ce9951a4bce56b88ba5ff5159486797ab18863f2b4c1c5e8465bd/multidict-6.5.0.tar.gz", hash = "sha256:942bd8002492ba819426a8d7aefde3189c1b87099cdf18aaaefefcf7f3f7b6d2", size = 98512, upload-time = "2025-06-17T14:15:56.556Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8b/88/f8354ef1cb1121234c3461ff3d11eac5f4fe115f00552d3376306275c9ab/multidict-6.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e118a202904623b1d2606d1c8614e14c9444b59d64454b0c355044058066469", size = 73858, upload-time = "2025-06-17T14:13:21.451Z" },
- { url = "https://files.pythonhosted.org/packages/49/04/634b49c7abe71bd1c61affaeaa0c2a46b6be8d599a07b495259615dbdfe0/multidict-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a42995bdcaff4e22cb1280ae7752c3ed3fbb398090c6991a2797a4a0e5ed16a9", size = 43186, upload-time = "2025-06-17T14:13:23.615Z" },
- { url = "https://files.pythonhosted.org/packages/3b/ff/091ff4830ec8f96378578bfffa7f324a9dd16f60274cec861ae65ba10be3/multidict-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2261b538145723ca776e55208640fffd7ee78184d223f37c2b40b9edfe0e818a", size = 43031, upload-time = "2025-06-17T14:13:24.725Z" },
- { url = "https://files.pythonhosted.org/packages/10/c1/1b4137845f8b8dbc2332af54e2d7761c6a29c2c33c8d47a0c8c70676bac1/multidict-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e5b19f8cd67235fab3e195ca389490415d9fef5a315b1fa6f332925dc924262", size = 233588, upload-time = "2025-06-17T14:13:26.181Z" },
- { url = "https://files.pythonhosted.org/packages/c3/77/cbe9a1f58c6d4f822663788e414637f256a872bc352cedbaf7717b62db58/multidict-6.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:177b081e4dec67c3320b16b3aa0babc178bbf758553085669382c7ec711e1ec8", size = 222714, upload-time = "2025-06-17T14:13:27.482Z" },
- { url = "https://files.pythonhosted.org/packages/6c/37/39e1142c2916973818515adc13bbdb68d3d8126935e3855200e059a79bab/multidict-6.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d30a2cc106a7d116b52ee046207614db42380b62e6b1dd2a50eba47c5ca5eb1", size = 242741, upload-time = "2025-06-17T14:13:28.92Z" },
- { url = "https://files.pythonhosted.org/packages/a3/aa/60c3ef0c87ccad3445bf01926a1b8235ee24c3dde483faef1079cc91706d/multidict-6.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a72933bc308d7a64de37f0d51795dbeaceebdfb75454f89035cdfc6a74cfd129", size = 235008, upload-time = "2025-06-17T14:13:30.587Z" },
- { url = "https://files.pythonhosted.org/packages/bf/5e/f7e0fd5f5b8a7b9a75b0f5642ca6b6dde90116266920d8cf63b513f3908b/multidict-6.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d109e663d032280ef8ef62b50924b2e887d5ddf19e301844a6cb7e91a172a6", size = 226627, upload-time = "2025-06-17T14:13:31.831Z" },
- { url = "https://files.pythonhosted.org/packages/b7/74/1bc0a3c6a9105051f68a6991fe235d7358836e81058728c24d5bbdd017cb/multidict-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b555329c9894332401f03b9a87016f0b707b6fccd4706793ec43b4a639e75869", size = 228232, upload-time = "2025-06-17T14:13:33.402Z" },
- { url = "https://files.pythonhosted.org/packages/99/e7/37118291cdc31f4cc680d54047cdea9b520e9a724a643919f71f8c2a2aeb/multidict-6.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6994bad9d471ef2156f2b6850b51e20ee409c6b9deebc0e57be096be9faffdce", size = 246616, upload-time = "2025-06-17T14:13:34.964Z" },
- { url = "https://files.pythonhosted.org/packages/ff/89/e2c08d6bdb21a1a55be4285510d058ace5f5acabe6b57900432e863d4c70/multidict-6.5.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b15f817276c96cde9060569023808eec966bd8da56a97e6aa8116f34ddab6534", size = 235007, upload-time = "2025-06-17T14:13:36.428Z" },
- { url = "https://files.pythonhosted.org/packages/89/1e/e39a98e8e1477ec7a871b3c17265658fbe6d617048059ae7fa5011b224f3/multidict-6.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b4bf507c991db535a935b2127cf057a58dbc688c9f309c72080795c63e796f58", size = 244824, upload-time = "2025-06-17T14:13:37.982Z" },
- { url = "https://files.pythonhosted.org/packages/a3/ba/63e11edd45c31e708c5a1904aa7ac4de01e13135a04cfe96bc71eb359b85/multidict-6.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:60c3f8f13d443426c55f88cf3172547bbc600a86d57fd565458b9259239a6737", size = 257229, upload-time = "2025-06-17T14:13:39.554Z" },
- { url = "https://files.pythonhosted.org/packages/0f/00/bdcceb6af424936adfc8b92a79d3a95863585f380071393934f10a63f9e3/multidict-6.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a10227168a24420c158747fc201d4279aa9af1671f287371597e2b4f2ff21879", size = 247118, upload-time = "2025-06-17T14:13:40.795Z" },
- { url = "https://files.pythonhosted.org/packages/b6/a0/4aa79e991909cca36ca821a9ba5e8e81e4cd5b887c81f89ded994e0f49df/multidict-6.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e3b1425fe54ccfde66b8cfb25d02be34d5dfd2261a71561ffd887ef4088b4b69", size = 243948, upload-time = "2025-06-17T14:13:42.477Z" },
- { url = "https://files.pythonhosted.org/packages/21/8b/e45e19ce43afb31ff6b0fd5d5816b4fcc1fcc2f37e8a82aefae06c40c7a6/multidict-6.5.0-cp310-cp310-win32.whl", hash = "sha256:b4e47ef51237841d1087e1e1548071a6ef22e27ed0400c272174fa585277c4b4", size = 40433, upload-time = "2025-06-17T14:13:43.972Z" },
- { url = "https://files.pythonhosted.org/packages/d2/6e/96e0ba4601343d9344e69503fca072ace19c35f7d4ca3d68401e59acdc8f/multidict-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:63b3b24fadc7067282c88fae5b2f366d5b3a7c15c021c2838de8c65a50eeefb4", size = 44423, upload-time = "2025-06-17T14:13:44.991Z" },
- { url = "https://files.pythonhosted.org/packages/eb/4a/9befa919d7a390f13a5511a69282b7437782071160c566de6e0ebf712c9f/multidict-6.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:8b2d61afbafc679b7eaf08e9de4fa5d38bd5dc7a9c0a577c9f9588fb49f02dbb", size = 41481, upload-time = "2025-06-17T14:13:49.389Z" },
- { url = "https://files.pythonhosted.org/packages/75/ba/484f8e96ee58ec4fef42650eb9dbbedb24f9bc155780888398a4725d2270/multidict-6.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8b4bf6bb15a05796a07a248084e3e46e032860c899c7a9b981030e61368dba95", size = 73283, upload-time = "2025-06-17T14:13:50.406Z" },
- { url = "https://files.pythonhosted.org/packages/71/48/01d62ea6199d76934c87746695b3ed16aeedfdd564e8d89184577037baac/multidict-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46bb05d50219655c42a4b8fcda9c7ee658a09adbb719c48e65a20284e36328ea", size = 42937, upload-time = "2025-06-17T14:13:51.45Z" },
- { url = "https://files.pythonhosted.org/packages/da/cf/bb462d920f26d9e2e0aff8a78aeb06af1225b826e9a5468870c57591910a/multidict-6.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54f524d73f4d54e87e03c98f6af601af4777e4668a52b1bd2ae0a4d6fc7b392b", size = 42748, upload-time = "2025-06-17T14:13:52.505Z" },
- { url = "https://files.pythonhosted.org/packages/cd/b1/d5c11ea0fdad68d3ed45f0e2527de6496d2fac8afe6b8ca6d407c20ad00f/multidict-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529b03600466480ecc502000d62e54f185a884ed4570dee90d9a273ee80e37b5", size = 236448, upload-time = "2025-06-17T14:13:53.562Z" },
- { url = "https://files.pythonhosted.org/packages/fc/69/c3ceb264994f5b338c812911a8d660084f37779daef298fc30bd817f75c7/multidict-6.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69ad681ad7c93a41ee7005cc83a144b5b34a3838bcf7261e2b5356057b0f78de", size = 228695, upload-time = "2025-06-17T14:13:54.775Z" },
- { url = "https://files.pythonhosted.org/packages/81/3d/c23dcc0d34a35ad29974184db2878021d28fe170ecb9192be6bfee73f1f2/multidict-6.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fe9fada8bc0839466b09fa3f6894f003137942984843ec0c3848846329a36ae", size = 247434, upload-time = "2025-06-17T14:13:56.039Z" },
- { url = "https://files.pythonhosted.org/packages/06/b3/06cf7a049129ff52525a859277abb5648e61d7afae7fb7ed02e3806be34e/multidict-6.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f94c6ea6405fcf81baef1e459b209a78cda5442e61b5b7a57ede39d99b5204a0", size = 239431, upload-time = "2025-06-17T14:13:57.33Z" },
- { url = "https://files.pythonhosted.org/packages/8a/72/b2fe2fafa23af0c6123aebe23b4cd23fdad01dfe7009bb85624e4636d0dd/multidict-6.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca75ad8a39ed75f079a8931435a5b51ee4c45d9b32e1740f99969a5d1cc2ee", size = 231542, upload-time = "2025-06-17T14:13:58.597Z" },
- { url = "https://files.pythonhosted.org/packages/a1/c9/a52ca0a342a02411a31b6af197a6428a5137d805293f10946eeab614ec06/multidict-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4c08f3a2a6cc42b414496017928d95898964fed84b1b2dace0c9ee763061f9", size = 233069, upload-time = "2025-06-17T14:13:59.834Z" },
- { url = "https://files.pythonhosted.org/packages/9b/55/a3328a3929b8e131e2678d5e65f552b0a6874fab62123e31f5a5625650b0/multidict-6.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:046a7540cfbb4d5dc846a1fd9843f3ba980c6523f2e0c5b8622b4a5c94138ae6", size = 250596, upload-time = "2025-06-17T14:14:01.178Z" },
- { url = "https://files.pythonhosted.org/packages/6c/b8/aa3905a38a8287013aeb0a54c73f79ccd8b32d2f1d53e5934643a36502c2/multidict-6.5.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:64306121171d988af77d74be0d8c73ee1a69cf6f96aea7fa6030c88f32a152dd", size = 237858, upload-time = "2025-06-17T14:14:03.232Z" },
- { url = "https://files.pythonhosted.org/packages/d3/eb/f11d5af028014f402e5dd01ece74533964fa4e7bfae4af4824506fa8c398/multidict-6.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b4ac1dd5eb0ecf6f7351d5a9137f30a83f7182209c5d37f61614dfdce5714853", size = 249175, upload-time = "2025-06-17T14:14:04.561Z" },
- { url = "https://files.pythonhosted.org/packages/ac/57/d451905a62e5ef489cb4f92e8190d34ac5329427512afd7f893121da4e96/multidict-6.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bab4a8337235365f4111a7011a1f028826ca683834ebd12de4b85e2844359c36", size = 259532, upload-time = "2025-06-17T14:14:05.798Z" },
- { url = "https://files.pythonhosted.org/packages/d3/90/ff82b5ac5cabe3c79c50cf62a62f3837905aa717e67b6b4b7872804f23c8/multidict-6.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a05b5604c5a75df14a63eeeca598d11b2c3745b9008539b70826ea044063a572", size = 250554, upload-time = "2025-06-17T14:14:07.382Z" },
- { url = "https://files.pythonhosted.org/packages/d5/5a/0cabc50d4bc16e61d8b0a8a74499a1409fa7b4ef32970b7662a423781fc7/multidict-6.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:67c4a640952371c9ca65b6a710598be246ef3be5ca83ed38c16a7660d3980877", size = 248159, upload-time = "2025-06-17T14:14:08.65Z" },
- { url = "https://files.pythonhosted.org/packages/c0/1d/adeabae0771544f140d9f42ab2c46eaf54e793325999c36106078b7f6600/multidict-6.5.0-cp311-cp311-win32.whl", hash = "sha256:fdeae096ca36c12d8aca2640b8407a9d94e961372c68435bef14e31cce726138", size = 40357, upload-time = "2025-06-17T14:14:09.91Z" },
- { url = "https://files.pythonhosted.org/packages/e1/fe/bbd85ae65c96de5c9910c332ee1f4b7be0bf0fb21563895167bcb6502a1f/multidict-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e2977ef8b7ce27723ee8c610d1bd1765da4f3fbe5a64f9bf1fd3b4770e31fbc0", size = 44432, upload-time = "2025-06-17T14:14:11.013Z" },
- { url = "https://files.pythonhosted.org/packages/96/af/f9052d9c4e65195b210da9f7afdea06d3b7592b3221cc0ef1b407f762faa/multidict-6.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:82d0cf0ea49bae43d9e8c3851e21954eff716259ff42da401b668744d1760bcb", size = 41408, upload-time = "2025-06-17T14:14:12.112Z" },
- { url = "https://files.pythonhosted.org/packages/0a/fa/18f4950e00924f7e84c8195f4fc303295e14df23f713d64e778b8fa8b903/multidict-6.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1bb986c8ea9d49947bc325c51eced1ada6d8d9b4c5b15fd3fcdc3c93edef5a74", size = 73474, upload-time = "2025-06-17T14:14:13.528Z" },
- { url = "https://files.pythonhosted.org/packages/6c/66/0392a2a8948bccff57e4793c9dde3e5c088f01e8b7f8867ee58a2f187fc5/multidict-6.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:03c0923da300120830fc467e23805d63bbb4e98b94032bd863bc7797ea5fa653", size = 43741, upload-time = "2025-06-17T14:14:15.188Z" },
- { url = "https://files.pythonhosted.org/packages/98/3e/f48487c91b2a070566cfbab876d7e1ebe7deb0a8002e4e896a97998ae066/multidict-6.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4c78d5ec00fdd35c91680ab5cf58368faad4bd1a8721f87127326270248de9bc", size = 42143, upload-time = "2025-06-17T14:14:16.612Z" },
- { url = "https://files.pythonhosted.org/packages/3f/49/439c6cc1cd00365cf561bdd3579cc3fa1a0d38effb3a59b8d9562839197f/multidict-6.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadc3cb78be90a887f8f6b73945b840da44b4a483d1c9750459ae69687940c97", size = 239303, upload-time = "2025-06-17T14:14:17.707Z" },
- { url = "https://files.pythonhosted.org/packages/c4/24/491786269e90081cb536e4d7429508725bc92ece176d1204a4449de7c41c/multidict-6.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b02e1ca495d71e07e652e4cef91adae3bf7ae4493507a263f56e617de65dafc", size = 236913, upload-time = "2025-06-17T14:14:18.981Z" },
- { url = "https://files.pythonhosted.org/packages/e8/76/bbe2558b820ebeca8a317ab034541790e8160ca4b1e450415383ac69b339/multidict-6.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fe92a62326eef351668eec4e2dfc494927764a0840a1895cff16707fceffcd3", size = 250752, upload-time = "2025-06-17T14:14:20.297Z" },
- { url = "https://files.pythonhosted.org/packages/3e/e3/3977f2c1123f553ceff9f53cd4de04be2c1912333c6fabbcd51531655476/multidict-6.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7673ee4f63879ecd526488deb1989041abcb101b2d30a9165e1e90c489f3f7fb", size = 243937, upload-time = "2025-06-17T14:14:21.935Z" },
- { url = "https://files.pythonhosted.org/packages/b6/b8/7a6e9c13c79709cdd2f22ee849f058e6da76892d141a67acc0e6c30d845c/multidict-6.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa097ae2a29f573de7e2d86620cbdda5676d27772d4ed2669cfa9961a0d73955", size = 237419, upload-time = "2025-06-17T14:14:23.215Z" },
- { url = "https://files.pythonhosted.org/packages/84/9d/8557f5e88da71bc7e7a8ace1ada4c28197f3bfdc2dd6e51d3b88f2e16e8e/multidict-6.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:300da0fa4f8457d9c4bd579695496116563409e676ac79b5e4dca18e49d1c308", size = 237222, upload-time = "2025-06-17T14:14:24.516Z" },
- { url = "https://files.pythonhosted.org/packages/a3/3b/8f023ad60e7969cb6bc0683738d0e1618f5ff5723d6d2d7818dc6df6ad3d/multidict-6.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9a19bd108c35877b57393243d392d024cfbfdefe759fd137abb98f6fc910b64c", size = 247861, upload-time = "2025-06-17T14:14:25.839Z" },
- { url = "https://files.pythonhosted.org/packages/af/1c/9cf5a099ce7e3189906cf5daa72c44ee962dcb4c1983659f3a6f8a7446ab/multidict-6.5.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f32a1777465a35c35ddbbd7fc1293077938a69402fcc59e40b2846d04a120dd", size = 243917, upload-time = "2025-06-17T14:14:27.164Z" },
- { url = "https://files.pythonhosted.org/packages/6c/bb/88ee66ebeef56868044bac58feb1cc25658bff27b20e3cfc464edc181287/multidict-6.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9cc1e10c14ce8112d1e6d8971fe3cdbe13e314f68bea0e727429249d4a6ce164", size = 249214, upload-time = "2025-06-17T14:14:28.795Z" },
- { url = "https://files.pythonhosted.org/packages/3e/ec/a90e88cc4a1309f33088ab1cdd5c0487718f49dfb82c5ffc845bb17c1973/multidict-6.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e95c5e07a06594bdc288117ca90e89156aee8cb2d7c330b920d9c3dd19c05414", size = 258682, upload-time = "2025-06-17T14:14:30.066Z" },
- { url = "https://files.pythonhosted.org/packages/d2/d8/16dd69a6811920a31f4e06114ebe67b1cd922c8b05c9c82b050706d0b6fe/multidict-6.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:40ff26f58323795f5cd2855e2718a1720a1123fb90df4553426f0efd76135462", size = 254254, upload-time = "2025-06-17T14:14:31.323Z" },
- { url = "https://files.pythonhosted.org/packages/ac/a8/90193a5f5ca1bdbf92633d69a25a2ef9bcac7b412b8d48c84d01a2732518/multidict-6.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76803a29fd71869a8b59c2118c9dcfb3b8f9c8723e2cce6baeb20705459505cf", size = 247741, upload-time = "2025-06-17T14:14:32.717Z" },
- { url = "https://files.pythonhosted.org/packages/cd/43/29c7a747153c05b41d1f67455426af39ed88d6de3f21c232b8f2724bde13/multidict-6.5.0-cp312-cp312-win32.whl", hash = "sha256:df7ecbc65a53a2ce1b3a0c82e6ad1a43dcfe7c6137733f9176a92516b9f5b851", size = 41049, upload-time = "2025-06-17T14:14:33.941Z" },
- { url = "https://files.pythonhosted.org/packages/1e/e8/8f3fc32b7e901f3a2719764d64aeaf6ae77b4ba961f1c3a3cf3867766636/multidict-6.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ec1c3fbbb0b655a6540bce408f48b9a7474fd94ed657dcd2e890671fefa7743", size = 44700, upload-time = "2025-06-17T14:14:35.016Z" },
- { url = "https://files.pythonhosted.org/packages/24/e4/e250806adc98d524d41e69c8d4a42bc3513464adb88cb96224df12928617/multidict-6.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:2d24a00d34808b22c1f15902899b9d82d0faeca9f56281641c791d8605eacd35", size = 41703, upload-time = "2025-06-17T14:14:36.168Z" },
- { url = "https://files.pythonhosted.org/packages/1a/c9/092c4e9402b6d16de761cff88cb842a5c8cc50ccecaf9c4481ba53264b9e/multidict-6.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:53d92df1752df67a928fa7f884aa51edae6f1cf00eeb38cbcf318cf841c17456", size = 73486, upload-time = "2025-06-17T14:14:37.238Z" },
- { url = "https://files.pythonhosted.org/packages/08/f9/6f7ddb8213f5fdf4db48d1d640b78e8aef89b63a5de8a2313286db709250/multidict-6.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:680210de2c38eef17ce46b8df8bf2c1ece489261a14a6e43c997d49843a27c99", size = 43745, upload-time = "2025-06-17T14:14:38.32Z" },
- { url = "https://files.pythonhosted.org/packages/f3/a7/b9be0163bfeee3bb08a77a1705e24eb7e651d594ea554107fac8a1ca6a4d/multidict-6.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e279259bcb936732bfa1a8eec82b5d2352b3df69d2fa90d25808cfc403cee90a", size = 42135, upload-time = "2025-06-17T14:14:39.897Z" },
- { url = "https://files.pythonhosted.org/packages/8e/30/93c8203f943a417bda3c573a34d5db0cf733afdfffb0ca78545c7716dbd8/multidict-6.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c185fc1069781e3fc8b622c4331fb3b433979850392daa5efbb97f7f9959bb", size = 238585, upload-time = "2025-06-17T14:14:41.332Z" },
- { url = "https://files.pythonhosted.org/packages/9d/fe/2582b56a1807604774f566eeef183b0d6b148f4b89d1612cd077567b2e1e/multidict-6.5.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6bb5f65ff91daf19ce97f48f63585e51595539a8a523258b34f7cef2ec7e0617", size = 236174, upload-time = "2025-06-17T14:14:42.602Z" },
- { url = "https://files.pythonhosted.org/packages/9b/c4/d8b66d42d385bd4f974cbd1eaa8b265e6b8d297249009f312081d5ded5c7/multidict-6.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8646b4259450c59b9286db280dd57745897897284f6308edbdf437166d93855", size = 250145, upload-time = "2025-06-17T14:14:43.944Z" },
- { url = "https://files.pythonhosted.org/packages/bc/64/62feda5093ee852426aae3df86fab079f8bf1cdbe403e1078c94672ad3ec/multidict-6.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d245973d4ecc04eea0a8e5ebec7882cf515480036e1b48e65dffcfbdf86d00be", size = 243470, upload-time = "2025-06-17T14:14:45.343Z" },
- { url = "https://files.pythonhosted.org/packages/67/dc/9f6fa6e854625cf289c0e9f4464b40212a01f76b2f3edfe89b6779b4fb93/multidict-6.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a133e7ddc9bc7fb053733d0ff697ce78c7bf39b5aec4ac12857b6116324c8d75", size = 236968, upload-time = "2025-06-17T14:14:46.609Z" },
- { url = "https://files.pythonhosted.org/packages/46/ae/4b81c6e3745faee81a156f3f87402315bdccf04236f75c03e37be19c94ff/multidict-6.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80d696fa38d738fcebfd53eec4d2e3aeb86a67679fd5e53c325756682f152826", size = 236575, upload-time = "2025-06-17T14:14:47.929Z" },
- { url = "https://files.pythonhosted.org/packages/8a/fa/4089d7642ea344226e1bfab60dd588761d4791754f8072e911836a39bedf/multidict-6.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:20d30c9410ac3908abbaa52ee5967a754c62142043cf2ba091e39681bd51d21a", size = 247632, upload-time = "2025-06-17T14:14:49.525Z" },
- { url = "https://files.pythonhosted.org/packages/16/ee/a353dac797de0f28fb7f078cc181c5f2eefe8dd16aa11a7100cbdc234037/multidict-6.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c65068cc026f217e815fa519d8e959a7188e94ec163ffa029c94ca3ef9d4a73", size = 243520, upload-time = "2025-06-17T14:14:50.83Z" },
- { url = "https://files.pythonhosted.org/packages/50/ec/560deb3d2d95822d6eb1bcb1f1cb728f8f0197ec25be7c936d5d6a5d133c/multidict-6.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e355ac668a8c3e49c2ca8daa4c92f0ad5b705d26da3d5af6f7d971e46c096da7", size = 248551, upload-time = "2025-06-17T14:14:52.229Z" },
- { url = "https://files.pythonhosted.org/packages/10/85/ddf277e67c78205f6695f2a7639be459bca9cc353b962fd8085a492a262f/multidict-6.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:08db204213d0375a91a381cae0677ab95dd8c67a465eb370549daf6dbbf8ba10", size = 258362, upload-time = "2025-06-17T14:14:53.934Z" },
- { url = "https://files.pythonhosted.org/packages/02/fc/d64ee1df9b87c5210f2d4c419cab07f28589c81b4e5711eda05a122d0614/multidict-6.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ffa58e3e215af8f6536dc837a990e456129857bb6fd546b3991be470abd9597a", size = 253862, upload-time = "2025-06-17T14:14:55.323Z" },
- { url = "https://files.pythonhosted.org/packages/c9/7c/a2743c00d9e25f4826d3a77cc13d4746398872cf21c843eef96bb9945665/multidict-6.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e86eb90015c6f21658dbd257bb8e6aa18bdb365b92dd1fba27ec04e58cdc31b", size = 247391, upload-time = "2025-06-17T14:14:57.293Z" },
- { url = "https://files.pythonhosted.org/packages/9b/03/7773518db74c442904dbd349074f1e7f2a854cee4d9529fc59e623d3949e/multidict-6.5.0-cp313-cp313-win32.whl", hash = "sha256:f34a90fbd9959d0f857323bd3c52b3e6011ed48f78d7d7b9e04980b8a41da3af", size = 41115, upload-time = "2025-06-17T14:14:59.33Z" },
- { url = "https://files.pythonhosted.org/packages/eb/9a/6fc51b1dc11a7baa944bc101a92167d8b0f5929d376a8c65168fc0d35917/multidict-6.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fcb2aa79ac6aef8d5b709bbfc2fdb1d75210ba43038d70fbb595b35af470ce06", size = 44768, upload-time = "2025-06-17T14:15:00.427Z" },
- { url = "https://files.pythonhosted.org/packages/82/2d/0d010be24b663b3c16e3d3307bbba2de5ae8eec496f6027d5c0515b371a8/multidict-6.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:6dcee5e7e92060b4bb9bb6f01efcbb78c13d0e17d9bc6eec71660dd71dc7b0c2", size = 41770, upload-time = "2025-06-17T14:15:01.854Z" },
- { url = "https://files.pythonhosted.org/packages/aa/d1/a71711a5f32f84b7b036e82182e3250b949a0ce70d51a2c6a4079e665449/multidict-6.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:cbbc88abea2388fde41dd574159dec2cda005cb61aa84950828610cb5010f21a", size = 80450, upload-time = "2025-06-17T14:15:02.968Z" },
- { url = "https://files.pythonhosted.org/packages/0f/a2/953a9eede63a98fcec2c1a2c1a0d88de120056219931013b871884f51b43/multidict-6.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70b599f70ae6536e5976364d3c3cf36f40334708bd6cebdd1e2438395d5e7676", size = 46971, upload-time = "2025-06-17T14:15:04.149Z" },
- { url = "https://files.pythonhosted.org/packages/44/61/60250212953459edda2c729e1d85130912f23c67bd4f585546fe4bdb1578/multidict-6.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:828bab777aa8d29d59700018178061854e3a47727e0611cb9bec579d3882de3b", size = 45548, upload-time = "2025-06-17T14:15:05.666Z" },
- { url = "https://files.pythonhosted.org/packages/11/b6/e78ee82e96c495bc2582b303f68bed176b481c8d81a441fec07404fce2ca/multidict-6.5.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9695fc1462f17b131c111cf0856a22ff154b0480f86f539d24b2778571ff94d", size = 238545, upload-time = "2025-06-17T14:15:06.88Z" },
- { url = "https://files.pythonhosted.org/packages/5a/0f/6132ca06670c8d7b374c3a4fd1ba896fc37fbb66b0de903f61db7d1020ec/multidict-6.5.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b5ac6ebaf5d9814b15f399337ebc6d3a7f4ce9331edd404e76c49a01620b68d", size = 229931, upload-time = "2025-06-17T14:15:08.24Z" },
- { url = "https://files.pythonhosted.org/packages/c0/63/d9957c506e6df6b3e7a194f0eea62955c12875e454b978f18262a65d017b/multidict-6.5.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84a51e3baa77ded07be4766a9e41d977987b97e49884d4c94f6d30ab6acaee14", size = 248181, upload-time = "2025-06-17T14:15:09.907Z" },
- { url = "https://files.pythonhosted.org/packages/43/3f/7d5490579640db5999a948e2c41d4a0efd91a75989bda3e0a03a79c92be2/multidict-6.5.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8de67f79314d24179e9b1869ed15e88d6ba5452a73fc9891ac142e0ee018b5d6", size = 241846, upload-time = "2025-06-17T14:15:11.596Z" },
- { url = "https://files.pythonhosted.org/packages/e1/f7/252b1ce949ece52bba4c0de7aa2e3a3d5964e800bce71fb778c2e6c66f7c/multidict-6.5.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17f78a52c214481d30550ec18208e287dfc4736f0c0148208334b105fd9e0887", size = 232893, upload-time = "2025-06-17T14:15:12.946Z" },
- { url = "https://files.pythonhosted.org/packages/45/7e/0070bfd48c16afc26e056f2acce49e853c0d604a69c7124bc0bbdb1bcc0a/multidict-6.5.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2966d0099cb2e2039f9b0e73e7fd5eb9c85805681aa2a7f867f9d95b35356921", size = 228567, upload-time = "2025-06-17T14:15:14.267Z" },
- { url = "https://files.pythonhosted.org/packages/2a/31/90551c75322113ebf5fd9c5422e8641d6952f6edaf6b6c07fdc49b1bebdd/multidict-6.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:86fb42ed5ed1971c642cc52acc82491af97567534a8e381a8d50c02169c4e684", size = 246188, upload-time = "2025-06-17T14:15:15.985Z" },
- { url = "https://files.pythonhosted.org/packages/cc/e2/aa4b02a55e7767ff292871023817fe4db83668d514dab7ccbce25eaf7659/multidict-6.5.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:4e990cbcb6382f9eae4ec720bcac6a1351509e6fc4a5bb70e4984b27973934e6", size = 235178, upload-time = "2025-06-17T14:15:17.395Z" },
- { url = "https://files.pythonhosted.org/packages/7d/5c/f67e726717c4b138b166be1700e2b56e06fbbcb84643d15f9a9d7335ff41/multidict-6.5.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d99a59d64bb1f7f2117bec837d9e534c5aeb5dcedf4c2b16b9753ed28fdc20a3", size = 243422, upload-time = "2025-06-17T14:15:18.939Z" },
- { url = "https://files.pythonhosted.org/packages/e5/1c/15fa318285e26a50aa3fa979bbcffb90f9b4d5ec58882d0590eda067d0da/multidict-6.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:e8ef15cc97c9890212e1caf90f0d63f6560e1e101cf83aeaf63a57556689fb34", size = 254898, upload-time = "2025-06-17T14:15:20.31Z" },
- { url = "https://files.pythonhosted.org/packages/ad/3d/d6c6d1c2e9b61ca80313912d30bb90d4179335405e421ef0a164eac2c0f9/multidict-6.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:b8a09aec921b34bd8b9f842f0bcfd76c6a8c033dc5773511e15f2d517e7e1068", size = 247129, upload-time = "2025-06-17T14:15:21.665Z" },
- { url = "https://files.pythonhosted.org/packages/29/15/1568258cf0090bfa78d44be66247cfdb16e27dfd935c8136a1e8632d3057/multidict-6.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff07b504c23b67f2044533244c230808a1258b3493aaf3ea2a0785f70b7be461", size = 243841, upload-time = "2025-06-17T14:15:23.38Z" },
- { url = "https://files.pythonhosted.org/packages/65/57/64af5dbcfd61427056e840c8e520b502879d480f9632fbe210929fd87393/multidict-6.5.0-cp313-cp313t-win32.whl", hash = "sha256:9232a117341e7e979d210e41c04e18f1dc3a1d251268df6c818f5334301274e1", size = 46761, upload-time = "2025-06-17T14:15:24.733Z" },
- { url = "https://files.pythonhosted.org/packages/26/a8/cac7f7d61e188ff44f28e46cb98f9cc21762e671c96e031f06c84a60556e/multidict-6.5.0-cp313-cp313t-win_amd64.whl", hash = "sha256:44cb5c53fb2d4cbcee70a768d796052b75d89b827643788a75ea68189f0980a1", size = 52112, upload-time = "2025-06-17T14:15:25.906Z" },
- { url = "https://files.pythonhosted.org/packages/51/9f/076533feb1b5488d22936da98b9c217205cfbf9f56f7174e8c5c86d86fe6/multidict-6.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:51d33fafa82640c0217391d4ce895d32b7e84a832b8aee0dcc1b04d8981ec7f4", size = 44358, upload-time = "2025-06-17T14:15:27.117Z" },
- { url = "https://files.pythonhosted.org/packages/44/d8/45e8fc9892a7386d074941429e033adb4640e59ff0780d96a8cf46fe788e/multidict-6.5.0-py3-none-any.whl", hash = "sha256:5634b35f225977605385f56153bd95a7133faffc0ffe12ad26e10517537e8dfc", size = 12181, upload-time = "2025-06-17T14:15:55.156Z" },
+ { name = "jinja2" },
+ { name = "markdown" },
+ { name = "markupsafe" },
+ { name = "mkdocs" },
+ { name = "mkdocs-autorefs" },
+ { name = "pymdown-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/63/4d/1ca8a9432579184599714aaeb36591414cc3d3bfd9d494f6db540c995ae4/mkdocstrings-1.0.2.tar.gz", hash = "sha256:48edd0ccbcb9e30a3121684e165261a9d6af4d63385fc4f39a54a49ac3b32ea8", size = 101048, upload-time = "2026-01-24T15:57:25.735Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/57/32/407a9a5fdd7d8ecb4af8d830b9bcdf47ea68f916869b3f44bac31f081250/mkdocstrings-1.0.2-py3-none-any.whl", hash = "sha256:41897815a8026c3634fe5d51472c3a569f92ded0ad8c7a640550873eea3b6817", size = 35443, upload-time = "2026-01-24T15:57:23.933Z" },
+]
+
+[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, upload-time = "2025-12-03T14:26:11.755Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/81/06/c5f8deba7d2cbdfa7967a716ae801aa9ca5f734b8f54fd473ef77a088dbe/mkdocstrings_python-2.0.1-py3-none-any.whl", hash = "sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90", size = 105055, upload-time = "2025-12-03T14:26:10.184Z" },
+]
+
+[[package]]
+name = "multidict"
+version = "6.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" },
+ { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" },
+ { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" },
+ { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" },
+ { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" },
+ { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" },
+ { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" },
+ { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" },
+ { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" },
+ { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135, upload-time = "2025-10-06T14:49:54.26Z" },
+ { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117, upload-time = "2025-10-06T14:49:55.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472, upload-time = "2025-10-06T14:49:57.048Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342, upload-time = "2025-10-06T14:49:58.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082, upload-time = "2025-10-06T14:49:59.89Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704, upload-time = "2025-10-06T14:50:01.485Z" },
+ { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355, upload-time = "2025-10-06T14:50:02.955Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259, upload-time = "2025-10-06T14:50:04.446Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903, upload-time = "2025-10-06T14:50:05.98Z" },
+ { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365, upload-time = "2025-10-06T14:50:07.511Z" },
+ { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062, upload-time = "2025-10-06T14:50:09.074Z" },
+ { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683, upload-time = "2025-10-06T14:50:10.714Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254, upload-time = "2025-10-06T14:50:12.28Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967, upload-time = "2025-10-06T14:50:14.16Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085, upload-time = "2025-10-06T14:50:15.639Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713, upload-time = "2025-10-06T14:50:17.066Z" },
+ { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915, upload-time = "2025-10-06T14:50:18.264Z" },
+ { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077, upload-time = "2025-10-06T14:50:19.853Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114, upload-time = "2025-10-06T14:50:21.223Z" },
+ { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442, upload-time = "2025-10-06T14:50:22.871Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885, upload-time = "2025-10-06T14:50:24.258Z" },
+ { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588, upload-time = "2025-10-06T14:50:25.716Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966, upload-time = "2025-10-06T14:50:28.192Z" },
+ { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618, upload-time = "2025-10-06T14:50:29.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539, upload-time = "2025-10-06T14:50:31.731Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345, upload-time = "2025-10-06T14:50:33.26Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934, upload-time = "2025-10-06T14:50:34.808Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243, upload-time = "2025-10-06T14:50:36.436Z" },
+ { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878, upload-time = "2025-10-06T14:50:37.953Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452, upload-time = "2025-10-06T14:50:39.574Z" },
+ { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312, upload-time = "2025-10-06T14:50:41.612Z" },
+ { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935, upload-time = "2025-10-06T14:50:43.972Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385, upload-time = "2025-10-06T14:50:45.648Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777, upload-time = "2025-10-06T14:50:47.154Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104, upload-time = "2025-10-06T14:50:48.851Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503, upload-time = "2025-10-06T14:50:50.16Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128, upload-time = "2025-10-06T14:50:51.92Z" },
+ { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410, upload-time = "2025-10-06T14:50:53.275Z" },
+ { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205, upload-time = "2025-10-06T14:50:54.911Z" },
+ { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084, upload-time = "2025-10-06T14:50:56.369Z" },
+ { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667, upload-time = "2025-10-06T14:50:57.991Z" },
+ { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590, upload-time = "2025-10-06T14:50:59.589Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112, upload-time = "2025-10-06T14:51:01.183Z" },
+ { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194, upload-time = "2025-10-06T14:51:02.794Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510, upload-time = "2025-10-06T14:51:04.724Z" },
+ { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395, upload-time = "2025-10-06T14:51:06.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520, upload-time = "2025-10-06T14:51:08.091Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479, upload-time = "2025-10-06T14:51:10.365Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903, upload-time = "2025-10-06T14:51:12.466Z" },
+ { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333, upload-time = "2025-10-06T14:51:14.48Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411, upload-time = "2025-10-06T14:51:16.072Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940, upload-time = "2025-10-06T14:51:17.544Z" },
+ { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087, upload-time = "2025-10-06T14:51:18.875Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368, upload-time = "2025-10-06T14:51:20.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326, upload-time = "2025-10-06T14:51:21.588Z" },
+ { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065, upload-time = "2025-10-06T14:51:22.93Z" },
+ { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475, upload-time = "2025-10-06T14:51:24.352Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324, upload-time = "2025-10-06T14:51:25.822Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877, upload-time = "2025-10-06T14:51:27.604Z" },
+ { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824, upload-time = "2025-10-06T14:51:29.664Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558, upload-time = "2025-10-06T14:51:31.684Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339, upload-time = "2025-10-06T14:51:33.699Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895, upload-time = "2025-10-06T14:51:36.189Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862, upload-time = "2025-10-06T14:51:41.291Z" },
+ { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376, upload-time = "2025-10-06T14:51:43.55Z" },
+ { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272, upload-time = "2025-10-06T14:51:45.265Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774, upload-time = "2025-10-06T14:51:46.836Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731, upload-time = "2025-10-06T14:51:48.541Z" },
+ { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193, upload-time = "2025-10-06T14:51:50.355Z" },
+ { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023, upload-time = "2025-10-06T14:51:51.883Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507, upload-time = "2025-10-06T14:51:53.672Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804, upload-time = "2025-10-06T14:51:55.415Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" },
]
[[package]]
@@ -773,23 +871,10 @@ dependencies = [
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
{ name = "mypy-extensions" },
{ name = "pathspec" },
- { name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" },
- { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" },
- { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" },
- { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" },
- { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" },
- { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" },
- { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" },
- { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" },
- { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" },
- { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" },
- { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" },
- { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" },
{ url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" },
{ url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" },
{ url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" },
@@ -821,30 +906,30 @@ wheels = [
]
[[package]]
-name = "nodeenv"
-version = "1.9.1"
+name = "packaging"
+version = "26.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
]
[[package]]
-name = "packaging"
-version = "25.0"
+name = "paginate"
+version = "0.5.7"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+ { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" },
]
[[package]]
name = "pathspec"
-version = "0.12.1"
+version = "1.0.3"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
+ { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" },
]
[[package]]
@@ -866,108 +951,111 @@ wheels = [
]
[[package]]
-name = "pre-commit"
-version = "4.5.1"
+name = "prek"
+version = "0.3.0"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cfgv" },
- { name = "identify" },
- { name = "nodeenv" },
- { name = "pyyaml" },
- { name = "virtualenv" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f2/1e/6c23d3470145be1d6ff29d93f2a521864788827d22e509e2b978eb5bb4cb/prek-0.3.0.tar.gz", hash = "sha256:e70f16bbaf2803e490b866cfa997ea5cc46e7ada55d61f0cdd84bc90b8d5ca7f", size = 316063, upload-time = "2026-01-22T04:00:01.648Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
+ { url = "https://files.pythonhosted.org/packages/84/49/469219c19bb00db678806f79fc084ac1ce9952004a183a798db26f6df22b/prek-0.3.0-py3-none-linux_armv6l.whl", hash = "sha256:7e5d40b22deff23e36f7ad91e24b8e62edf32f30f6dad420459f7ec7188233c3", size = 4317493, upload-time = "2026-01-22T03:59:51.769Z" },
+ { url = "https://files.pythonhosted.org/packages/87/9f/f7afc49cc0fd92d1ba492929dc1573cb7004d09b61341aa6ee32a5288657/prek-0.3.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6712b58cbb5a7db0aaef180c489ce9f3462e0293d54e54baeedd75fc0d9d8c28", size = 4323961, upload-time = "2026-01-22T03:59:56.92Z" },
+ { url = "https://files.pythonhosted.org/packages/42/94/ba36dc29e71d476bf71c3bac2b0c89cfcfc4b8973a0a6b20728f429f4560/prek-0.3.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f2c446fd9012a98c5690b4badf3f7dfb8d424cf0c6798a2d08ee56511f0a670", size = 3970121, upload-time = "2026-01-22T03:59:55.722Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/93/6131dd9f6cde3d72815b978b766de21b2ac9cc15fc38f5c22267cc7e574d/prek-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:10f3da7cda2397f7d2f3ff7f2be0d7486c15d4941f7568095b7168e57a9c88c5", size = 4307430, upload-time = "2026-01-22T03:59:47.484Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/08/7c55a765d96028d38dc984e66a096a969d80e56f66a47801acc86dede856/prek-0.3.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f747bb4a4322fea35d548cd2c1bd24477f56ed009f3d62a2b97ecbfc88096ac", size = 4238032, upload-time = "2026-01-22T04:00:02.606Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/a7/59d9bf902b749c8a0cef9e8ac073cc5c886634cd09404c00af4a76470b3b/prek-0.3.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40bd61f11d8caabc0e2a5d4c326639d6ff558b580ef4388aabec293ddb5afd35", size = 4493295, upload-time = "2026-01-22T03:59:45.964Z" },
+ { url = "https://files.pythonhosted.org/packages/08/dc/902b2e4ddff59ad001ddc2cda3b47e457ab1ee811698a4002b3e4f84faf1/prek-0.3.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d096b5e273d17a1300b20a7101a9e5a624a8104825eb59659776177f7fccea1", size = 5033370, upload-time = "2026-01-22T03:59:44.806Z" },
+ { url = "https://files.pythonhosted.org/packages/15/cd/277a3d2768b80bb1ff3c2ea8378687bb4c527d88a8b543bf6f364f8a0dc9/prek-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df39face5f1298851fbae495267ddf60f1694ea594ed5c6cdb88bdd6de14f6a4", size = 4549792, upload-time = "2026-01-22T03:59:41.518Z" },
+ { url = "https://files.pythonhosted.org/packages/26/21/53aeabd3822ef7fa350aac66d099d4d97b05e8383a2df35499229389a642/prek-0.3.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9462f80a576d661490aa058d4493a991a34c7532dea76b7b004a17c8bc6b80f2", size = 4323158, upload-time = "2026-01-22T03:59:54.284Z" },
+ { url = "https://files.pythonhosted.org/packages/27/c2/3a7392b0e7fd07e339d89701b49b12a89d85256a57279877195028215957/prek-0.3.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:33d3fa40eecf996ed14bab2d006c39d21ae344677d62599963efd9b27936558e", size = 4344632, upload-time = "2026-01-22T04:00:03.71Z" },
+ { url = "https://files.pythonhosted.org/packages/71/89/8254ac981d75d0ce2826bcac74fed901540d629cb2d9f4d73ce62f8ce843/prek-0.3.0-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:d8c6abfd53a23718afdf4e6107418db1d74c5d904e9b7ec7900e285f8da90723", size = 4216608, upload-time = "2026-01-22T03:59:58.527Z" },
+ { url = "https://files.pythonhosted.org/packages/20/f5/854d57d89376fac577ee647a1dba1b87e27b2baeca7edc3d40295adeb7c8/prek-0.3.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:eb4c80c3e7c0e16bf307947809112bfef3715a1b83c2b03f5937707934635617", size = 4371174, upload-time = "2026-01-22T03:59:53.088Z" },
+ { url = "https://files.pythonhosted.org/packages/03/38/8927619411da8d3f189415c452ec7a463f09dea69e272888723f37b4b18f/prek-0.3.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:602bcce070c50900167acd89dcdf95d27894412f8a7b549c8eb66de612a99653", size = 4659113, upload-time = "2026-01-22T03:59:43.166Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/4d/16baeef633b8b230dde878b858c0e955149c860feef518b5eb5aac640eec/prek-0.3.0-py3-none-win32.whl", hash = "sha256:a69229365ce33c68c05db7ae73ad1ef8bc7f0914ab3bc484ab7781256bcdfb7a", size = 3937103, upload-time = "2026-01-22T03:59:48.719Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f2/c7395b4afd1bba32cad2b24c30fd7781e94c1e41137348cd150bbef001d6/prek-0.3.0-py3-none-win_amd64.whl", hash = "sha256:a0379afd8d31bd5da6ee8977820fdb3c30601bed836b39761e6f605451dbccaa", size = 4290763, upload-time = "2026-01-22T03:59:59.938Z" },
+ { url = "https://files.pythonhosted.org/packages/df/83/97ed76ab5470025992cd50cb1ebdeb21fcf6c25459f9ffc49ac7bf040cf4/prek-0.3.0-py3-none-win_arm64.whl", hash = "sha256:82e2c64f75dc1ea6f2023f4322500eb8da5d0557baf06c88677bddf163e1542a", size = 4041580, upload-time = "2026-01-22T03:59:50.082Z" },
]
[[package]]
name = "propcache"
-version = "0.3.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" },
- { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" },
- { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" },
- { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" },
- { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" },
- { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" },
- { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" },
- { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" },
- { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" },
- { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" },
- { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" },
- { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" },
- { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" },
- { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" },
- { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" },
- { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" },
- { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" },
- { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" },
- { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" },
- { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" },
- { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" },
- { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" },
- { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" },
- { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" },
- { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" },
- { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" },
- { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" },
- { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" },
- { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" },
- { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" },
- { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" },
- { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" },
- { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" },
- { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" },
- { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" },
- { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" },
- { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" },
- { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" },
- { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" },
- { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" },
- { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" },
- { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" },
- { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" },
- { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" },
- { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" },
- { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" },
- { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" },
- { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" },
- { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" },
- { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" },
- { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" },
- { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" },
- { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" },
- { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" },
- { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" },
- { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" },
- { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" },
- { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" },
- { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" },
- { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" },
- { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" },
- { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" },
- { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" },
- { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" },
- { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" },
- { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" },
- { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" },
- { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" },
- { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" },
- { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" },
- { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" },
- { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" },
- { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" },
- { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" },
- { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" },
- { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" },
- { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" },
- { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" },
- { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" },
- { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" },
- { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" },
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" },
+ { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" },
+ { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" },
+ { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" },
+ { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" },
+ { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" },
+ { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" },
+ { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" },
+ { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" },
+ { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" },
+ { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" },
+ { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" },
+ { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" },
+ { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" },
+ { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" },
+ { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" },
+ { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" },
+ { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" },
+ { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" },
+ { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" },
+ { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" },
+ { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" },
+ { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" },
+ { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" },
+ { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" },
+ { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" },
+ { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" },
+ { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" },
+ { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" },
+ { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
]
[[package]]
@@ -997,24 +1085,45 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/11/a1938340ecb32d71e47ad4914843775011e6e9da59ba1229f181fef3119e/pyhumps-3.8.0-py3-none-any.whl", hash = "sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6", size = 6095, upload-time = "2022-10-21T10:38:58.231Z" },
]
+[[package]]
+name = "pymdown-extensions"
+version = "10.20.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1e/6c/9e370934bfa30e889d12e61d0dae009991294f40055c238980066a7fbd83/pymdown_extensions-10.20.1.tar.gz", hash = "sha256:e7e39c865727338d434b55f1dd8da51febcffcaebd6e1a0b9c836243f660740a", size = 852860, upload-time = "2026-01-24T05:56:56.758Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/40/6d/b6ee155462a0156b94312bdd82d2b92ea56e909740045a87ccb98bf52405/pymdown_extensions-10.20.1-py3-none-any.whl", hash = "sha256:24af7feacbca56504b313b7b418c4f5e1317bb5fea60f03d57be7fcc40912aa0", size = 268768, upload-time = "2026-01-24T05:56:54.537Z" },
+]
+
[[package]]
name = "pyoverkiz"
-version = "1.19.4"
+version = "2.0.0"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
{ name = "attrs" },
{ name = "backoff" },
- { name = "backports-strenum", marker = "python_full_version < '3.11'" },
{ name = "boto3" },
{ name = "pyhumps" },
{ name = "warrant-lite" },
]
+[package.optional-dependencies]
+docs = [
+ { name = "mkdocs" },
+ { name = "mkdocs-autorefs" },
+ { name = "mkdocs-material" },
+ { name = "mkdocstrings", extra = ["python"] },
+ { name = "pymdown-extensions" },
+]
+
[package.dev-dependencies]
dev = [
{ name = "mypy" },
- { name = "pre-commit" },
+ { name = "prek" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
@@ -1027,16 +1136,21 @@ requires-dist = [
{ name = "aiohttp", specifier = ">=3.10.3,<4.0.0" },
{ name = "attrs", specifier = ">=21.2" },
{ name = "backoff", specifier = ">=1.10.0,<3.0" },
- { name = "backports-strenum", marker = "python_full_version < '3.11'", specifier = ">=1.2.4,<2.0.0" },
{ name = "boto3", specifier = ">=1.18.59,<2.0.0" },
+ { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.5.0" },
+ { name = "mkdocs-autorefs", marker = "extra == 'docs'", specifier = ">=1.0.0" },
+ { name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.5.0" },
+ { name = "mkdocstrings", extras = ["python"], marker = "extra == 'docs'", specifier = ">=0.24.0" },
{ name = "pyhumps", specifier = ">=3.8.0,<4.0.0" },
+ { name = "pymdown-extensions", marker = "extra == 'docs'", specifier = ">=10.0" },
{ name = "warrant-lite", specifier = ">=1.0.4,<2.0.0" },
]
+provides-extras = ["docs"]
[package.metadata.requires-dev]
dev = [
{ name = "mypy", specifier = ">=1.16.1" },
- { name = "pre-commit", specifier = ">=3.8,<5.0" },
+ { name = "prek", specifier = ">=0.2.27" },
{ name = "pytest", specifier = ">=8.3.2,<10.0.0" },
{ name = "pytest-asyncio", specifier = ">=0.23.8,<1.4.0" },
{ name = "pytest-cov", specifier = ">=5,<8" },
@@ -1050,12 +1164,10 @@ version = "9.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
- { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
- { name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
wheels = [
@@ -1067,7 +1179,6 @@ name = "pytest-asyncio"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
{ name = "pytest" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
@@ -1081,7 +1192,7 @@ name = "pytest-cov"
version = "7.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "coverage", extra = ["toml"] },
+ { name = "coverage" },
{ name = "pluggy" },
{ name = "pytest" },
]
@@ -1118,51 +1229,65 @@ wheels = [
[[package]]
name = "pyyaml"
-version = "6.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" },
- { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" },
- { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" },
- { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" },
- { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" },
- { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" },
- { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" },
- { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" },
- { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" },
- { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" },
- { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" },
- { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" },
- { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" },
- { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" },
- { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" },
- { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" },
- { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" },
- { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" },
- { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" },
- { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" },
- { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" },
- { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" },
- { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" },
- { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" },
- { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" },
- { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" },
- { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" },
- { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" },
- { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" },
- { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" },
- { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" },
- { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" },
- { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" },
- { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" },
- { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
- { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
+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, upload-time = "2025-09-25T21:33:16.546Z" }
+wheels = [
+ { 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, upload-time = "2025-09-25T21:32:11.445Z" },
+ { 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, upload-time = "2025-09-25T21:32:12.492Z" },
+ { 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, upload-time = "2025-09-25T21:32:13.652Z" },
+ { 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, upload-time = "2025-09-25T21:32:15.21Z" },
+ { 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, upload-time = "2025-09-25T21:32:16.431Z" },
+ { 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, upload-time = "2025-09-25T21:32:17.56Z" },
+ { 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, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
+ { 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, upload-time = "2025-09-25T21:32:23.673Z" },
+ { 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, upload-time = "2025-09-25T21:32:25.149Z" },
+ { 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, upload-time = "2025-09-25T21:32:26.575Z" },
+ { 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, upload-time = "2025-09-25T21:32:27.727Z" },
+ { 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, upload-time = "2025-09-25T21:32:28.878Z" },
+ { 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, upload-time = "2025-09-25T21:32:30.178Z" },
+ { 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, upload-time = "2025-09-25T21:32:31.353Z" },
+ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
+ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
+ { 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, upload-time = "2025-09-25T21:32:35.712Z" },
+ { 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, upload-time = "2025-09-25T21:32:36.789Z" },
+ { 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, upload-time = "2025-09-25T21:32:37.966Z" },
+ { 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, upload-time = "2025-09-25T21:32:39.178Z" },
+ { 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, upload-time = "2025-09-25T21:32:40.865Z" },
+ { 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, upload-time = "2025-09-25T21:32:42.084Z" },
+ { 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, upload-time = "2025-09-25T21:32:43.362Z" },
+ { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
+ { 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, upload-time = "2025-09-25T21:32:44.377Z" },
+ { 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, upload-time = "2025-09-25T21:32:45.407Z" },
+ { 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, upload-time = "2025-09-25T21:32:48.83Z" },
+ { 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, upload-time = "2025-09-25T21:32:50.149Z" },
+ { 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, upload-time = "2025-09-25T21:32:51.808Z" },
+ { 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, upload-time = "2025-09-25T21:32:52.941Z" },
+ { 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, upload-time = "2025-09-25T21:32:54.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
+]
+
+[[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, upload-time = "2025-05-13T15:24:01.64Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" },
]
[[package]]
name = "requests"
-version = "2.32.4"
+version = "2.32.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
@@ -1170,9 +1295,9 @@ dependencies = [
{ name = "idna" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
@@ -1234,55 +1359,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
-[[package]]
-name = "tomli"
-version = "2.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" },
- { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" },
- { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" },
- { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" },
- { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" },
- { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" },
- { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" },
- { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" },
- { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" },
- { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" },
- { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" },
- { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" },
- { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" },
- { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" },
- { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" },
- { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" },
- { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" },
- { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" },
- { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" },
- { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" },
- { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" },
- { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" },
- { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" },
- { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" },
- { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" },
- { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" },
- { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" },
- { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" },
- { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" },
- { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" },
- { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" },
- { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" },
- { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" },
- { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" },
- { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" },
- { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" },
- { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" },
- { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" },
- { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" },
- { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" },
- { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
-]
-
[[package]]
name = "ty"
version = "0.0.13"
@@ -1325,21 +1401,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
]
-[[package]]
-name = "virtualenv"
-version = "20.36.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "distlib" },
- { name = "filelock" },
- { name = "platformdirs" },
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" },
-]
-
[[package]]
name = "warrant-lite"
version = "1.0.4"
@@ -1355,101 +1416,120 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/31/04/e2deee7138370e27cb4f86cfe9e4230d3228b6a0804bd68e97e49483d465/warrant_lite-1.0.4-py3-none-any.whl", hash = "sha256:e5ea95dfee9d4f56398c3d8846fb9e16db1b0ad6395c59e1c5df5bf541f075b2", size = 10210, upload-time = "2019-05-21T13:50:30.487Z" },
]
+[[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, upload-time = "2024-11-01T14:07:13.037Z" }
+wheels = [
+ { 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, upload-time = "2024-11-01T14:06:37.745Z" },
+ { 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, upload-time = "2024-11-01T14:06:39.748Z" },
+ { 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, upload-time = "2024-11-01T14:06:41.009Z" },
+ { 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, upload-time = "2024-11-01T14:06:42.952Z" },
+ { 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, upload-time = "2024-11-01T14:06:45.084Z" },
+ { 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, upload-time = "2024-11-01T14:06:47.324Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
+ { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
+ { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
+ { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
+ { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
+]
+
[[package]]
name = "yarl"
-version = "1.20.1"
+version = "1.22.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "multidict" },
{ name = "propcache" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4", size = 132910, upload-time = "2025-06-10T00:42:31.108Z" },
- { url = "https://files.pythonhosted.org/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a", size = 90644, upload-time = "2025-06-10T00:42:33.851Z" },
- { url = "https://files.pythonhosted.org/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed", size = 89322, upload-time = "2025-06-10T00:42:35.688Z" },
- { url = "https://files.pythonhosted.org/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e", size = 323786, upload-time = "2025-06-10T00:42:37.817Z" },
- { url = "https://files.pythonhosted.org/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73", size = 319627, upload-time = "2025-06-10T00:42:39.937Z" },
- { url = "https://files.pythonhosted.org/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e", size = 339149, upload-time = "2025-06-10T00:42:42.627Z" },
- { url = "https://files.pythonhosted.org/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8", size = 333327, upload-time = "2025-06-10T00:42:44.842Z" },
- { url = "https://files.pythonhosted.org/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23", size = 326054, upload-time = "2025-06-10T00:42:47.149Z" },
- { url = "https://files.pythonhosted.org/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70", size = 315035, upload-time = "2025-06-10T00:42:48.852Z" },
- { url = "https://files.pythonhosted.org/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb", size = 338962, upload-time = "2025-06-10T00:42:51.024Z" },
- { url = "https://files.pythonhosted.org/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2", size = 335399, upload-time = "2025-06-10T00:42:53.007Z" },
- { url = "https://files.pythonhosted.org/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30", size = 338649, upload-time = "2025-06-10T00:42:54.964Z" },
- { url = "https://files.pythonhosted.org/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309", size = 358563, upload-time = "2025-06-10T00:42:57.28Z" },
- { url = "https://files.pythonhosted.org/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24", size = 357609, upload-time = "2025-06-10T00:42:59.055Z" },
- { url = "https://files.pythonhosted.org/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13", size = 350224, upload-time = "2025-06-10T00:43:01.248Z" },
- { url = "https://files.pythonhosted.org/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8", size = 81753, upload-time = "2025-06-10T00:43:03.486Z" },
- { url = "https://files.pythonhosted.org/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16", size = 86817, upload-time = "2025-06-10T00:43:05.231Z" },
- { url = "https://files.pythonhosted.org/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833, upload-time = "2025-06-10T00:43:07.393Z" },
- { url = "https://files.pythonhosted.org/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070, upload-time = "2025-06-10T00:43:09.538Z" },
- { url = "https://files.pythonhosted.org/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818, upload-time = "2025-06-10T00:43:11.575Z" },
- { url = "https://files.pythonhosted.org/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003, upload-time = "2025-06-10T00:43:14.088Z" },
- { url = "https://files.pythonhosted.org/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537, upload-time = "2025-06-10T00:43:16.431Z" },
- { url = "https://files.pythonhosted.org/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358, upload-time = "2025-06-10T00:43:18.704Z" },
- { url = "https://files.pythonhosted.org/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362, upload-time = "2025-06-10T00:43:20.888Z" },
- { url = "https://files.pythonhosted.org/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979, upload-time = "2025-06-10T00:43:23.169Z" },
- { url = "https://files.pythonhosted.org/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274, upload-time = "2025-06-10T00:43:27.111Z" },
- { url = "https://files.pythonhosted.org/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294, upload-time = "2025-06-10T00:43:28.96Z" },
- { url = "https://files.pythonhosted.org/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169, upload-time = "2025-06-10T00:43:30.701Z" },
- { url = "https://files.pythonhosted.org/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776, upload-time = "2025-06-10T00:43:32.51Z" },
- { url = "https://files.pythonhosted.org/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341, upload-time = "2025-06-10T00:43:34.543Z" },
- { url = "https://files.pythonhosted.org/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988, upload-time = "2025-06-10T00:43:36.489Z" },
- { url = "https://files.pythonhosted.org/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113, upload-time = "2025-06-10T00:43:38.592Z" },
- { url = "https://files.pythonhosted.org/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485, upload-time = "2025-06-10T00:43:41.038Z" },
- { url = "https://files.pythonhosted.org/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686, upload-time = "2025-06-10T00:43:42.692Z" },
- { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667, upload-time = "2025-06-10T00:43:44.369Z" },
- { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025, upload-time = "2025-06-10T00:43:46.295Z" },
- { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709, upload-time = "2025-06-10T00:43:48.22Z" },
- { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287, upload-time = "2025-06-10T00:43:49.924Z" },
- { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429, upload-time = "2025-06-10T00:43:51.7Z" },
- { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429, upload-time = "2025-06-10T00:43:53.494Z" },
- { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862, upload-time = "2025-06-10T00:43:55.766Z" },
- { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616, upload-time = "2025-06-10T00:43:58.056Z" },
- { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954, upload-time = "2025-06-10T00:43:59.773Z" },
- { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575, upload-time = "2025-06-10T00:44:02.051Z" },
- { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061, upload-time = "2025-06-10T00:44:04.196Z" },
- { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142, upload-time = "2025-06-10T00:44:06.527Z" },
- { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894, upload-time = "2025-06-10T00:44:08.379Z" },
- { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378, upload-time = "2025-06-10T00:44:10.51Z" },
- { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069, upload-time = "2025-06-10T00:44:12.834Z" },
- { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249, upload-time = "2025-06-10T00:44:14.731Z" },
- { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710, upload-time = "2025-06-10T00:44:16.716Z" },
- { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" },
- { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" },
- { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" },
- { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" },
- { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" },
- { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" },
- { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" },
- { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" },
- { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" },
- { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" },
- { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" },
- { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" },
- { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" },
- { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" },
- { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" },
- { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" },
- { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" },
- { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" },
- { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" },
- { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" },
- { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" },
- { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" },
- { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" },
- { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" },
- { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" },
- { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" },
- { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" },
- { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" },
- { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" },
- { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" },
- { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" },
- { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" },
- { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" },
- { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" },
- { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" },
+ { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" },
+ { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" },
+ { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" },
+ { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" },
+ { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" },
+ { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" },
+ { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" },
+ { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" },
+ { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" },
+ { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" },
+ { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" },
+ { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" },
+ { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" },
+ { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" },
+ { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" },
+ { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" },
+ { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" },
+ { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" },
+ { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" },
+ { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" },
+ { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" },
+ { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" },
+ { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" },
+ { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" },
+ { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" },
+ { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" },
+ { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" },
+ { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" },
+ { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" },
+ { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" },
+ { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" },
+ { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" },
+ { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" },
+ { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" },
]