From c5bb2b6457dea147eb9e0bec47471cad0b25de9d Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 20:22:20 -0700 Subject: [PATCH 01/39] build: Migrate from Poetry to uv (#606) * build: Migrate from Poetry to uv Replace Poetry with uv for dependency management, packaging, and publishing: - Convert pyproject.toml to PEP 621 project metadata with the uv_build backend and PEP 735 dependency groups. - Replace poetry.lock with uv.lock, keeping the previously locked versions of the lint toolchain (black, pylint, mypy, rstcheck, docutils) to avoid behavior drift. - Update the justfile to use uv run, uv build, and uv version. - Replace the Poetry setup and caching in GitHub Actions with astral-sh/setup-uv, and switch publishing to uv publish. - Install uv instead of Poetry in the devcontainer. - Update the README development docs. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Xi2DoWdLxtV7AqqxtbQVqN * style: Format code with black targeting Python 3.10 With PEP 621 metadata, black now infers its target versions from requires-python and applies newer formatting (trailing commas after **kwargs). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Xi2DoWdLxtV7AqqxtbQVqN * build: Resolve uv version from pyproject.toml in CI Declare required-version under [tool.uv] and let setup-uv discover it from the config file instead of passing a version through an action input. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Xi2DoWdLxtV7AqqxtbQVqN --------- Co-authored-by: Claude --- .devcontainer/Dockerfile | 8 +- .devcontainer/devcontainer.json | 5 +- .github/actions/setup/action.yml | 40 +- .github/workflows/_publish.yml | 5 +- .github/workflows/generate.yml | 6 +- .github/workflows/version.yml | 2 +- .gitignore | 8 +- README.rst | 14 +- justfile | 22 +- poetry.lock | 1642 ----------------- pyproject.toml | 55 +- seam/client.py | 2 +- seam/modules/action_attempts.py | 4 +- seam/routes/access_codes.py | 28 +- seam/routes/access_codes_unmanaged.py | 16 +- seam/routes/access_grants.py | 20 +- seam/routes/access_grants_unmanaged.py | 8 +- seam/routes/access_methods.py | 24 +- seam/routes/access_methods_unmanaged.py | 4 +- seam/routes/acs_access_groups.py | 12 +- seam/routes/acs_credentials.py | 20 +- seam/routes/acs_encoders.py | 16 +- seam/routes/acs_encoders_simulate.py | 12 +- seam/routes/acs_entrances.py | 12 +- seam/routes/acs_systems.py | 8 +- seam/routes/acs_users.py | 40 +- seam/routes/action_attempts.py | 8 +- seam/routes/client_sessions.py | 20 +- seam/routes/connect_webviews.py | 8 +- seam/routes/connected_accounts.py | 8 +- seam/routes/customers.py | 12 +- seam/routes/devices.py | 8 +- seam/routes/devices_unmanaged.py | 8 +- seam/routes/events.py | 8 +- seam/routes/instant_keys.py | 4 +- seam/routes/locks.py | 16 +- seam/routes/locks_simulate.py | 8 +- seam/routes/noise_sensors.py | 4 +- seam/routes/noise_sensors_noise_thresholds.py | 8 +- seam/routes/phones.py | 4 +- seam/routes/phones_simulate.py | 4 +- seam/routes/spaces.py | 16 +- seam/routes/thermostats.py | 48 +- seam/routes/thermostats_daily_programs.py | 4 +- seam/routes/thermostats_schedules.py | 8 +- seam/routes/thermostats_simulate.py | 8 +- seam/routes/user_identities.py | 24 +- seam/routes/user_identities_unmanaged.py | 8 +- seam/routes/workspaces.py | 8 +- uv.lock | 1583 ++++++++++++++++ 50 files changed, 1893 insertions(+), 1975 deletions(-) delete mode 100644 poetry.lock create mode 100644 uv.lock diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c06a52f4..594c0c9a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,8 +3,8 @@ ARG VARIANT="3" FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} ARG NODE_VERSION="24" -ARG POETRY_VERSION="1.8.2" -ARG POETRY_SRC="https://install.python-poetry.org" +ARG UV_VERSION="0.8.17" +ARG UV_SRC="https://astral.sh/uv" # https://github.com/microsoft/vscode-dev-containers/blob/main/containers/go/.devcontainer/base.Dockerfile ENV USERNAME=vscode @@ -25,9 +25,7 @@ RUN apt-get update -y \ USER vscode WORKDIR /home/vscode -RUN curl -fsSL -o install-poetry.py "${POETRY_SRC}" \ - && python install-poetry.py --version $POETRY_VERSION \ - && rm install-poetry.py +RUN curl -fsSL "${UV_SRC}/${UV_VERSION}/install.sh" | sh RUN mkdir -p .config/git \ && echo ".vscode/*" >> .config/git/ignore \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0d6bb46e..a70bd664 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,12 +5,11 @@ "dockerfile": "Dockerfile", "args": { "NODE_VERSION": "24", - "POETRY_VERSION": "1.8.2", + "UV_VERSION": "0.8.17", "VARIANT": "3.12" } }, "remoteEnv": { - "POETRY_VIRTUALENVS_IN_PROJECT": "true", "PATH": "${containerEnv:PATH}:/home/vscode/.local/bin" }, "extensions": [ @@ -19,6 +18,6 @@ "EditorConfig.EditorConfig", "esbenp.prettier-vscode" ], - "postCreateCommand": "poetry install && npm install", + "postCreateCommand": "uv sync && npm install", "remoteUser": "vscode" } diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 8f000cae..518622cb 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -7,10 +7,6 @@ inputs: description: The Python version. required: false default: '3.12' - poetry_version: - description: The Poetry version. - required: false - default: '1.8.2' just_version: description: The just version. required: false @@ -23,46 +19,20 @@ inputs: runs: using: composite steps: - - name: Setup Poetry cache on Linux - uses: actions/cache@v4 - if: runner.os == 'Linux' - with: - key: poetry-${{ inputs.poetry_version }}-${{ inputs.python_version }}-${{ runner.os }}-${{ runner.arch }} - path: | - ~/.local/bin - ~/.local/share/pypoetry - - name: Setup Poetry cache on macOS - uses: actions/cache@v4 - if: runner.os == 'macOS' - with: - key: poetry-${{ inputs.poetry_version }}-${{ inputs.python_version }}-${{ runner.os }}-${{ runner.arch }} - path: | - ~/.local/bin - ~/.local/share/pypoetry - ~/Library/Application Support/pypoetry - name: Setup just uses: extractions/setup-just@v4 with: just-version: ${{ inputs.just_version }} - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ inputs.python_version }} - - name: Setup Poetry - uses: Gr1N/setup-poetry@v9 - with: - poetry-version: ${{ inputs.poetry_version }} - - name: Setup Python with cache - uses: actions/setup-python@v5 - if: inputs.install_dependencies == 'true' + - name: Setup uv + uses: astral-sh/setup-uv@v7 with: - cache: poetry python-version: ${{ inputs.python_version }} + enable-cache: true - name: Check lockfile if: inputs.install_dependencies == 'true' shell: bash - run: poetry check --lock + run: uv lock --check - name: Install dependencies if: inputs.install_dependencies == 'true' shell: bash - run: poetry install --sync + run: uv sync diff --git a/.github/workflows/_publish.yml b/.github/workflows/_publish.yml index cc6b0609..8be71cff 100644 --- a/.github/workflows/_publish.yml +++ b/.github/workflows/_publish.yml @@ -31,7 +31,6 @@ jobs: name: ${{ inputs.artifact_name }} path: dist/ - name: Publish - run: poetry publish --skip-existing -u $USERNAME -p $PASSWORD + run: uv publish --check-url https://pypi.org/simple/ env: - USERNAME: __token__ - PASSWORD: ${{ secrets.registry_token }} + UV_PUBLISH_TOKEN: ${{ secrets.registry_token }} diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 40f49dad..637aafca 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -35,12 +35,12 @@ jobs: uses: ./.github/actions/setup-node with: install_dependencies: 'false' - - name: Normalize poetry.lock - run: poetry lock --no-update + - name: Normalize uv.lock + run: uv lock - name: Normalize package-lock.json run: npm install - name: Install dependencies - run: poetry install --sync + run: uv sync - name: Generate code run: npm run generate - name: Commit diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 9bde1873..afb09b8c 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -33,7 +33,7 @@ jobs: uses: ./.github/actions/setup - name: Cut ${{ github.event.inputs.version }} version run: | - poetry version "${{ github.event.inputs.version }}" + uv version "${{ github.event.inputs.version }}" just version - name: Record prerelease channel env: diff --git a/.gitignore b/.gitignore index ff0aacf2..4a43d43e 100644 --- a/.gitignore +++ b/.gitignore @@ -110,12 +110,12 @@ ipython_config.py # install all needed dependencies. #Pipfile.lock -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# uv +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock +# https://docs.astral.sh/uv/concepts/projects/sync/#checking-the-lockfile +#uv.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. diff --git a/README.rst b/README.rst index 65cad433..4dbba7bb 100644 --- a/README.rst +++ b/README.rst @@ -496,7 +496,7 @@ Quickstart $ git clone https://github.com/seamapi/python.git $ cd python - $ poetry install + $ uv sync Run each command below in a separate terminal window: @@ -521,19 +521,19 @@ Clone the project with Requirements ~~~~~~~~~~~~ -You will need `Python 3`_ and Poetry_ and Node.js_ with npm_ and just_. +You will need `Python 3`_ and uv_ and Node.js_ with npm_ and just_. Install the development dependencies with :: - $ poetry install + $ uv sync $ npm install .. _just: https://just.systems/ .. _Node.js: https://nodejs.org/ .. _npm: https://www.npmjs.com/ -.. _Poetry: https://poetry.eustace.io/ +.. _uv: https://docs.astral.sh/uv/ .. _Python 3: https://www.python.org/ Tests @@ -561,7 +561,7 @@ Run tests on changes with Publishing ~~~~~~~~~~ -New versions are created with `poetry version`_. +New versions are created with `uv version`_. Automatic ^^^^^^^^^ @@ -576,7 +576,7 @@ Manual ^^^^^^ Publish a new version by triggering a `version workflow_dispatch on GitHub Actions`_. -The ``version`` input will be passed as the first argument to `poetry version`_. +The ``version`` input will be passed as the first argument to `uv version`_. This may be done on the web or using the `GitHub CLI`_ with @@ -584,7 +584,7 @@ This may be done on the web or using the `GitHub CLI`_ with $ gh workflow run version.yml --raw-field version= -.. _Poetry version: https://python-poetry.org/docs/cli/#version +.. _uv version: https://docs.astral.sh/uv/reference/cli/#uv-version .. _GitHub CLI: https://cli.github.com/ .. _version workflow_dispatch on GitHub Actions: https://github.com/seamapi/python/actions?query=workflow%3Aversion diff --git a/justfile b/justfile index d55b9a0b..a34cc4d5 100644 --- a/justfile +++ b/justfile @@ -2,25 +2,25 @@ default: build @build: rm -rf dist - poetry build + uv build @format: - poetry run black . + uv run black . @lint: - poetry run pylint ./seam ./test - poetry run black --check . - poetry run rstcheck README.rst - poetry run mypy seam/resources --disable-error-code=arg-type --disable-error-code=import-not-found + uv run pylint ./seam ./test + uv run black --check . + uv run rstcheck README.rst + uv run mypy seam/resources --disable-error-code=arg-type --disable-error-code=import-not-found @test: - poetry run pytest --cov=./seam + uv run pytest --cov=./seam @watch: - poetry run ptw + uv run ptw @version: - git add pyproject.toml - git commit -m "$(poetry version -s)" - git tag --sign "v$(poetry version -s)" -m "$(poetry version -s)" + git add pyproject.toml uv.lock + git commit -m "$(uv version --short)" + git tag --sign "v$(uv version --short)" -m "$(uv version --short)" git push --follow-tags diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index f7cbcb72..00000000 --- a/poetry.lock +++ /dev/null @@ -1,1642 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.3.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.8" -files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, -] - -[package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} -idna = ">=2.8" -sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} - -[package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] - -[[package]] -name = "astroid" -version = "3.2.2" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, - {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "attrs" -version = "23.2.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] - -[[package]] -name = "black" -version = "24.4.2" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, - {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, - {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, - {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, - {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, - {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, - {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, - {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, - {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, - {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, - {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, - {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, - {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, - {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, - {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, - {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, - {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, - {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, - {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, - {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, - {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, - {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2024.2.2" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.5.1" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, - {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, - {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, - {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, - {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, - {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, - {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, - {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "dataclasses-json" -version = "0.6.6" -description = "Easily serialize dataclasses to and from JSON." -optional = false -python-versions = "<4.0,>=3.7" -files = [ - {file = "dataclasses_json-0.6.6-py3-none-any.whl", hash = "sha256:e54c5c87497741ad454070ba0ed411523d46beb5da102e221efb873801b0ba85"}, - {file = "dataclasses_json-0.6.6.tar.gz", hash = "sha256:0c09827d26fffda27f1be2fed7a7a01a29c5ddcd2eb6393ad5ebf9d77e9deae8"}, -] - -[package.dependencies] -marshmallow = ">=3.18.0,<4.0.0" -typing-inspect = ">=0.4.0,<1" - -[[package]] -name = "deprecated" -version = "1.2.14" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] - -[[package]] -name = "dill" -version = "0.3.8" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "docopt" -version = "0.6.2" -description = "Pythonic argument parser, that will make you smile" -optional = false -python-versions = "*" -files = [ - {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, -] - -[[package]] -name = "docutils" -version = "0.21.2" -description = "Docutils -- Python Documentation Utilities" -optional = false -python-versions = ">=3.9" -files = [ - {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, - {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "httpcore" -version = "1.0.5" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.13,<0.15" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] - -[[package]] -name = "httpx" -version = "0.27.0" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] - -[[package]] -name = "idna" -version = "3.7" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "jh2" -version = "5.0.3" -description = "HTTP/2 State-Machine based protocol implementation" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jh2-5.0.3-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:764acdd572413198eb7a1299d08d32b0819c33220604f76ba7ea722443c3b929"}, - {file = "jh2-5.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f83bdbbae9fac7766e2edcac9a275af5a70e8e7188296c84cbeb552e1f1f2e8d"}, - {file = "jh2-5.0.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a921112bbafaea4d5ef9e2a25f03cacdaa1795b6a961f0fe430b8de15b939b3a"}, - {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18cdf52edf8e636f3a4a74d92eb62bc6692a2c78e288b0724341c82b078bb261"}, - {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ccb5fc490722b5ca0966a2402ae90a7bc70ec8a4a9bce224948db211f5fa2a9"}, - {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7c7dcecef260a792da2b7653011d09bcad5e1455e38fd194ee07fcb01a803fc3"}, - {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aa0af8f8a1a27dc7e166840fbdda46abc673d5cd8e2319ac08a3c7d5e9e9920"}, - {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78228b1a255581e93144d52feda6e8605fcfbfae7aa289def8879a7be6ca8a74"}, - {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2be4f69c7c8d87d28948065a038d84555a0784acf3886e9c18707f34ceb7c1b"}, - {file = "jh2-5.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f83206f4e18d4836ec9de573c9c0c27c02e277b59685d9d490f7187968b780fa"}, - {file = "jh2-5.0.3-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:be4e8c8ccb401cbd6386a406c21a87f690d68f1fdcc1698dcc813429bf5a9ce3"}, - {file = "jh2-5.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b6facef13abe549d70fd722ab668fae822cfdcade6199a12e7ec06fe0ba44326"}, - {file = "jh2-5.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3d2e48899378c3026d3640f1be1a043038af74f4076f1fb0c1b7bc13fb9c0619"}, - {file = "jh2-5.0.3-cp37-abi3-win_arm64.whl", hash = "sha256:2faaf1792220ffd5dcae8e88dd8f3b2b72771589121dbabc92fb503f488021c2"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:72db414df9a49a3498b7e083002cd9a3eefc4bce33789d7ae31d0e1c92229f0e"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6fa5629d6e18c6be93cc17da6b31a21511405e443df0a6936a0795807bf949f2"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a5d5326a15eed6b4dd64598522b741b65168093705e4276f964627ada3281f48"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af39530c7173dd41383ac9910164695768418bd5910bc1e8e628383d82656881"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c091dd40414b028793d3867968842a55c09e26bcbbba76e6daf8080b47384fd"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9e4b981ddce810d7a691f535c150212bef8a70c61081007c13b19ab30e44409b"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57f8d46fac15bbe1fe6c576b00ef2aaf616c359cc2fb8a468d46b05e19495bf5"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7b3476e3f13ce6751a45a82861a5fd64b38ef166c40974d0c97a0762293e12"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498a7e463f2be67ec1085833e99582bcb1e3ea1c2933f640f7c4896e307673eb"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:37e9d6f59e2c305f33793ea24fb588300828a83f9f51d47671e8335fc49a59e0"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:09b89d2700e9530f13b10dd1b8cb0e5389e3da833b6aa7d0bef74ef39229c7ba"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a68cf6e7baf185f831b3eccdbfe91b7d48ad6af78b4b929d6df89d92d9a4ca62"}, - {file = "jh2-5.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7669f95cfac416a3e97b224d214c9838bb5b6b9e35a1892637337fe774d0406b"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:af5560b7856c6a1e17071d86e16e288f6ca3f465a29fd683347f84cf4ad8a99b"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:769f569fd7dfbe027ddf28c5c73dda48494bb3ffef0be6f60d5661fa6c754da9"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-macosx_11_0_arm64.whl", hash = "sha256:48befaf5dd60aa3b623e8cfafdb097516820f9b5c2ae38d399b6e4eed1cfdb46"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac6259ba5591120b3663089ac1ba9a521249794d753343c59447808b744e2755"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dc1f2442d6eb9ab8f54d328ec938918ca273290d1e99329387967dd74e1b054e"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:557c40acd62c51001cdad2674c5a9d400127796ce7fbe8d82698ff8bec478092"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e175649572bc2a7c60a7e03d8bd1d42d0f64634d31d0d7930b65e1b31f8e43d"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0125bbc6d41bd55f088e952fbfec0652eb0ac45632604d6644c45e2ccb83507b"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ecdf049246a0bc01bd404737b1506770577e982802872539f7734368877623a"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:70a401591577502f3efb2fb247378fa5a0adfd48578e15f7365d6db656447e66"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3ba6a00394c6cb5d1409fbb910eba2ff2a216bb553af198ccc8f64af63133f67"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4eb16b0f95181c7bb19115446b5987b694727c6afd618ee900e895a54d101188"}, - {file = "jh2-5.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e967d4a1bdb7a8726e0349dea82eba530dbed8fed4cad118f81e867f84c0446e"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2fcc906639e5987ee0eacb12ba5a7678ac76677986dbeeb41dc7d1b44926ac43"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:39dbcd14e8313370c83287080ddac77a42cace50ef223852d6b0bea73df8ac3e"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0ceddd1be2a9153d5eec25ac3aca846f35bc171325e549509cac75ecd26930d"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8aa14f5b3077526fed01464e4400860c9f88f73b0499f87e50e4cbf0d851def"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:89cec07a5e15660c10c29537d6df2b61daf9673cbd014971664b50d3c5eedc34"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fc81fe2ef8a2c011a67468a169790973d0595731599c7c21d0d223b29c886d43"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:024862bbaa795549823386bc30b0bb7c1807df5df75b4e5ce97f216812796fca"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7417bd01c897c5afe9da8270ca4fc0485378a05bee0d4c0ad435c48aee841ad"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f67738cb9e669c3b28ab1f7c611df0c3e5cb6da06df5a7f303e8510b890764a"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fa6d7060c225d2f1c888ecb8e834a4685d4468fe11968d56a5eb3376e45d287c"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:32f0700ecca8707b9561328705efa9ad1626ca829b2c4964e753d579337d53c2"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:969f62d321bd82207be836f99bbbb854ec5596ca82a2c905c404fed5e4a9289c"}, - {file = "jh2-5.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1d1194a66b25fea05f34e0bd1cf5ffc72ec67fd3e584d4c1293e9962815ec180"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b1b47bba88666287ad998dd561292dbe38ad767072a4ecdfe90a98f2f2e95579"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:97a57f39f6fbfb96966edf0d6879afed5784582573644cd5c2f58907f6e45759"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0ab31da34be3c726acda1eed2fd9ad9daf14f4f45bb15fdce7ed877ce82f677"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:090755e41a0c0b7021dfdfda46970e3920d6524a528d0456a0759310b2cd3a30"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e8903186182de97107260540796f6579b7322cb82d7d3e8aca11fd09c2847fe"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:85714f220fbd7ab7d17ab83cbbdac4d43e0fc699738da06b018a9aa01adad5f0"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d59abb4b1adfd9e7f0b6df22f4f6ea260128d7605110a39bab29dbed32a5ab28"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe75dbd44d1c53341acd5505afd29af8172e7ce67a7d21fd4466d7db26be3766"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c58aafe13f208769f16fa612095a64aed27b35a241eaffcef10c105b5c48c03"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:83fa9a815edbe52b6c0c096a20c1bb3f9669b706e8a69775dae63c973a60b2ea"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:b4d6d2e8a14526951b15cbeaae89625838570affb429ab90df32b6a34a7d417b"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ea555676ce7eb72d5be9b5107286c83c72cc12bf7dbdcc9d2381672451c077a4"}, - {file = "jh2-5.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ac1e1db4a92fa1b4625e57de2ad36b6376dfcc53be6298afbd82ee96f172d80f"}, - {file = "jh2-5.0.3-py3-none-any.whl", hash = "sha256:4ac75f013a1600d8111306fa5d3b35ce08bbbd8fde2aca096a0ddf6a415e999f"}, - {file = "jh2-5.0.3.tar.gz", hash = "sha256:c13d97a3f82a02e6a2a89606f1ffe1771670266dc7746140e00e66c4dad12b14"}, -] - -[[package]] -name = "kiss-headers" -version = "2.4.3" -description = "Object-oriented HTTP and IMAP (structured) headers." -optional = false -python-versions = ">=3.7" -files = [ - {file = "kiss_headers-2.4.3-py3-none-any.whl", hash = "sha256:9d800b77532068e8748be9f96f30eaeb547cdc5345e4689ddf07b77071256239"}, - {file = "kiss_headers-2.4.3.tar.gz", hash = "sha256:70c689ce167ac83146f094ea916b40a3767d67c2e05a4cb95b0fd2e33bf243f1"}, -] - -[[package]] -name = "librt" -version = "0.13.0" -description = "Mypyc runtime library" -optional = false -python-versions = ">=3.9" -files = [ - {file = "librt-0.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34e47058fcc69a313293d6dee94216a4f30c929ae6f2476e58c5ba635aa639d5"}, - {file = "librt-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dbdd5b6509d0c2a8fe72cf494c299a61dbd58142a90a4190664ae159e4a7b547"}, - {file = "librt-0.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e56ea4ee4df77585a6b5c138f6538680886024fa559f5b55bd14b12e98e67b2"}, - {file = "librt-0.13.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f1f9cc4d09a46d9cb3c2063ae100629d3f52a6517c3c08c2f4c9828261883929"}, - {file = "librt-0.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f125f5d46b20f89dc5587a55cc416b4ba2a5b2ffda36d048ee120e17598a653a"}, - {file = "librt-0.13.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2608d3b39f9e0b4a66a130d9150c615cba40a5090d25eeeaa225e0e46de8c0ac"}, - {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fd35e95ab5e45c3901d37110263c7db85a961110f5460588fe37f8c131f88a7"}, - {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5f31b0aa13c9b04370d4da6be1ab7779776b3a075cceb6747a39a4be85fe1e40"}, - {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0b795f5fc70fbbb787ceaf79bb3a0d627bcc33c53de51741755263ec406b775a"}, - {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36b306a623aaad96fe4b378692b54f9c0789fccd833b9851753d5fbf6138cfde"}, - {file = "librt-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a3762e75fcac8c9e4dacaaf438bffd9003e2ca2c531b756f3c0035deefa674c8"}, - {file = "librt-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:d63bae12a8aeb51380be3438e4dc4bd27354d0f8e19166b2f44e3e94d6f552dc"}, - {file = "librt-0.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1b5a7bbff495baedbd9b916c367d66854008f8f3b575908ded477c499dc60082"}, - {file = "librt-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34bc7938b9fdf14fe32a406c19c71faf894c5cee7e7474bd0be2f17200b82d14"}, - {file = "librt-0.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f40e56b61b41be5f7dec938cfeffd660668cf4b5e72c78e7bd671d66b7bc2c79"}, - {file = "librt-0.13.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:9c5d02b89de5acd0379a51ec44a89476fb03df6145442e1c8ecd6bee2f91b176"}, - {file = "librt-0.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7db9a3ff32ef5f7d1703d93831a3316cdf0b537de6a1cc03cc8fdd09b9194e89"}, - {file = "librt-0.13.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3dbb2a31882456cadc7053378e81ad7ed7693db4ac9f98ab5f81ef034aa8ec9f"}, - {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c6014e3c80f9c1fe268ef8b0e0ef113bac672cc032f2f93866e7ddad4f3e663d"}, - {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:091b60a4d2174fc1ec5c34cdc0b72efb6224753d76b7da61ebeab7a191aec8bd"}, - {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:66cb1138f384a191a6d75f986064841fcfdc0cea98f7bd9c9ab9b38049917588"}, - {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:17221a7569f8f292aa0014226e48aa25b8c2b08da18088cd230953d0ea0f9cd1"}, - {file = "librt-0.13.0-cp311-cp311-win32.whl", hash = "sha256:fc67741da44c6eaa90e01eafb586bbba9b51eb5b6ed381ee6f5ae72eb3316d21"}, - {file = "librt-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc99dfb62b23c9207c33d0be8a2e2af7a42e21e6ea388b380a0c948c7b88953b"}, - {file = "librt-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:40ccd13c252d3fe473ffc8a57be7565abc8b64cf1b108344c859d5164f7f3e0c"}, - {file = "librt-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30536798f4504c0fad0885b1d371b0539abb081e4570c9d7c641cb51141b49f0"}, - {file = "librt-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93d24ebb82aa4420b1409c389e7857bc35bd0b668007ac8172427d5c73cc8cc5"}, - {file = "librt-0.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb8a1adce42d8b75485a5d56a9623a50bcab995b6079f1dac59fc44034dd93d9"}, - {file = "librt-0.13.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0763ca2ab66058174f9dee426dc64f5e0a89c24a7df8d3fe3f1836c04e25de4b"}, - {file = "librt-0.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b222493da6e7b6199db9bd79502436cf5a27da3c1f7fa83c7e285444fc93fd03"}, - {file = "librt-0.13.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fadc63331f4388c3dc90090448f682a7e9feafc11481391c1e94f2f907a3976e"}, - {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70d9c62a4cffd9f23396cd5ef93fc5d11b31596b9b7d6306074abe3d5fcf09bd"}, - {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66c0e7e6b02a155576df2c77ec933a70b72da726e248c494abf690923e624348"}, - {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ac04bcd3328eb91d99dfedf6a60d9c1f15d3434e6f6daf922f0420f7d90b85c7"}, - {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db327e7271e653c32040b85ae6188059c924b57d7e1e29f935523fa017cd4e82"}, - {file = "librt-0.13.0-cp312-cp312-win32.whl", hash = "sha256:860bd1d8ba48456ce08feaf8d343a8aaeb2fa086f2bcaa2a923fa3f7a3ff9aa3"}, - {file = "librt-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:e54a315caf843c8d77e388cadc56ea9ded569935ee2d2347d7ea94992e5aa6fa"}, - {file = "librt-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:c718e99a0992127af84385378460db624103b559ab260435abcfe77a4e4ed1c1"}, - {file = "librt-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a468951af16155824e88bdd8326ebe5bdb371f3ec0ac04642994b98201d914f3"}, - {file = "librt-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae01d8512cc17079e53425635327dbf3f7ff57a42c00dec348bf79791c56444c"}, - {file = "librt-0.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32c26893cd085c1efe83219e78d866da23fb20a066101b8f68210004361d224c"}, - {file = "librt-0.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5929da1981a46bcf4b28b1b9499905f0ff58e2419da402a048234e9783acbc4b"}, - {file = "librt-0.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b85d664d777bab6c0d709416cb42938251fda9e221b79e3a2215d85df5f4f9"}, - {file = "librt-0.13.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:531b2df3e9fe96b1fcf73a6d165921e4656be5f58d631d384ebce344298368db"}, - {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:109b84a9edf69ad89dc1f66358659e14a031baca95e3e5b0060bd903ede8efd6"}, - {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1304368a3e7ffc3e9db986796cc5326fdb5943a3567ecc137cff318e4240c0e7"}, - {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e4f9b472e7d308d94b62c801982065661158c6ed02790d6c7ddb4337cea0f9c1"}, - {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f836c37478f167a81200d8c8b2c920a22224564bed2c23d7aeec760965c367a"}, - {file = "librt-0.13.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:4000d961ff9598ac6ea603c6c836a5ed49bc205ade5fc378b998dfe1e2c36628"}, - {file = "librt-0.13.0-cp313-cp313-win32.whl", hash = "sha256:79e44cff71750d299d61a678e49995b0d5935a9cda238c2574daeca3ba536927"}, - {file = "librt-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:54dab44a847d5ad1acd05c8a83fe518ae685516ecf4d3f7cc6e3df2a66767650"}, - {file = "librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566"}, - {file = "librt-0.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:25218d94b1d2cbc0ba1d8a3f9dc9af578d9646e5ed16443a70cde1dfdcce6d71"}, - {file = "librt-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f26629539d4893c2957a16c41bb058e1e135c1f150f6a2e25ed047f64cf3f5c6"}, - {file = "librt-0.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4517d47b2b8af26975a406fba7d314de9696d864252e0257c6ea90238cfe27f"}, - {file = "librt-0.13.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f19e181de5b3a1148bb3420b8c4b0b0ea0fce6950099724ad151d6cea5acc180"}, - {file = "librt-0.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22034924f5b42d5a56371cf271771bfeaabf235a7a8b6264bef2d20013f786c6"}, - {file = "librt-0.13.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7897db4e95e22468bdda33d8e012ceacd0182abf001e6389d763f0def6286b9"}, - {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1ce61b3746545029d4f5c17d6bd74b676254ad98433086c846ffb5e8fa73f007"}, - {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:46c330e82565962c761dbce7941be2cff7db674ee807455a8d0cadc5f9b759b0"}, - {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:375f5af8f99cbaa99dd293af986e3d57caabc9ba81a5d3f021603764854197a1"}, - {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9320d34c3376ae204b2cd176e8d4883a013934e0aef822f1aed9c536490c275d"}, - {file = "librt-0.13.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9af313c66157a69dc69ea0059a66961692250e0dc95af9c385a48ffb770a0d16"}, - {file = "librt-0.13.0-cp314-cp314-win32.whl", hash = "sha256:f2a7253458e34f33543551394ae4fe104b497ec2a65ac266074de64c1df82e37"}, - {file = "librt-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:a3dfe4edf10e8ed7e55b026a8bfc2c2a8704218b659cd4bffdf604fab966dc39"}, - {file = "librt-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:68a5faee4bba381cb93b5961f684a514cf0053cb92308ff9c792c2fea0b174c6"}, - {file = "librt-0.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a38fb81d8376dfa2f8963b265fec07637802b0d01e2a127c19c66cb070fb24f5"}, - {file = "librt-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d4c8d9bd5abce34b2e75edb3bf37ab0f34e49b1f915a40ae8468eb7c85bc5b46"}, - {file = "librt-0.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:387e2f1d27e89bffe0d3f520f0da0662c973fd607ca16c1808f8a5085419485e"}, - {file = "librt-0.13.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:4f6db193d2e5e0ed60359b9a5a682cd67205d0d3b1e459a867dd4b5c4e7eaa7a"}, - {file = "librt-0.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d38604854e8d22faadf683ec6c02bb0f886e2ba56ef981a1c36ee275f21ea22"}, - {file = "librt-0.13.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:371f7ce73026815dafd51c50ce38416e91428b28c4b2ec97cd39271164b0045c"}, - {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3aaedf52171bee90860704c560bc798fe83b76247df47568e0197e9b13c735a0"}, - {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:96bad8725a4f196a798366c25ce075d1f7543a4ec045ffc13e6a7ec095cdab04"}, - {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6bf6a559ffe4a93bbea6cf31ddf01a7fd9ba342ef51f27beb178e318b74acd61"}, - {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:301067672387902c55f94b51d5022304b36c966ea9fe1f21caab99a9bef487c9"}, - {file = "librt-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:5fdcf34f86de8fb66d7dc7589f96ba91c4aa46671200d400e6fd6f109a483f18"}, - {file = "librt-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:260c33e92263fa629b4f6d3c51967a1c2158fe6c33237aaa3ebeac586b085259"}, - {file = "librt-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2f281549a4c52ac7bb97997f14353f8bd0e53a34ca0dad1c905cfd0b4a58ae99"}, - {file = "librt-0.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f442e3954b1addc759faae22a7c9a3f1e16d7d1db3f484279dc27d62e06968fa"}, - {file = "librt-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e786428f291dd2d2f1cbfc0e0caa45a2e395fab0ad3e2c9314daa8873414390"}, - {file = "librt-0.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21b7ac084f701a9cdff6139745a6620579d65a9379ac2d9d50a86368b109e63c"}, - {file = "librt-0.13.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a6e556d6aba31c93dd97ce661d66614d2429c0a3923f9dc8f0af7e8df10223a4"}, - {file = "librt-0.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3657346f867469e962549435aa05fd15330b1d6a92829f8e27988e194382d005"}, - {file = "librt-0.13.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:791aa18a373b90da8ac3c44fc77544f33fdf53ae403acdce9b39f1c26b4a3b94"}, - {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d6fb0eaa108814581c4d3bfbd068c3fb6757812a81415008d1bae08267cca360"}, - {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a001519c315d5db40710f2665d32c4791f1d4779fc96a9423fd18d92c8b9ac7b"}, - {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:d9188caac26e47671b52836a5e2a49873a7fc11c673b0c122d22515f98bc14e1"}, - {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:05d96b80b95d3a2721b619f8982b8558848b04875bb4772fd54842b59f61dd97"}, - {file = "librt-0.13.0-cp39-cp39-win32.whl", hash = "sha256:c3cd253cf32fe4f4662960d6bf7d55cb8be0c31a5d644a4d48aeafebaff3409a"}, - {file = "librt-0.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:b15e26cc0fe622d0c67e98bee6ef6bc8f792e20ee3006aa12627a00463d9399f"}, - {file = "librt-0.13.0.tar.gz", hash = "sha256:1d2a610c14ac0d0750ee0a3ab8548e83155258387891caaca04def4bf7289781"}, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "marshmallow" -version = "3.21.2" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -optional = false -python-versions = ">=3.8" -files = [ - {file = "marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1"}, - {file = "marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56"}, -] - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] -tests = ["pytest", "pytz", "simplejson"] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "mypy" -version = "1.19.1" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, - {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, - {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, - {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, - {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, - {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, - {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, - {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, - {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, - {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, - {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, - {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, - {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, - {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, - {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, - {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, - {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, - {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, - {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, - {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, - {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, - {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, - {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, - {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, - {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, - {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, - {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, - {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, - {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, - {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, - {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, - {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, - {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, - {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, - {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, - {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, - {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, - {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, -] - -[package.dependencies] -librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} -mypy_extensions = ">=1.0.0" -pathspec = ">=0.9.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "niquests" -version = "3.6.5" -description = "Niquests is a simple, yet elegant, HTTP library. It is a drop-in replacement for Requests, which is under feature freeze." -optional = false -python-versions = ">=3.7" -files = [ - {file = "niquests-3.6.5-py3-none-any.whl", hash = "sha256:8a2334da25001ef0db044ab0198d39704409e4c2d442ca18c5abd7ddee2ffd6a"}, - {file = "niquests-3.6.5.tar.gz", hash = "sha256:3895ec88e96c3050e11f79b6a6855ede8147f6fa2080ef74b2cc0a3bbe379653"}, -] - -[package.dependencies] -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -kiss-headers = ">=2,<4" -urllib3-future = ">=2.7.905,<3" -wassima = ">=1.0.1,<2" - -[package.extras] -http3 = ["urllib3-future[qh3]"] -ocsp = ["urllib3-future[qh3]"] -socks = ["urllib3-future[socks]"] -speedups = ["orjson (>=3,<4)", "urllib3-future[brotli,zstd]"] - -[[package]] -name = "packaging" -version = "24.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.2.2" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pydantic" -version = "2.7.1" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, - {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, -] - -[package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.18.2" -typing-extensions = ">=4.6.1" - -[package.extras] -email = ["email-validator (>=2.0.0)"] - -[[package]] -name = "pydantic-core" -version = "2.18.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, - {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, - {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, - {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, - {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, - {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, - {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, - {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, - {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, - {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, - {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, - {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, - {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, - {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, - {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, - {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, - {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, - {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, - {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, - {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, - {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, - {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, - {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, - {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, - {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, - {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, - {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, - {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, - {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, - {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, - {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pygments" -version = "2.18.0" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pylint" -version = "3.2.2" -description = "python code static checker" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4"}, - {file = "pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8"}, -] - -[package.dependencies] -astroid = ">=3.2.2,<=3.3.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, -] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pytest" -version = "8.2.1" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, - {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.5,<2.0" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "5.0.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, -] - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] - -[[package]] -name = "pytest-runner" -version = "6.0.1" -description = "Invoke py.test as distutils command with dependency resolution" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-runner-6.0.1.tar.gz", hash = "sha256:70d4739585a7008f37bf4933c013fdb327b8878a5a69fcbb3316c88882f0f49b"}, - {file = "pytest_runner-6.0.1-py3-none-any.whl", hash = "sha256:ea326ed6f6613992746062362efab70212089a4209c08d67177b3df1c52cd9f2"}, -] - -[package.extras] -docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-virtualenv", "types-setuptools"] - -[[package]] -name = "pytest-watch" -version = "4.2.0" -description = "Local continuous test runner with pytest and watchdog." -optional = false -python-versions = "*" -files = [ - {file = "pytest-watch-4.2.0.tar.gz", hash = "sha256:06136f03d5b361718b8d0d234042f7b2f203910d8568f63df2f866b547b3d4b9"}, -] - -[package.dependencies] -colorama = ">=0.3.3" -docopt = ">=0.4.0" -pytest = ">=2.6.4" -watchdog = ">=0.6.0" - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "qh3" -version = "1.0.7" -description = "A lightway and fast implementation of QUIC and HTTP/3" -optional = false -python-versions = ">=3.7" -files = [ - {file = "qh3-1.0.7-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:392cbfc95c832f78b840a5b17d743d4dcf8d47d7217d17370b939a8717939fa6"}, - {file = "qh3-1.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:75bea34952369975a646379aa2b6438f557b4da7a76ddb59973000d96ea8063e"}, - {file = "qh3-1.0.7-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9d40552d28eaa89c819edfc3bc3752ed3d7da59119840d8fe09790b9c76f5819"}, - {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f1283bbab24b26565fb9b90278ea96c7940396d31ee9fac169e9c7e1b36fd96"}, - {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f7f910a1523b1d2f16e20034452b63335f90431868365bee0fe29d8e6473438"}, - {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:92d87664b758bc1f8b4bcfa60cce866c1e96938d59af3de25189661e263cc510"}, - {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6f56c34e8d7009596a61a800c80f478a9e9ceccfdc11b28544b59ad904a5ff6"}, - {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4da2ba491c7fb240556a996b0a10cf7b6ce17816a9c2a53a02b08a40eb2fa37"}, - {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586cfd89068fd44e5471b31bd7d4c4b01b80a85ac28e26b14da6ede4583e8017"}, - {file = "qh3-1.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1835f149b1d359a9de915207bc0543004edcb3077390614bca5d52c637914ad3"}, - {file = "qh3-1.0.7-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:1e7618c6059a5f838b858ea834ef6bc66d1e95bdeb8ea466217d74aedf471415"}, - {file = "qh3-1.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ebf612690cfefc66d19648f67bd0a7a83112230c322e21245a5536ca05aa9564"}, - {file = "qh3-1.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7ef77f2663f60ac3b46fc13ad67076dcff8dfe37886c1793fa7411c50385b061"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4fb6c73399313881b80708354af6ac5d837cf9733189476f919d7c9b88e5ccba"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4bbd1a4a5b49cbcfd02a57b07eab729cec7b4a75f2180a03fa0d200d46833cbb"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0ccbcf47a1311deece090ac6c83918741be4b568f7018748d3f3762c70a45c3c"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6e5871df72c6303e9c68e8ab50d25152fc4d4ee0d8ebdced20724922fbaeb53"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:981725de5210eb8f902fa5015b029072a532c4dd44770bc6d2b45b569b91212a"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4018dc5023738b4da756297787e6606a771777b576d61da95226c2ec0d5d50ef"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:debbcd38adf35bb2589095f78ef59690f507b1fd6aa71440c80ad17a15485fbc"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c83a719c48dbd3d8d592193e83360c11657a5c1bda31e519a8b8d7b1f60ae627"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6487edea20ac7bea0e820bafb9bd4957d80a341facd7d64163953adcf560bf95"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e70de70904cca4f53dec7ddbc15e8d2fe0e4cad01c05063cddb1087460a367a"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8e03f81f60ef0772e2a32e3e56fbcc907f111f4057909fbb013e2f65516b5799"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89ece443484baff18ce12d08cbcf5fa8bcfbfbc6517e1f2e020666c6adf003e9"}, - {file = "qh3-1.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0ce94a6fbd6bd2715e66d28560af8c41bd99bd419bb89abc45f761932dc8cb54"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6c8b427f31c486fc63898ec15b5d12636f29d09275e186da5cacbf4296143e7d"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8bf1b4f50639ee5277e4baef3740c4790f59760c4a537cef9e8096b2c7c2ca69"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-macosx_11_0_arm64.whl", hash = "sha256:acb42fc6d6251ee9af39a4330c9fe3a4958517b76ee7f4c66787a8b099bdf0f4"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5a807d4bac257e1e68e690464444361320ce730f68d8d1821a1e70a3cd795cf"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d51d9402d5544540363424cfe9c06f7c1e7e0b1bfaaa7128e5034d27a0f2cef"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fbcb8a29285b8a9d1587948b30f2520bc564d0341f860c2fc57a5d53a77a2e23"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0753e77e9bbb6140c05adb3fbd19dbba6b2f5f27598e608e2a543165ede4b337"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25a1e9a27efd7fe55e521f4f68b9bc7a4ef6b1a0f51cb8a98134fcf6e9bec6ab"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6ad8a4c31b5579b65482172e7e7f945ec7b29030046ce29d7fce396246fbb7"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:43911c6f206ac2654b857bef14dda7394509f9746f145ccb4e496f32a9d67422"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:bda728efcf03e49ec16d1f0a8d3953fcc113df5d3055b63c838d7dc7834c357e"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:03f7ea1cedacbfe6252f202ac65d0040a8942ce627df241bf74a0f2ad7c4a73f"}, - {file = "qh3-1.0.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5f82a48b9c06b51ab482fedd4b0476b0c0ce0f4a0d47c5fef924adc8cd795a89"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:091247b9bb24e6abb055772a502ea70857e7dabdf1dd993053db680c7bdd3718"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c73458b6a79652a63a64b715d8047f07d7f61ab091a87f32c2303f9cb9d77bbd"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:733553159d65b447bf7c7aa84cde6b158e057025b602192c43b41083f3e06b4a"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308154b300b28436a415f2163b942e0c809f8cbeede1a940be563d64462e35b2"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d5db32a2a9ea4210bb5a3c6c037e02d11f6709be38e077eee77cdf0e626dc7f7"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57d3013bb1f03cf1a2c8a1bb7d536722eaa059c58d3f6918fc5b62f8469d4f6c"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaca92cfbaabf2a8792325243e63cf7512c4760aed2371a5fc9ce32612245bfe"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7da2e218fed81602d4f1034cd9698afee2c56270b1520cfde09e14d47dbdb2d2"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04a0005c4ccf5d9510491d5e731021af1f764c23e2bb456f3b5674311a126e90"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:15b54ed14df131266e65d7ef09b7bb3f1fb4d74d4fb632b51d233c58be050368"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:bf675b50dbf971d21e8fc4ccf6fc18c77942f6ac20358ac6ca3a98857e467f42"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a438f7cb5923ec519c3a1bd4e8db67067abe5d1d3d0499bffd19213b52ee654f"}, - {file = "qh3-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:53ccddf58ea50250609da3753977ad501783fa0cf2dc15960add5c15e2313b47"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:168bd4f481c6b7f780e4f46f399d6c7faadebe4227b5130d0d7c335d10dc13e3"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:188ef7bd33b1b85af93058ce365ff7b65377a99580fdc37bf9d18de98ac90ebd"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:63407c2c680ac4a713f502a23658e9e26818133312b78765bb5efe21978feb02"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:703775328761d10a6ba2e412a6c44304e28b4d05799765496c7a21569ac7f732"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffe51161a782c99cfe792100af43f8577ac3f9fcc247e80a860981cd8e4bc8f7"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:567a2a76b2c5ef42a2c9adef9596c8ebbdb614660363bcd020096ea38969bede"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9aa47c11d2d4c2ed75506333fd83c481017f6da7f45002e24e391842d8dc989a"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67433e71e5bf6a76f41f1bde9c69aaf017ae3b4ae5e9f3e4a123a45619dc5783"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2569550be0faa50e723a1bbf3a9061b66a7546d514fea81bb9125e16f6a7d37"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a5aea4ed5b8880210aa34957c33b4c576b2e68b9cbc48013974d34a41d71d17f"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:be0675ced825a4a659888541060031621b575a99bee4efc1e435ffa7815a696d"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ecf9eb27fcbbd9c8cf4a374c5e0d034ec0c3e576f1ed028f7ce84a3484fb06fa"}, - {file = "qh3-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:89f00a47cef7197ea6367bb0a1ecb9517aeaf8c8dd26202cfbf3e3346d0f4aeb"}, - {file = "qh3-1.0.7.tar.gz", hash = "sha256:eb527d8317746209509b9c575527577cdc9b3cfb0f49294fc1cd109b0570362c"}, -] - -[[package]] -name = "rich" -version = "13.7.1" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "rstcheck" -version = "6.2.1" -description = "Checks syntax of reStructuredText and code blocks nested within it" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rstcheck-6.2.1-py3-none-any.whl", hash = "sha256:b450943707d8ca053f5c6b9f103ee595f4926a064203e5e579172aefb3fe2c12"}, - {file = "rstcheck-6.2.1.tar.gz", hash = "sha256:e4d173950b023eb12c2b9d2348a8c62bef46612bbc7b29e1e57d37320ed0a891"}, -] - -[package.dependencies] -rstcheck-core = ">=1.1" -typer = {version = ">=0.4.1", extras = ["all"]} - -[package.extras] -dev = ["rstcheck[docs,sphinx,testing,toml,type-check]", "tox (>=3.15)"] -docs = ["m2r2 (>=0.3.2)", "sphinx (>=5.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx-click (>=4.0.3)", "sphinx-rtd-theme (>=1.2)", "sphinxcontrib-spelling (>=7.3)"] -sphinx = ["sphinx (>=5.0)"] -testing = ["coverage-conditional-plugin (>=0.5)", "coverage[toml] (>=6.0)", "pytest (>=7.2)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.0)", "pytest-sugar (>=0.9.5)"] -toml = ["tomli (>=2.0)"] -type-check = ["mypy (>=1.0)"] - -[[package]] -name = "rstcheck-core" -version = "1.2.1" -description = "Checks syntax of reStructuredText and code blocks nested within it" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rstcheck-core-1.2.1.tar.gz", hash = "sha256:9b330020d912e2864f23f332c1a0569463ca3b06b8fee7b7bdd201b055f7f831"}, - {file = "rstcheck_core-1.2.1-py3-none-any.whl", hash = "sha256:1c100de418b6c9e14d9cf6558644d0ab103fdc447f891313882d02df3a3c52ba"}, -] - -[package.dependencies] -docutils = ">=0.7" -pydantic = ">=2" - -[package.extras] -dev = ["rstcheck-core[docs,sphinx,testing,toml,type-check,yaml]", "tox (>=3.15)"] -docs = ["m2r2 (>=0.3.2)", "sphinx (>=5.0,!=7.2.5)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.15)", "sphinx-rtd-theme (>=1.2)", "sphinxcontrib-apidoc (>=0.3)", "sphinxcontrib-spelling (>=7.3)"] -sphinx = ["sphinx (>=5.0)"] -testing = ["coverage-conditional-plugin (>=0.5)", "coverage[toml] (>=6.0)", "pytest (>=7.2)", "pytest-cov (>=3.0)", "pytest-mock (>=3.7)", "pytest-randomly (>=3.0)", "pytest-sugar (>=0.9.5)"] -toml = ["tomli (>=2.0)"] -type-check = ["mypy (>=1.0)", "types-PyYAML (>=6.0.0)", "types-docutils (>=0.18)"] -yaml = ["pyyaml (>=6.0.0)"] - -[[package]] -name = "shellingham" -version = "1.5.4" -description = "Tool to Detect Surrounding Shell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "svix" -version = "1.24.0" -description = "Svix webhooks API client and webhook verification library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "svix-1.24.0.tar.gz", hash = "sha256:02a5daf20123cfa20f5b9302e77df8d1f9e8223430af0ad51c2c544a0ba549b2"}, -] - -[package.dependencies] -attrs = ">=21.3.0" -Deprecated = "*" -httpx = ">=0.23.0" -python-dateutil = "*" -types-Deprecated = "*" -types-python-dateutil = "*" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tomlkit" -version = "0.12.5" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, -] - -[[package]] -name = "typer" -version = "0.12.3" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, - {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, -] - -[package.dependencies] -click = ">=8.0.0" -rich = ">=10.11.0" -shellingham = ">=1.3.0" -typing-extensions = ">=3.7.4.3" - -[[package]] -name = "types-deprecated" -version = "1.2.9.20240311" -description = "Typing stubs for Deprecated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-Deprecated-1.2.9.20240311.tar.gz", hash = "sha256:0680e89989a8142707de8103f15d182445a533c1047fd9b7e8c5459101e9b90a"}, - {file = "types_Deprecated-1.2.9.20240311-py3-none-any.whl", hash = "sha256:d7793aaf32ff8f7e49a8ac781de4872248e0694c4b75a7a8a186c51167463f9d"}, -] - -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20240316" -description = "Typing stubs for python-dateutil" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, -] - -[[package]] -name = "typing-extensions" -version = "4.11.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, -] - -[[package]] -name = "typing-inspect" -version = "0.9.0" -description = "Runtime inspection utilities for typing module." -optional = false -python-versions = "*" -files = [ - {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, - {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, -] - -[package.dependencies] -mypy-extensions = ">=0.3.0" -typing-extensions = ">=3.7.4" - -[[package]] -name = "urllib3-future" -version = "2.7.910" -description = "urllib3.future is a powerful HTTP 1.1, 2, and 3 client with both sync and async interfaces" -optional = false -python-versions = ">=3.7" -files = [ - {file = "urllib3_future-2.7.910-py3-none-any.whl", hash = "sha256:364a42fb806242ada6ba55123a653a1e8fd72b9c6436422caec5f07cb8777507"}, - {file = "urllib3_future-2.7.910.tar.gz", hash = "sha256:480d04d20061878d3a275a212d708ab854f7a2bf4c98a2286861cff812954189"}, -] - -[package.dependencies] -h11 = ">=0.11.0,<1.0.0" -jh2 = ">=5.0.3,<6.0.0" -qh3 = {version = ">=1.0.3,<2.0.0", markers = "(platform_system == \"Darwin\" or platform_system == \"Windows\" or platform_system == \"Linux\") and (platform_machine == \"x86_64\" or platform_machine == \"s390x\" or platform_machine == \"aarch64\" or platform_machine == \"armv7l\" or platform_machine == \"ppc64le\" or platform_machine == \"ppc64\" or platform_machine == \"AMD64\" or platform_machine == \"arm64\") and (platform_python_implementation == \"CPython\" or (platform_python_implementation == \"PyPy\" and python_version < \"3.11\"))"} - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -qh3 = ["qh3 (>=1.0.3,<2.0.0)"] -socks = ["python-socks (>=2.0,<3.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "wassima" -version = "1.1.1" -description = "Access your OS root certificates with the atmost ease" -optional = false -python-versions = ">=3.7" -files = [ - {file = "wassima-1.1.1-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1f6f5198ddb3d68d7b6fe9229c55a2c83cb56232b72dcdd4b2ebc7540138aa20"}, - {file = "wassima-1.1.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:583e9e510cb88f067e9a48b39ac58549a258623d1e07eb6bb0512280a10c0e8f"}, - {file = "wassima-1.1.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:59c73287561dcbd0102ff897b136af0ef5e5879192f0908c597d85b8269701b0"}, - {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50afde20c335c18c39a47ff9c9d1c481864275e6ec1fb50b23ff4d693428b4fd"}, - {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b2edfdc6ec4f07a35fd4b84c2b282f856062a832ced48eddce1a44c82525f275"}, - {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f41980d3d83bc446e28822c2f4a395f787814273f623811e4ed1035dfde7b267"}, - {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a04100cdd5ad8f7d1e85b1b679bc7b5db95c788d24aed6e8b63a8ad47fccf62e"}, - {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af205bfad17f814a05341bc1889ff61c40f44b37139c58c9c47aa593e83e2f"}, - {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdba17f5d0844468c8bb7b5103505985bdedcad5dd90cd722475386f115332d2"}, - {file = "wassima-1.1.1-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:32bca7ed733a94c32d6c78d10c902dd4dc2dcf5785b560d82915de1544e076ce"}, - {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ae474c2458addbe1b657ee080dc73a52ef002504c055f71176a246aecf2b714f"}, - {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:c2baa1316b40044caecb9fd5f574527784673b809515668bb7d9631c29f03d39"}, - {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:bc2d94894c6c270787b010b12243ce4a2ffe6a62b1f5682c912ea4540fe5d6df"}, - {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8f81f8dc770e7988321003776b2d3fff65339de4216e6f9b39d831333390c728"}, - {file = "wassima-1.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:878ae83bb9130b0c86426fc7682b397e6c8fd7a0457c277485e96b792f7f12b1"}, - {file = "wassima-1.1.1-cp37-abi3-win_arm64.whl", hash = "sha256:491d0541b5995618efc85c9b249f1a9fa33744973e42bd523bf52c01adcd078e"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:84a1607d508024febacc192414797daa0bce64bbf24e7ae93e182aecadaf200a"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a63866dec44e5d0ef9606b43a33d3017d0de7d931a128a3d1b0c28ad5714e8ce"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:85244bda65f8a39ad700aa941f1f0123f588933c79afbb4f65f600fa6c83863d"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:665f0b4a424b56e907bd579d8144b339f1383a6dd53bd4ebdd6b6f2653cd6d3e"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:41630a1f13b356f19cda2ab406ca93185a426fc65ba608d58000c125f07cdf2e"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:028f07252188a7b6a1e73c955959b3028d6f5633e798eb6b27ee96716c62ab31"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec076533caed061bdec324b0536bf01b704edf4a6bfd022462bc64233d9660e2"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c99e98dd7960d7dd7342f5fa569a4cfcd5e9333f74f1a9115a483fa6c03a5f8"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1aa554e79832c8f94fbedc77a53ff4207cfce45ebd0c3a550d6c0eb8fbd82118"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7cac5cc91a6dc12b675c6749e5e0a36f35b4976dd37df36d50246fd8c3707866"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2763d8f5cc58c4a9ee986c39709d3af91a9dc215c8a8895b992783a828a35824"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8d2d8cbec28a9f810207ca2647a0cf6c674b95162f2269d35846421d5948a1e6"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:0353ba99c1db703902d084a81875ed3e06a3831f3eb039240607198e8f6bbb5c"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:95d1b7144286c090b943fc42711da922384f7e4e629a5dd2e8f05b66d008cf76"}, - {file = "wassima-1.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bdb76ee185736ed98ed92b8200b380b732b4e3df08219ea9491c6c7c9a983778"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1f7d14d3019f179b5747168282fcd4c0cc3a2363eb974bcbc532b546a4513c24"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e26e96df5d5358f093314ac162aed33ae3c04d80e9bdd1c1c2cd07c8e0109f36"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-macosx_11_0_arm64.whl", hash = "sha256:18857ddbc6d257541027ffc59dc6e87c21bf0ba5a10abde5073d01cec0352faf"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86fc536580e31c81717212751fab16ff962f16302f71a71a2786a4c743ac1e1a"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2ce27271a38fd21e2dc3cc5167431a63473ebeec1c02975e4000c95d04e35a9"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4ed19ff2afc209ea837fefbfe9eec01eaa6b8af46fe8dbdb8a692a894646013b"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c08969d10347b71b25c31e9ce73416002ff9d27fa22aa1e652ecd8cffda0a92"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c24eea8459388db8ec524c32eca1e06fff2ea274b2ca92431f5a9a5b05b4b6e6"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb04b797b1cc0504d0c34625b1e939c236e001a73db0615825c0b1f9b926992"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e289c87ef4a738593bc0ce455c564e1c17d4acffab27d010751cd60b47c235cd"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:797200152716971b9d7f06bf1bcc3767b2998afa73bb1515c3bdc7828e70a711"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0b080a60c5019dcd11e4dd2870479a1876d09c1caa2f1d2cfb9645a10e7880b8"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_i686.whl", hash = "sha256:b870df6e1166b86522298d5a3d945ce65743be2d16586b9d2eabbb49ec7dbf2c"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:14d8395e06d0db4a54f99058bf00818420d4cfda8fbcc48b56e8ca73d5f2551a"}, - {file = "wassima-1.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5166da190adff31bdc432d4675e75945e88341a2334e5420a50f0f9758fb55b9"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dfe2cc6069aa074f2262ef128ad31d4212d850ea3c5c793525ed93a29b356658"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:829c11682c5ef7a90bc248a56ef50ef1290335b554626c1221f8a8c244d69cbb"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:e22c247574d35c2f450ede7662e630d9fd07e2230434aea142afd3cf819a3f3b"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e996e4772a41edffcce071360bcc2359deae3e6dd23ad1b101f4bb5168e7d0c6"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49cf2bc1f7e4cd622600bfbbb709320d5b60469dfaeef5493cc464771ffd027a"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8866a5f401b790f47fb084227e6c8576c25f48b1e50161a50705d603b67147cb"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ab2757fdbf1ea4c6623c575866b8c6c0ea1c8c99470e2e3c67ffae63f73f85"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a45b6986634634404da64c34386a71130666ac930e9a84be4050b8777ba8b9d"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890121a09213b98755eab091c72d9e2fdfe7f659d153051dc8a8adeef366e36f"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36194a67632b472f455a36b89efff7b9e1b521df4d24a3ba2789b2bb322ac81e"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:856ffd4b95ae31518e7c7109796feef27801da79c40ee1c75f166c9655934cd2"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:6b0142f9121095b5807159621d6c37cf1defb409a6cd245d0f496d40072f67c1"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_i686.whl", hash = "sha256:0612efb6f2e9339a3748fa8dc622f0be5f71b95b4471e1c1fbf5006a7e98a060"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:18cdae934705ec6a8df947f25c5230d6da812717ac23b5f51ad570c63bf7e3d7"}, - {file = "wassima-1.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:21e87777709d5c7d60503c589deef6a214ff8b94fa3e75ad5ff19518aeb0d4b1"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3565a0a206003acfa9eade388c7374add98800025bb04f568e57e11be27167ee"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:29b55e43836bf64c7f2a022e8dd358abdab05894cb444a4ad5d550ac0f8869d3"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:929216bccf041e21ad27e28aff46e9744f001792eb5ef2868a826a18fa2f58c7"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd201e7ba92dfb919b5200b844536133015e8f231954160e2d57ebf6e54d3cc2"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f6cb1b86329dd39d4e097d5d5abae9da47ce41485a4fd138c631b6e07389b8c"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:39511a31adbd988ec9706459f162b0258ef8be231837cf3e1adfce80858484ec"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd1f0d44b4a9274c89faa2cb2dc193d7e71e263cdd82184cb34bcabd6acfdbfd"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1736dd1a018dc4a9a71c51de7a3dd1bed76b31d35c7360de974c73542ea47e8"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36a5b0eb6713803ff0034ee5317950a36f2edcde73be8ecdcc959c2fb5e1fc39"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:83352eef88ce33fd7729215f4aea0bece6f05cb9226c710cf4fa4cd0a6a69e93"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:824af089a281d6cfb15d12708eed3cae211a15bbf3a0925d52fa242c10672091"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:5298e90f925205b037e44efcd843d2414aad720d6755fa8d604b1385272d0f9e"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_i686.whl", hash = "sha256:eafece652771c9e7d1922348e67c57481a1a488faa98235ff0f3bdd6732a7fbe"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b00e2db17d32c2b97ce1429fd25e868c2dc0380ecf55ffae9949ff866053364c"}, - {file = "wassima-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e8d67bb4ba6941fa16418e9af884aa2f3b002af1edb25c82dbdc0f96fc7ad594"}, - {file = "wassima-1.1.1-py3-none-any.whl", hash = "sha256:b5b67d9128d728d35a0dd5b0ed071a0feaf2c7e0a7416660864f180b752623df"}, - {file = "wassima-1.1.1.tar.gz", hash = "sha256:b673f31051fd1b9292bd6e05853016b401ac703c377a7d0657242eb41ce6121b"}, -] - -[[package]] -name = "watchdog" -version = "4.0.0" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.8" -files = [ - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, - {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, - {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, - {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"}, - {file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"}, - {file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"}, - {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, - {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, - {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, - {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, - {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, - {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, - {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, - {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "wrapt" -version = "1.16.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.6" -files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.10.0" -content-hash = "5e6069a97d8f774c1413f8e09c6a4c3d639296a5f3797c0416cb23b38d407a77" diff --git a/pyproject.toml b/pyproject.toml index c644d3a8..ea2e1462 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,34 +1,45 @@ -[tool.poetry] +[project] name = "seam" version = "2.2.0" description = "SDK for the Seam API written in Python." -authors = ["Seam Labs, Inc. "] +authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" +license-files = ["LICENSE.txt"] readme = "README.rst" -homepage = "https://github.com/seamapi/python" -repository = "https://github.com/seamapi/python" -exclude = ["**/*_test.py"] -include = ["seam/py.typed"] +requires-python = ">=3.10" +dependencies = [ + "dataclasses-json>=0.6.4,<0.7", + "niquests>=3.6.4,<4", + "svix>=1.24.0,<2", +] -[tool.poetry.dependencies] -python = "^3.10.0" -dataclasses-json = "^0.6.4" -niquests = "^3.6.4" -svix = "^1.24.0" +[project.urls] +Homepage = "https://github.com/seamapi/python" +Repository = "https://github.com/seamapi/python" -[tool.poetry.group.dev.dependencies] -black = "^24.3.0" -pylint = "^3.1.0" -pytest = "^8.1.1" -pytest-cov = "^5.0.0" -pytest-runner = "^6.0.0" -pytest-watch = "^4.2.0" -rstcheck = "^6.1.2" -mypy = "^1.17.0" +[dependency-groups] +dev = [ + "black>=24.3.0,<25", + "pylint>=3.1.0,<4", + "pytest>=8.1.1,<9", + "pytest-cov>=5.0.0,<6", + "pytest-runner>=6.0.0,<7", + "pytest-watch>=4.2.0,<5", + "rstcheck>=6.1.2,<7", + "mypy>=1.17.0,<2", +] [build-system] -requires = ["poetry>=1.8"] -build-backend = "poetry.masonry.api" +requires = ["uv_build>=0.8.17,<0.9.0"] +build-backend = "uv_build" + +[tool.uv] +required-version = ">=0.8.17,<0.9.0" + +[tool.uv.build-backend] +module-root = "" +source-exclude = ["**/*_test.py"] +wheel-exclude = ["**/*_test.py"] [tool.pytest.ini_options] norecursedirs = [ diff --git a/seam/client.py b/seam/client.py index 644b7ad5..9dc3ea44 100644 --- a/seam/client.py +++ b/seam/client.py @@ -52,7 +52,7 @@ def __init__( retries: Optional[Retry] = DEFAULT_RETRIES, timeout: Optional[float] = DEFAULT_TIMEOUT, niquests_options: Optional[Dict[str, Any]] = None, - **kwargs + **kwargs, ): # niquests.Session mounts its adapters while initializing, so retries # must be passed through here. Assigning self.retries afterwards leaves diff --git a/seam/modules/action_attempts.py b/seam/modules/action_attempts.py index 0f4da8b3..7d48243c 100644 --- a/seam/modules/action_attempts.py +++ b/seam/modules/action_attempts.py @@ -22,7 +22,7 @@ def poll_until_ready( *, action_attempt_id: str, timeout: Optional[float] = TIMEOUT, - polling_interval: Optional[float] = POLLING_INTERVAL + polling_interval: Optional[float] = POLLING_INTERVAL, ) -> ActionAttempt: time_waiting = 0.0 @@ -47,7 +47,7 @@ def resolve_action_attempt( client: SeamHttpClient, *, action_attempt: ActionAttempt, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]], ) -> ActionAttempt: if wait_for_action_attempt is True: return poll_until_ready( diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index a194118e..e6929152 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -37,7 +37,7 @@ def create( preferred_code_length: Optional[float] = None, starts_at: Optional[str] = None, use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None + use_offline_access_code: Optional[bool] = None, ) -> AccessCode: """Creates a new `access code `_. For granting access, we recommend `Access Grants `_ instead: they work across both standalone smart locks and access control systems and manage the underlying codes for you. Use this low-level endpoint only when you need direct control over a code on a single device, such as setting a custom PIN value. @@ -97,7 +97,7 @@ def create_multiple( prefer_native_scheduling: Optional[bool] = None, preferred_code_length: Optional[float] = None, starts_at: Optional[str] = None, - use_backup_access_code_pool: Optional[bool] = None + use_backup_access_code_pool: Optional[bool] = None, ) -> List[AccessCode]: """Creates new `access codes `_ that share a common code across multiple devices. @@ -169,7 +169,7 @@ def get( *, access_code_id: Optional[str] = None, code: Optional[str] = None, - device_id: Optional[str] = None + device_id: Optional[str] = None, ) -> AccessCode: """Returns a specified `access code `_. @@ -197,7 +197,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[AccessCode]: """Returns a list of all `access codes `_. @@ -250,7 +250,7 @@ def report_device_constraints( device_id: str, max_code_length: Optional[int] = None, min_code_length: Optional[int] = None, - supported_code_lengths: Optional[List[float]] = None + supported_code_lengths: Optional[List[float]] = None, ) -> None: """Enables you to report access code-related constraints for a device. Currently, supports reporting supported code length constraints for SmartThings devices. @@ -287,7 +287,7 @@ def update( starts_at: Optional[str] = None, type: Optional[str] = None, use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None + use_offline_access_code: Optional[bool] = None, ) -> None: """Updates a specified active or upcoming `access code `_. @@ -344,7 +344,7 @@ def update_multiple( common_code_key: str, ends_at: Optional[str] = None, name: Optional[str] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> None: """Updates `access codes `_ that share a common code across multiple devices. @@ -402,7 +402,7 @@ def create( preferred_code_length: Optional[float] = None, starts_at: Optional[str] = None, use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None + use_offline_access_code: Optional[bool] = None, ) -> AccessCode: """Creates a new `access code `_. For granting access, we recommend `Access Grants `_ instead: they work across both standalone smart locks and access control systems and manage the underlying codes for you. Use this low-level endpoint only when you need direct control over a code on a single device, such as setting a custom PIN value. @@ -500,7 +500,7 @@ def create_multiple( prefer_native_scheduling: Optional[bool] = None, preferred_code_length: Optional[float] = None, starts_at: Optional[str] = None, - use_backup_access_code_pool: Optional[bool] = None + use_backup_access_code_pool: Optional[bool] = None, ) -> List[AccessCode]: """Creates new `access codes `_ that share a common code across multiple devices. @@ -618,7 +618,7 @@ def get( *, access_code_id: Optional[str] = None, code: Optional[str] = None, - device_id: Optional[str] = None + device_id: Optional[str] = None, ) -> AccessCode: """Returns a specified `access code `_. @@ -656,7 +656,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[AccessCode]: """Returns a list of all `access codes `_. @@ -741,7 +741,7 @@ def report_device_constraints( device_id: str, max_code_length: Optional[int] = None, min_code_length: Optional[int] = None, - supported_code_lengths: Optional[List[float]] = None + supported_code_lengths: Optional[List[float]] = None, ) -> None: """Enables you to report access code-related constraints for a device. Currently, supports reporting supported code length constraints for SmartThings devices. @@ -790,7 +790,7 @@ def update( starts_at: Optional[str] = None, type: Optional[str] = None, use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None + use_offline_access_code: Optional[bool] = None, ) -> None: """Updates a specified active or upcoming `access code `_. @@ -889,7 +889,7 @@ def update_multiple( common_code_key: str, ends_at: Optional[str] = None, name: Optional[str] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> None: """Updates `access codes `_ that share a common code across multiple devices. diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index ac90e4ee..da1b17f1 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -13,7 +13,7 @@ def convert_to_managed( access_code_id: str, allow_external_modification: Optional[bool] = None, force: Optional[bool] = None, - is_external_modification_allowed: Optional[bool] = None + is_external_modification_allowed: Optional[bool] = None, ) -> None: """Converts an `unmanaged access code `_ to an `access code managed through Seam `_. @@ -45,7 +45,7 @@ def get( *, access_code_id: Optional[str] = None, code: Optional[str] = None, - device_id: Optional[str] = None + device_id: Optional[str] = None, ) -> UnmanagedAccessCode: """Returns a specified `unmanaged access code `_. @@ -68,7 +68,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[UnmanagedAccessCode]: """Returns a list of all `unmanaged access codes `_. @@ -93,7 +93,7 @@ def update( is_managed: bool, allow_external_modification: Optional[bool] = None, force: Optional[bool] = None, - is_external_modification_allowed: Optional[bool] = None + is_external_modification_allowed: Optional[bool] = None, ) -> None: """Updates a specified `unmanaged access code `_. @@ -121,7 +121,7 @@ def convert_to_managed( access_code_id: str, allow_external_modification: Optional[bool] = None, force: Optional[bool] = None, - is_external_modification_allowed: Optional[bool] = None + is_external_modification_allowed: Optional[bool] = None, ) -> None: """Converts an `unmanaged access code `_ to an `access code managed through Seam `_. @@ -175,7 +175,7 @@ def get( *, access_code_id: Optional[str] = None, code: Optional[str] = None, - device_id: Optional[str] = None + device_id: Optional[str] = None, ) -> UnmanagedAccessCode: """Returns a specified `unmanaged access code `_. @@ -208,7 +208,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[UnmanagedAccessCode]: """Returns a list of all `unmanaged access codes `_. @@ -247,7 +247,7 @@ def update( is_managed: bool, allow_external_modification: Optional[bool] = None, force: Optional[bool] = None, - is_external_modification_allowed: Optional[bool] = None + is_external_modification_allowed: Optional[bool] = None, ) -> None: """Updates a specified `unmanaged access code `_. diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index f6835ebd..e9550c33 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -33,7 +33,7 @@ def create( reservation_key: Optional[str] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> AccessGrant: """Creates a new `Access Grant `_. Access Grants are the default and recommended way to grant a user access to any physical space, irrespective of the locking hardware. They work with both standalone smart locks (using ``device_ids``) and access control systems (using ``acs_entrance_ids`` or ``space_ids``), and can issue PIN codes, key cards, and mobile keys through a single request. @@ -82,7 +82,7 @@ def get( self, *, access_grant_id: Optional[str] = None, - access_grant_key: Optional[str] = None + access_grant_key: Optional[str] = None, ) -> AccessGrant: """Get an Access Grant. @@ -100,7 +100,7 @@ def get_related( access_grant_ids: Optional[List[str]] = None, access_grant_keys: Optional[List[str]] = None, exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None + include: Optional[List[str]] = None, ) -> Batch: """Gets all related resources for one or more Access Grants. @@ -131,7 +131,7 @@ def list( page_cursor: Optional[str] = None, reservation_key: Optional[str] = None, space_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[AccessGrant]: """Gets an Access Grant. @@ -185,7 +185,7 @@ def update( access_grant_key: Optional[str] = None, ends_at: Optional[str] = None, name: Optional[str] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> None: """Updates an existing Access Grant's time window. @@ -229,7 +229,7 @@ def create( reservation_key: Optional[str] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> AccessGrant: """Creates a new `Access Grant `_. Access Grants are the default and recommended way to grant a user access to any physical space, irrespective of the locking hardware. They work with both standalone smart locks (using ``device_ids``) and access control systems (using ``acs_entrance_ids`` or ``space_ids``), and can issue PIN codes, key cards, and mobile keys through a single request. @@ -318,7 +318,7 @@ def get( self, *, access_grant_id: Optional[str] = None, - access_grant_key: Optional[str] = None + access_grant_key: Optional[str] = None, ) -> AccessGrant: """Get an Access Grant. @@ -344,7 +344,7 @@ def get_related( access_grant_ids: Optional[List[str]] = None, access_grant_keys: Optional[List[str]] = None, exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None + include: Optional[List[str]] = None, ) -> Batch: """Gets all related resources for one or more Access Grants. @@ -387,7 +387,7 @@ def list( page_cursor: Optional[str] = None, reservation_key: Optional[str] = None, space_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[AccessGrant]: """Gets an Access Grant. @@ -481,7 +481,7 @@ def update( access_grant_key: Optional[str] = None, ends_at: Optional[str] = None, name: Optional[str] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> None: """Updates an existing Access Grant's time window. diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index 43786955..2dd8b80d 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -24,7 +24,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, reservation_key: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[UnmanagedAccessGrant]: """Gets unmanaged Access Grants (where is_managed = false). @@ -49,7 +49,7 @@ def update( *, access_grant_id: str, is_managed: bool, - access_grant_key: Optional[str] = None + access_grant_key: Optional[str] = None, ) -> None: """Updates an unmanaged Access Grant to make it managed. @@ -94,7 +94,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, reservation_key: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[UnmanagedAccessGrant]: """Gets unmanaged Access Grants (where is_managed = false). @@ -135,7 +135,7 @@ def update( *, access_grant_id: str, is_managed: bool, - access_grant_key: Optional[str] = None + access_grant_key: Optional[str] = None, ) -> None: """Updates an unmanaged Access Grant to make it managed. diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index ffafd2d5..c92c2d10 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -22,7 +22,7 @@ def assign_card( *, access_method_id: str, card_number: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Assigns a pre-registered card credential, identified by ``card_number``, to a card-mode access method. Use this endpoint for access systems that use pre-registered cards, where a physical card must be associated with an access method before it can be used for access. Assigning a card credential also triggers issuance of the access method. @@ -41,7 +41,7 @@ def delete( *, access_method_id: Optional[str] = None, access_grant_id: Optional[str] = None, - reservation_key: Optional[str] = None + reservation_key: Optional[str] = None, ) -> None: """Deletes an access method. @@ -59,7 +59,7 @@ def encode( *, access_method_id: str, acs_encoder_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Encodes an existing access method onto a plastic card placed on the specified `encoder `_. @@ -87,7 +87,7 @@ def get_related( *, access_method_ids: List[str], exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None + include: Optional[List[str]] = None, ) -> Batch: """Gets all related resources for one or more Access Methods. @@ -111,7 +111,7 @@ def list( device_id: Optional[str] = None, limit: Optional[int] = None, page_cursor: Optional[str] = None, - space_id: Optional[str] = None + space_id: Optional[str] = None, ) -> List[AccessMethod]: """Lists all access methods, usually filtered by Access Grant. @@ -140,7 +140,7 @@ def unlock_door( *, access_method_id: str, acs_entrance_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Remotely unlocks a specified `entrance `_ using the cloud key credential associated with an access method. Returns an action attempt that tracks the progress of the unlock operation. @@ -169,7 +169,7 @@ def assign_card( *, access_method_id: str, card_number: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Assigns a pre-registered card credential, identified by ``card_number``, to a card-mode access method. Use this endpoint for access systems that use pre-registered cards, where a physical card must be associated with an access method before it can be used for access. Assigning a card credential also triggers issuance of the access method. @@ -206,7 +206,7 @@ def delete( *, access_method_id: Optional[str] = None, access_grant_id: Optional[str] = None, - reservation_key: Optional[str] = None + reservation_key: Optional[str] = None, ) -> None: """Deletes an access method. @@ -234,7 +234,7 @@ def encode( *, access_method_id: str, acs_encoder_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Encodes an existing access method onto a plastic card placed on the specified `encoder `_. @@ -286,7 +286,7 @@ def get_related( *, access_method_ids: List[str], exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None + include: Optional[List[str]] = None, ) -> Batch: """Gets all related resources for one or more Access Methods. @@ -320,7 +320,7 @@ def list( device_id: Optional[str] = None, limit: Optional[int] = None, page_cursor: Optional[str] = None, - space_id: Optional[str] = None + space_id: Optional[str] = None, ) -> List[AccessMethod]: """Lists all access methods, usually filtered by Access Grant. @@ -369,7 +369,7 @@ def unlock_door( *, access_method_id: str, acs_entrance_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Remotely unlocks a specified `entrance `_ using the cloud key credential associated with an access method. Returns an action attempt that tracks the progress of the unlock operation. diff --git a/seam/routes/access_methods_unmanaged.py b/seam/routes/access_methods_unmanaged.py index 44ad9c5f..a2783641 100644 --- a/seam/routes/access_methods_unmanaged.py +++ b/seam/routes/access_methods_unmanaged.py @@ -22,7 +22,7 @@ def list( access_grant_id: str, acs_entrance_id: Optional[str] = None, device_id: Optional[str] = None, - space_id: Optional[str] = None + space_id: Optional[str] = None, ) -> List[UnmanagedAccessMethod]: """Lists all unmanaged access methods (where is_managed = false), usually filtered by Access Grant. @@ -64,7 +64,7 @@ def list( access_grant_id: str, acs_entrance_id: Optional[str] = None, device_id: Optional[str] = None, - space_id: Optional[str] = None + space_id: Optional[str] = None, ) -> List[UnmanagedAccessMethod]: """Lists all unmanaged access methods (where is_managed = false), usually filtered by Access Grant. diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index 11ce5959..606ffa8d 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -12,7 +12,7 @@ def add_user( *, acs_access_group_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Adds a specified `access system user `_ to a specified `access group `_. @@ -47,7 +47,7 @@ def list( acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, search: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[AcsAccessGroup]: """Returns a list of all `access groups `_. @@ -88,7 +88,7 @@ def remove_user( *, acs_access_group_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Removes a specified `access system user `_ from a specified `access group `_. @@ -111,7 +111,7 @@ def add_user( *, acs_access_group_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Adds a specified `access system user `_ to a specified `access group `_. @@ -168,7 +168,7 @@ def list( acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, search: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[AcsAccessGroup]: """Returns a list of all `access groups `_. @@ -235,7 +235,7 @@ def remove_user( *, acs_access_group_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Removes a specified `access system user `_ from a specified `access group `_. diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index c655973e..cc2910bf 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -12,7 +12,7 @@ def assign( *, acs_credential_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Assigns a specified `credential `_ to a specified `access system user `_. @@ -40,7 +40,7 @@ def create( salto_space_metadata: Optional[Dict[str, Any]] = None, starts_at: Optional[str] = None, user_identity_id: Optional[str] = None, - visionline_metadata: Optional[Dict[str, Any]] = None + visionline_metadata: Optional[Dict[str, Any]] = None, ) -> AcsCredential: """Creates a new `credential `_ for a specified `ACS user `_. For granting access, we recommend `Access Grants `_ instead: they create and manage the underlying credentials for you, across access systems and standalone smart locks alike. Use this low-level endpoint only when you need direct control over an individual ACS credential. @@ -100,7 +100,7 @@ def list( is_multi_phone_sync_credential: Optional[bool] = None, limit: Optional[float] = None, page_cursor: Optional[str] = None, - search: Optional[str] = None + search: Optional[str] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_. @@ -138,7 +138,7 @@ def unassign( *, acs_credential_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Unassigns a specified `credential `_ from a specified `access system user `_. @@ -156,7 +156,7 @@ def update( *, acs_credential_id: str, code: Optional[str] = None, - ends_at: Optional[str] = None + ends_at: Optional[str] = None, ) -> None: """Updates the code and ends at date and time for a specified `credential `_. @@ -179,7 +179,7 @@ def assign( *, acs_credential_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Assigns a specified `credential `_ to a specified `access system user `_. @@ -217,7 +217,7 @@ def create( salto_space_metadata: Optional[Dict[str, Any]] = None, starts_at: Optional[str] = None, user_identity_id: Optional[str] = None, - visionline_metadata: Optional[Dict[str, Any]] = None + visionline_metadata: Optional[Dict[str, Any]] = None, ) -> AcsCredential: """Creates a new `credential `_ for a specified `ACS user `_. For granting access, we recommend `Access Grants `_ instead: they create and manage the underlying credentials for you, across access systems and standalone smart locks alike. Use this low-level endpoint only when you need direct control over an individual ACS credential. @@ -323,7 +323,7 @@ def list( is_multi_phone_sync_credential: Optional[bool] = None, limit: Optional[float] = None, page_cursor: Optional[str] = None, - search: Optional[str] = None + search: Optional[str] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_. @@ -391,7 +391,7 @@ def unassign( *, acs_credential_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Unassigns a specified `credential `_ from a specified `access system user `_. @@ -419,7 +419,7 @@ def update( *, acs_credential_id: str, code: Optional[str] = None, - ends_at: Optional[str] = None + ends_at: Optional[str] = None, ) -> None: """Updates the code and ends at date and time for a specified `credential `_. diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index e537a0f3..3bd18abb 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -20,7 +20,7 @@ def encode_credential( acs_encoder_id: str, access_method_id: Optional[str] = None, acs_credential_id: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Encodes an existing `credential `_ onto a plastic card placed on the specified `encoder `_. Either provide an ``acs_credential_id`` or an ``access_method_id`` @@ -52,7 +52,7 @@ def list( acs_system_ids: Optional[List[str]] = None, acs_encoder_ids: Optional[List[str]] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None + page_cursor: Optional[str] = None, ) -> List[AcsEncoder]: """Returns a list of all `encoders `_. @@ -75,7 +75,7 @@ def scan_credential( *, acs_encoder_id: str, salto_ks_metadata: Optional[Dict[str, Any]] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Scans an encoded `acs_credential `_ from a plastic card placed on the specified `encoder `_. @@ -96,7 +96,7 @@ def scan_to_assign_credential( acs_user_id: Optional[str] = None, salto_ks_metadata: Optional[Dict[str, Any]] = None, user_identity_id: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Scans a physical card placed on the specified `encoder `_ and assigns the scanned credential to an ACS user. Provide either an ``acs_user_id`` or a ``user_identity_id``. @@ -130,7 +130,7 @@ def encode_credential( acs_encoder_id: str, access_method_id: Optional[str] = None, acs_credential_id: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Encodes an existing `credential `_ onto a plastic card placed on the specified `encoder `_. Either provide an ``acs_credential_id`` or an ``access_method_id`` @@ -188,7 +188,7 @@ def list( acs_system_ids: Optional[List[str]] = None, acs_encoder_ids: Optional[List[str]] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None + page_cursor: Optional[str] = None, ) -> List[AcsEncoder]: """Returns a list of all `encoders `_. @@ -225,7 +225,7 @@ def scan_credential( *, acs_encoder_id: str, salto_ks_metadata: Optional[Dict[str, Any]] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Scans an encoded `acs_credential `_ from a plastic card placed on the specified `encoder `_. @@ -264,7 +264,7 @@ def scan_to_assign_credential( acs_user_id: Optional[str] = None, salto_ks_metadata: Optional[Dict[str, Any]] = None, user_identity_id: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Scans a physical card placed on the specified `encoder `_ and assigns the scanned credential to an ACS user. Provide either an ``acs_user_id`` or a ``user_identity_id``. diff --git a/seam/routes/acs_encoders_simulate.py b/seam/routes/acs_encoders_simulate.py index e08aaf10..e9c5653a 100644 --- a/seam/routes/acs_encoders_simulate.py +++ b/seam/routes/acs_encoders_simulate.py @@ -11,7 +11,7 @@ def next_credential_encode_will_fail( *, acs_encoder_id: str, error_code: Optional[str] = None, - acs_credential_id: Optional[str] = None + acs_credential_id: Optional[str] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -40,7 +40,7 @@ def next_credential_scan_will_fail( *, acs_encoder_id: str, error_code: Optional[str] = None, - acs_credential_id_on_seam: Optional[str] = None + acs_credential_id_on_seam: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -57,7 +57,7 @@ def next_credential_scan_will_succeed( *, acs_encoder_id: str, acs_credential_id_on_seam: Optional[str] = None, - scenario: Optional[str] = None + scenario: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -79,7 +79,7 @@ def next_credential_encode_will_fail( *, acs_encoder_id: str, error_code: Optional[str] = None, - acs_credential_id: Optional[str] = None + acs_credential_id: Optional[str] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -131,7 +131,7 @@ def next_credential_scan_will_fail( *, acs_encoder_id: str, error_code: Optional[str] = None, - acs_credential_id_on_seam: Optional[str] = None + acs_credential_id_on_seam: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -160,7 +160,7 @@ def next_credential_scan_will_succeed( *, acs_encoder_id: str, acs_credential_id_on_seam: Optional[str] = None, - scenario: Optional[str] = None + scenario: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 104779c0..30d38cf2 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -22,7 +22,7 @@ def grant_access( *, acs_entrance_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Grants a specified `access system user `_ access to a specified `access system entrance `_. @@ -48,7 +48,7 @@ def list( location_id: Optional[str] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - space_id: Optional[str] = None + space_id: Optional[str] = None, ) -> List[AcsEntrance]: """Returns a list of all `access system entrances `_. @@ -96,7 +96,7 @@ def unlock( *, acs_credential_id: str, acs_entrance_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Remotely unlocks a specified `entrance `_ using a cloud_key credential. Returns an action attempt that tracks the progress of the unlock operation. @@ -135,7 +135,7 @@ def grant_access( *, acs_entrance_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Grants a specified `access system user `_ access to a specified `access system entrance `_. @@ -171,7 +171,7 @@ def list( location_id: Optional[str] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - space_id: Optional[str] = None + space_id: Optional[str] = None, ) -> List[AcsEntrance]: """Returns a list of all `access system entrances `_. @@ -255,7 +255,7 @@ def unlock( *, acs_credential_id: str, acs_entrance_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Remotely unlocks a specified `entrance `_ using a cloud_key credential. Returns an action attempt that tracks the progress of the unlock operation. diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index b4ca7d3d..4e582c1d 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -21,7 +21,7 @@ def list( *, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - search: Optional[str] = None + search: Optional[str] = None, ) -> List[AcsSystem]: """Returns a list of all `access systems `_. @@ -55,7 +55,7 @@ def report_devices( *, acs_system_id: str, acs_encoders: Optional[List[Dict[str, Any]]] = None, - acs_entrances: Optional[List[Dict[str, Any]]] = None + acs_entrances: Optional[List[Dict[str, Any]]] = None, ) -> None: """Reports ACS system device status including encoders and entrances. @@ -92,7 +92,7 @@ def list( *, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - search: Optional[str] = None + search: Optional[str] = None, ) -> List[AcsSystem]: """Returns a list of all `access systems `_. @@ -145,7 +145,7 @@ def report_devices( *, acs_system_id: str, acs_encoders: Optional[List[Dict[str, Any]]] = None, - acs_entrances: Optional[List[Dict[str, Any]]] = None + acs_entrances: Optional[List[Dict[str, Any]]] = None, ) -> None: """Reports ACS system device status including encoders and entrances. diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 7f59c38e..e5567333 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -29,7 +29,7 @@ def create( email: Optional[str] = None, email_address: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> AcsUser: """Creates a new `access system user `_. @@ -58,7 +58,7 @@ def delete( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Deletes a specified `access system user `_ and invalidates the access system user's `credentials `_. @@ -76,7 +76,7 @@ def get( *, acs_user_id: Optional[str] = None, acs_system_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> AcsUser: """Returns a specified `access system user `_. @@ -100,7 +100,7 @@ def list( search: Optional[str] = None, user_identity_email_address: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_phone_number: Optional[str] = None + user_identity_phone_number: Optional[str] = None, ) -> List[AcsUser]: """Returns a list of all `access system users `_. @@ -129,7 +129,7 @@ def list_accessible_entrances( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[AcsEntrance]: """Lists the `entrances `_ to which a specified `access system user `_ has access. @@ -148,7 +148,7 @@ def remove_from_access_group( *, acs_access_group_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Removes a specified `access system user `_ from a specified `access group `_. @@ -166,7 +166,7 @@ def revoke_access_to_all_entrances( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Revokes access to all `entrances `_ for a specified `access system user `_. @@ -184,7 +184,7 @@ def suspend( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """`Suspends `_ a specified `access system user `_. Suspending an access system user revokes their access temporarily. To restore an access system user's access, you can `unsuspend `_ them. @@ -202,7 +202,7 @@ def unsuspend( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """`Unsuspends `_ a specified suspended `access system user `_. While `suspending an access system user `_ revokes their access temporarily, unsuspending the access system user restores their access. @@ -226,7 +226,7 @@ def update( full_name: Optional[str] = None, hid_acs_system_id: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Updates the properties of a specified `access system user `_. @@ -286,7 +286,7 @@ def create( email: Optional[str] = None, email_address: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> AcsUser: """Creates a new `access system user `_. @@ -335,7 +335,7 @@ def delete( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Deletes a specified `access system user `_ and invalidates the access system user's `credentials `_. @@ -363,7 +363,7 @@ def get( *, acs_user_id: Optional[str] = None, acs_system_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> AcsUser: """Returns a specified `access system user `_. @@ -397,7 +397,7 @@ def list( search: Optional[str] = None, user_identity_email_address: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_phone_number: Optional[str] = None + user_identity_phone_number: Optional[str] = None, ) -> List[AcsUser]: """Returns a list of all `access system users `_. @@ -446,7 +446,7 @@ def list_accessible_entrances( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[AcsEntrance]: """Lists the `entrances `_ to which a specified `access system user `_ has access. @@ -477,7 +477,7 @@ def remove_from_access_group( *, acs_access_group_id: str, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Removes a specified `access system user `_ from a specified `access group `_. @@ -505,7 +505,7 @@ def revoke_access_to_all_entrances( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Revokes access to all `entrances `_ for a specified `access system user `_. @@ -533,7 +533,7 @@ def suspend( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """`Suspends `_ a specified `access system user `_. Suspending an access system user revokes their access temporarily. To restore an access system user's access, you can `unsuspend `_ them. @@ -561,7 +561,7 @@ def unsuspend( *, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """`Unsuspends `_ a specified suspended `access system user `_. While `suspending an access system user `_ revokes their access temporarily, unsuspending the access system user restores their access. @@ -595,7 +595,7 @@ def update( full_name: Optional[str] = None, hid_acs_system_id: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> None: """Updates the properties of a specified `access system user `_. diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index e14dfe8e..f1291ddf 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -12,7 +12,7 @@ def get( self, *, action_attempt_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Returns a specified `action attempt `_. @@ -30,7 +30,7 @@ def list( action_attempt_ids: Optional[List[str]] = None, device_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None + page_cursor: Optional[str] = None, ) -> List[ActionAttempt]: """Returns a list of the `action attempts `_ that you specify as an array of ``action_attempt_id``s. @@ -55,7 +55,7 @@ def get( self, *, action_attempt_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Returns a specified `action attempt `_. @@ -89,7 +89,7 @@ def list( action_attempt_ids: Optional[List[str]] = None, device_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None + page_cursor: Optional[str] = None, ) -> List[ActionAttempt]: """Returns a list of the `action attempts `_ that you specify as an array of ``action_attempt_id``s. diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index 29f996af..5e318cc1 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -17,7 +17,7 @@ def create( expires_at: Optional[str] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> ClientSession: """Creates a new `client session `_. @@ -52,7 +52,7 @@ def get( self, *, client_session_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> ClientSession: """Returns a specified `client session `_. @@ -72,7 +72,7 @@ def get_or_create( expires_at: Optional[str] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> ClientSession: """Returns a `client session `_ with specific characteristics or creates a new client session with these characteristics if it does not yet exist. @@ -100,7 +100,7 @@ def grant_access( connected_account_ids: Optional[List[str]] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> None: """Grants a `client session `_ access to one or more resources, such as `Connect Webviews `_, `user identities `_, and so on. @@ -126,7 +126,7 @@ def list( connect_webview_id: Optional[str] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - without_user_identifier_key: Optional[bool] = None + without_user_identifier_key: Optional[bool] = None, ) -> List[ClientSession]: """Returns a list of all `client sessions `_. @@ -168,7 +168,7 @@ def create( expires_at: Optional[str] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> ClientSession: """Creates a new `client session `_. @@ -229,7 +229,7 @@ def get( self, *, client_session_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> ClientSession: """Returns a specified `client session `_. @@ -257,7 +257,7 @@ def get_or_create( expires_at: Optional[str] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> ClientSession: """Returns a `client session `_ with specific characteristics or creates a new client session with these characteristics if it does not yet exist. @@ -301,7 +301,7 @@ def grant_access( connected_account_ids: Optional[List[str]] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> None: """Grants a `client session `_ access to one or more resources, such as `Connect Webviews `_, `user identities `_, and so on. @@ -343,7 +343,7 @@ def list( connect_webview_id: Optional[str] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - without_user_identifier_key: Optional[bool] = None + without_user_identifier_key: Optional[bool] = None, ) -> List[ClientSession]: """Returns a list of all `client sessions `_. diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index d6b32d6e..44cd32ad 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -19,7 +19,7 @@ def create( customer_key: Optional[str] = None, excluded_providers: Optional[List[str]] = None, provider_category: Optional[str] = None, - wait_for_device_creation: Optional[bool] = None + wait_for_device_creation: Optional[bool] = None, ) -> ConnectWebview: """Creates a new `Connect Webview `_. @@ -81,7 +81,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[ConnectWebview]: """Returns a list of all `Connect Webviews `_. @@ -118,7 +118,7 @@ def create( customer_key: Optional[str] = None, excluded_providers: Optional[List[str]] = None, provider_category: Optional[str] = None, - wait_for_device_creation: Optional[bool] = None + wait_for_device_creation: Optional[bool] = None, ) -> ConnectWebview: """Creates a new `Connect Webview `_. @@ -218,7 +218,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[ConnectWebview]: """Returns a list of all `Connect Webviews `_. diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index bcc979fb..fbd82829 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -50,7 +50,7 @@ def list( page_cursor: Optional[str] = None, search: Optional[str] = None, space_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[ConnectedAccount]: """Returns a list of all `connected accounts `_. @@ -88,7 +88,7 @@ def update( automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - display_name: Optional[str] = None + display_name: Optional[str] = None, ) -> None: """Updates a `connected account `_. @@ -165,7 +165,7 @@ def list( page_cursor: Optional[str] = None, search: Optional[str] = None, space_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[ConnectedAccount]: """Returns a list of all `connected accounts `_. @@ -227,7 +227,7 @@ def update( automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - display_name: Optional[str] = None + display_name: Optional[str] = None, ) -> None: """Updates a `connected account `_. diff --git a/seam/routes/customers.py b/seam/routes/customers.py index bdbf4f54..26ddfa92 100644 --- a/seam/routes/customers.py +++ b/seam/routes/customers.py @@ -20,7 +20,7 @@ def create_portal( locale: Optional[str] = None, navigation_mode: Optional[str] = None, read_only: Optional[bool] = None, - customer_data: Optional[Dict[str, Any]] = None + customer_data: Optional[Dict[str, Any]] = None, ) -> CustomerPortal: """Creates a new customer portal magic link with configurable features. @@ -71,7 +71,7 @@ def delete_data( tenant_keys: Optional[List[str]] = None, unit_keys: Optional[List[str]] = None, user_identity_keys: Optional[List[str]] = None, - user_keys: Optional[List[str]] = None + user_keys: Optional[List[str]] = None, ) -> None: """Deletes customer data including resources like spaces, properties, rooms, users, etc. This will delete the partner resources and any related Seam resources (user identities, access grants, spaces). @@ -138,7 +138,7 @@ def push_data( tenants: Optional[List[Dict[str, Any]]] = None, units: Optional[List[Dict[str, Any]]] = None, user_identities: Optional[List[Dict[str, Any]]] = None, - users: Optional[List[Dict[str, Any]]] = None + users: Optional[List[Dict[str, Any]]] = None, ) -> None: """Pushes customer data including resources like spaces, properties, rooms, users, etc. @@ -202,7 +202,7 @@ def create_portal( locale: Optional[str] = None, navigation_mode: Optional[str] = None, read_only: Optional[bool] = None, - customer_data: Optional[Dict[str, Any]] = None + customer_data: Optional[Dict[str, Any]] = None, ) -> CustomerPortal: """Creates a new customer portal magic link with configurable features. @@ -279,7 +279,7 @@ def delete_data( tenant_keys: Optional[List[str]] = None, unit_keys: Optional[List[str]] = None, user_identity_keys: Optional[List[str]] = None, - user_keys: Optional[List[str]] = None + user_keys: Optional[List[str]] = None, ) -> None: """Deletes customer data including resources like spaces, properties, rooms, users, etc. This will delete the partner resources and any related Seam resources (user identities, access grants, spaces). @@ -388,7 +388,7 @@ def push_data( tenants: Optional[List[Dict[str, Any]]] = None, units: Optional[List[Dict[str, Any]]] = None, user_identities: Optional[List[Dict[str, Any]]] = None, - users: Optional[List[Dict[str, Any]]] = None + users: Optional[List[Dict[str, Any]]] = None, ) -> None: """Pushes customer data including resources like spaces, properties, rooms, users, etc. diff --git a/seam/routes/devices.py b/seam/routes/devices.py index 433a8d40..fcec08c9 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -52,7 +52,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `devices `_. @@ -122,7 +122,7 @@ def update( custom_metadata: Optional[Dict[str, Any]] = None, is_managed: Optional[bool] = None, name: Optional[str] = None, - properties: Optional[Dict[str, Any]] = None + properties: Optional[Dict[str, Any]] = None, ) -> None: """Updates a specified `device `_. @@ -198,7 +198,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `devices `_. @@ -316,7 +316,7 @@ def update( custom_metadata: Optional[Dict[str, Any]] = None, is_managed: Optional[bool] = None, name: Optional[str] = None, - properties: Optional[Dict[str, Any]] = None + properties: Optional[Dict[str, Any]] = None, ) -> None: """Updates a specified `device `_. diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 9c0e865f..8da04525 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -42,7 +42,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[UnmanagedDevice]: """Returns a list of all `unmanaged devices `_. @@ -89,7 +89,7 @@ def update( *, device_id: str, custom_metadata: Optional[Dict[str, Any]] = None, - is_managed: Optional[bool] = None + is_managed: Optional[bool] = None, ) -> None: """Updates a specified `unmanaged device `_. To convert an unmanaged device to managed, set ``is_managed`` to ``true``. @@ -152,7 +152,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[UnmanagedDevice]: """Returns a list of all `unmanaged devices `_. @@ -235,7 +235,7 @@ def update( *, device_id: str, custom_metadata: Optional[Dict[str, Any]] = None, - is_managed: Optional[bool] = None + is_managed: Optional[bool] = None, ) -> None: """Updates a specified `unmanaged device `_. To convert an unmanaged device to managed, set ``is_managed`` to ``true``. diff --git a/seam/routes/events.py b/seam/routes/events.py index 30ce08e9..eb624c73 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -12,7 +12,7 @@ def get( *, event_id: Optional[str] = None, device_id: Optional[str] = None, - event_type: Optional[str] = None + event_type: Optional[str] = None, ) -> SeamEvent: """Returns a specified event. This endpoint returns the same event that would be sent to a `webhook `_, but it enables you to retrieve an event that already took place. @@ -56,7 +56,7 @@ def list( space_id: Optional[str] = None, space_ids: Optional[List[str]] = None, unstable_offset: Optional[float] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[SeamEvent]: """Returns a list of all events. This endpoint returns the same events that would be sent to a `webhook `_, but it enables you to filter or see events that already took place. @@ -130,7 +130,7 @@ def get( *, event_id: Optional[str] = None, device_id: Optional[str] = None, - event_type: Optional[str] = None + event_type: Optional[str] = None, ) -> SeamEvent: """Returns a specified event. This endpoint returns the same event that would be sent to a `webhook `_, but it enables you to retrieve an event that already took place. @@ -184,7 +184,7 @@ def list( space_id: Optional[str] = None, space_ids: Optional[List[str]] = None, unstable_offset: Optional[float] = None, - user_identity_id: Optional[str] = None + user_identity_id: Optional[str] = None, ) -> List[SeamEvent]: """Returns a list of all events. This endpoint returns the same events that would be sent to a `webhook `_, but it enables you to filter or see events that already took place. diff --git a/seam/routes/instant_keys.py b/seam/routes/instant_keys.py index b1c3bf23..d9af3a77 100644 --- a/seam/routes/instant_keys.py +++ b/seam/routes/instant_keys.py @@ -18,7 +18,7 @@ def get( self, *, instant_key_id: Optional[str] = None, - instant_key_url: Optional[str] = None + instant_key_url: Optional[str] = None, ) -> InstantKey: """Gets an `instant key `_. @@ -61,7 +61,7 @@ def get( self, *, instant_key_id: Optional[str] = None, - instant_key_url: Optional[str] = None + instant_key_url: Optional[str] = None, ) -> InstantKey: """Gets an `instant key `_. diff --git a/seam/routes/locks.py b/seam/routes/locks.py index 23a41b0d..e1f168a5 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -20,7 +20,7 @@ def configure_auto_lock( auto_lock_enabled: bool, device_id: str, auto_lock_delay_seconds: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Configures the auto-lock setting for a specified `lock `_. @@ -70,7 +70,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -114,7 +114,7 @@ def lock_door( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Locks a `lock `_. See also `Locking and Unlocking Smart Locks `_. @@ -130,7 +130,7 @@ def unlock_door( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Unlocks a `lock `_. See also `Locking and Unlocking Smart Locks `_. @@ -158,7 +158,7 @@ def configure_auto_lock( auto_lock_enabled: bool, device_id: str, auto_lock_delay_seconds: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Configures the auto-lock setting for a specified `lock `_. @@ -236,7 +236,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -316,7 +316,7 @@ def lock_door( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Locks a `lock `_. See also `Locking and Unlocking Smart Locks `_. @@ -348,7 +348,7 @@ def unlock_door( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Unlocks a `lock `_. See also `Locking and Unlocking Smart Locks `_. diff --git a/seam/routes/locks_simulate.py b/seam/routes/locks_simulate.py index 937cb37d..66fde77e 100644 --- a/seam/routes/locks_simulate.py +++ b/seam/routes/locks_simulate.py @@ -13,7 +13,7 @@ def keypad_code_entry( *, code: str, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Simulates the entry of a code on a keypad. You can only perform this action for `August `_ devices within `sandbox workspaces `_. @@ -31,7 +31,7 @@ def manual_lock_via_keypad( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Simulates a manual lock action using a keypad. You can only perform this action for `August `_ devices within `sandbox workspaces `_. @@ -53,7 +53,7 @@ def keypad_code_entry( *, code: str, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Simulates the entry of a code on a keypad. You can only perform this action for `August `_ devices within `sandbox workspaces `_. @@ -89,7 +89,7 @@ def manual_lock_via_keypad( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Simulates a manual lock action using a keypad. You can only perform this action for `August `_ devices within `sandbox workspaces `_. diff --git a/seam/routes/noise_sensors.py b/seam/routes/noise_sensors.py index efd90f41..7c2b517b 100644 --- a/seam/routes/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -40,7 +40,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. @@ -115,7 +115,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index 8c5c9f02..5b62e1ce 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -15,7 +15,7 @@ def create( starts_daily_at: str, name: Optional[str] = None, noise_threshold_decibels: Optional[float] = None, - noise_threshold_nrs: Optional[float] = None + noise_threshold_nrs: Optional[float] = None, ) -> NoiseThreshold: """Creates a new `noise threshold `_ for a `noise sensor `_. Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. @@ -71,7 +71,7 @@ def update( name: Optional[str] = None, noise_threshold_decibels: Optional[float] = None, noise_threshold_nrs: Optional[float] = None, - starts_daily_at: Optional[str] = None + starts_daily_at: Optional[str] = None, ) -> None: """Updates a `noise threshold `_ for a `noise sensor `_. @@ -105,7 +105,7 @@ def create( starts_daily_at: str, name: Optional[str] = None, noise_threshold_decibels: Optional[float] = None, - noise_threshold_nrs: Optional[float] = None + noise_threshold_nrs: Optional[float] = None, ) -> NoiseThreshold: """Creates a new `noise threshold `_ for a `noise sensor `_. Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. @@ -201,7 +201,7 @@ def update( name: Optional[str] = None, noise_threshold_decibels: Optional[float] = None, noise_threshold_nrs: Optional[float] = None, - starts_daily_at: Optional[str] = None + starts_daily_at: Optional[str] = None, ) -> None: """Updates a `noise threshold `_ for a `noise sensor `_. diff --git a/seam/routes/phones.py b/seam/routes/phones.py index 2c19692f..1ea284cc 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -33,7 +33,7 @@ def list( self, *, acs_credential_id: Optional[str] = None, - owner_user_identity_id: Optional[str] = None + owner_user_identity_id: Optional[str] = None, ) -> List[Phone]: """Returns a list of all `phones `_. To filter the list of returned phones by a specific owner user identity or credential, include the ``owner_user_identity_id`` or ``acs_credential_id``, respectively, in the request body. @@ -87,7 +87,7 @@ def list( self, *, acs_credential_id: Optional[str] = None, - owner_user_identity_id: Optional[str] = None + owner_user_identity_id: Optional[str] = None, ) -> List[Phone]: """Returns a list of all `phones `_. To filter the list of returned phones by a specific owner user identity or credential, include the ``owner_user_identity_id`` or ``acs_credential_id``, respectively, in the request body. diff --git a/seam/routes/phones_simulate.py b/seam/routes/phones_simulate.py index 3144dba7..b11b693a 100644 --- a/seam/routes/phones_simulate.py +++ b/seam/routes/phones_simulate.py @@ -13,7 +13,7 @@ def create_sandbox_phone( user_identity_id: str, assa_abloy_metadata: Optional[Dict[str, Any]] = None, custom_sdk_installation_id: Optional[str] = None, - phone_metadata: Optional[Dict[str, Any]] = None + phone_metadata: Optional[Dict[str, Any]] = None, ) -> Phone: """Creates a new simulated phone in a `sandbox workspace `_. See also `Creating a Simulated Phone for a User Identity `_. @@ -40,7 +40,7 @@ def create_sandbox_phone( user_identity_id: str, assa_abloy_metadata: Optional[Dict[str, Any]] = None, custom_sdk_installation_id: Optional[str] = None, - phone_metadata: Optional[Dict[str, Any]] = None + phone_metadata: Optional[Dict[str, Any]] = None, ) -> Phone: """Creates a new simulated phone in a `sandbox workspace `_. See also `Creating a Simulated Phone for a User Identity `_. diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index 4ebb7ed4..1add7f83 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -46,7 +46,7 @@ def create( customer_data: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - space_key: Optional[str] = None + space_key: Optional[str] = None, ) -> Space: """Creates a new space. @@ -94,7 +94,7 @@ def get_related( exclude: Optional[List[str]] = None, include: Optional[List[str]] = None, space_ids: Optional[List[str]] = None, - space_keys: Optional[List[str]] = None + space_keys: Optional[List[str]] = None, ) -> Batch: """Gets all related resources for one or more Spaces. @@ -117,7 +117,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - space_key: Optional[str] = None + space_key: Optional[str] = None, ) -> List[Space]: """Returns a list of all spaces. @@ -175,7 +175,7 @@ def update( device_ids: Optional[List[str]] = None, name: Optional[str] = None, space_id: Optional[str] = None, - space_key: Optional[str] = None + space_key: Optional[str] = None, ) -> Space: """Updates an existing space. @@ -263,7 +263,7 @@ def create( customer_data: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - space_key: Optional[str] = None + space_key: Optional[str] = None, ) -> Space: """Creates a new space. @@ -343,7 +343,7 @@ def get_related( exclude: Optional[List[str]] = None, include: Optional[List[str]] = None, space_ids: Optional[List[str]] = None, - space_keys: Optional[List[str]] = None + space_keys: Optional[List[str]] = None, ) -> Batch: """Gets all related resources for one or more Spaces. @@ -378,7 +378,7 @@ def list( limit: Optional[float] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - space_key: Optional[str] = None + space_key: Optional[str] = None, ) -> List[Space]: """Returns a list of all spaces. @@ -474,7 +474,7 @@ def update( device_ids: Optional[List[str]] = None, name: Optional[str] = None, space_id: Optional[str] = None, - space_key: Optional[str] = None + space_key: Optional[str] = None, ) -> Space: """Updates an existing space. diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index f0741076..2008310d 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -34,7 +34,7 @@ def activate_climate_preset( *, climate_preset_key: str, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Activates a specified `climate preset `_ for a specified `thermostat `_. @@ -54,7 +54,7 @@ def cool( device_id: str, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `cool mode `_. @@ -84,7 +84,7 @@ def create_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None + name: Optional[str] = None, ) -> None: """Creates a `climate preset `_ for a specified `thermostat `_. @@ -131,7 +131,7 @@ def heat( device_id: str, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `heat mode `_. @@ -155,7 +155,7 @@ def heat_cool( cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `heat-cool ("auto") mode `_. @@ -193,7 +193,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -237,7 +237,7 @@ def off( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `"off" mode `_. @@ -267,7 +267,7 @@ def set_fan_mode( device_id: str, fan_mode: Optional[str] = None, fan_mode_setting: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `fan mode setting `_ for a specified `thermostat `_. @@ -292,7 +292,7 @@ def set_hvac_mode( cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `HVAC mode `_ for a specified `thermostat `_. @@ -321,7 +321,7 @@ def set_temperature_threshold( lower_limit_celsius: Optional[float] = None, lower_limit_fahrenheit: Optional[float] = None, upper_limit_celsius: Optional[float] = None, - upper_limit_fahrenheit: Optional[float] = None + upper_limit_fahrenheit: Optional[float] = None, ) -> None: """Sets a `temperature threshold `_ for a specified thermostat. Seam emits a ``thermostat.temperature_threshold_exceeded`` event and adds a warning on a thermostat if it reports a temperature outside the threshold range. @@ -352,7 +352,7 @@ def update_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None + name: Optional[str] = None, ) -> None: """Updates a specified `climate preset `_ for a specified `thermostat `_. @@ -394,7 +394,7 @@ def update_weekly_program( thursday_program_id: Optional[str] = None, tuesday_program_id: Optional[str] = None, wednesday_program_id: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Updates the thermostat weekly program for a thermostat device. To configure a weekly program, specify the ID of the daily program that you want to use for each day of the week. When you update a weekly program, the set of programs that you specify overwrites any previous weekly program for the thermostat. @@ -447,7 +447,7 @@ def activate_climate_preset( *, climate_preset_key: str, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Activates a specified `climate preset `_ for a specified `thermostat `_. @@ -487,7 +487,7 @@ def cool( device_id: str, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `cool mode `_. @@ -537,7 +537,7 @@ def create_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None + name: Optional[str] = None, ) -> None: """Creates a `climate preset `_ for a specified `thermostat `_. @@ -620,7 +620,7 @@ def heat( device_id: str, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `heat mode `_. @@ -664,7 +664,7 @@ def heat_cool( cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `heat-cool ("auto") mode `_. @@ -726,7 +726,7 @@ def list( search: Optional[str] = None, space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None + user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -806,7 +806,7 @@ def off( self, *, device_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets a specified `thermostat `_ to `"off" mode `_. @@ -860,7 +860,7 @@ def set_fan_mode( device_id: str, fan_mode: Optional[str] = None, fan_mode_setting: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `fan mode setting `_ for a specified `thermostat `_. @@ -905,7 +905,7 @@ def set_hvac_mode( cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `HVAC mode `_ for a specified `thermostat `_. @@ -960,7 +960,7 @@ def set_temperature_threshold( lower_limit_celsius: Optional[float] = None, lower_limit_fahrenheit: Optional[float] = None, upper_limit_celsius: Optional[float] = None, - upper_limit_fahrenheit: Optional[float] = None + upper_limit_fahrenheit: Optional[float] = None, ) -> None: """Sets a `temperature threshold `_ for a specified thermostat. Seam emits a ``thermostat.temperature_threshold_exceeded`` event and adds a warning on a thermostat if it reports a temperature outside the threshold range. @@ -1005,7 +1005,7 @@ def update_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None + name: Optional[str] = None, ) -> None: """Updates a specified `climate preset `_ for a specified `thermostat `_. @@ -1075,7 +1075,7 @@ def update_weekly_program( thursday_program_id: Optional[str] = None, tuesday_program_id: Optional[str] = None, wednesday_program_id: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Updates the thermostat weekly program for a thermostat device. To configure a weekly program, specify the ID of the daily program that you want to use for each day of the week. When you update a weekly program, the set of programs that you specify overwrites any previous weekly program for the thermostat. diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index 56e886da..5ce877ae 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -37,7 +37,7 @@ def update( name: str, periods: List[Dict[str, Any]], thermostat_daily_program_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Updates a specified thermostat daily program. The periods that you specify overwrite any existing periods for the daily program. @@ -103,7 +103,7 @@ def update( name: str, periods: List[Dict[str, Any]], thermostat_daily_program_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Updates a specified thermostat daily program. The periods that you specify overwrite any existing periods for the daily program. diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index 24935b3e..6acd9c91 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -16,7 +16,7 @@ def create( starts_at: str, is_override_allowed: Optional[bool] = None, max_override_period_minutes: Optional[int] = None, - name: Optional[str] = None + name: Optional[str] = None, ) -> ThermostatSchedule: """Creates a new `thermostat schedule `_ for a specified `thermostat `_. @@ -77,7 +77,7 @@ def update( is_override_allowed: Optional[bool] = None, max_override_period_minutes: Optional[int] = None, name: Optional[str] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> None: """Updates a specified `thermostat schedule `_. @@ -112,7 +112,7 @@ def create( starts_at: str, is_override_allowed: Optional[bool] = None, max_override_period_minutes: Optional[int] = None, - name: Optional[str] = None + name: Optional[str] = None, ) -> ThermostatSchedule: """Creates a new `thermostat schedule `_ for a specified `thermostat `_. @@ -213,7 +213,7 @@ def update( is_override_allowed: Optional[bool] = None, max_override_period_minutes: Optional[int] = None, name: Optional[str] = None, - starts_at: Optional[str] = None + starts_at: Optional[str] = None, ) -> None: """Updates a specified `thermostat schedule `_. diff --git a/seam/routes/thermostats_simulate.py b/seam/routes/thermostats_simulate.py index 94d2a1be..ae488ec5 100644 --- a/seam/routes/thermostats_simulate.py +++ b/seam/routes/thermostats_simulate.py @@ -14,7 +14,7 @@ def hvac_mode_adjusted( cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, - heating_set_point_fahrenheit: Optional[float] = None + heating_set_point_fahrenheit: Optional[float] = None, ) -> None: """Simulates having adjusted the `HVAC mode `_ for a `thermostat `_. Only applicable for `sandbox devices `_. See also `Testing Your Thermostat App with Simulate Endpoints `_. @@ -38,7 +38,7 @@ def temperature_reached( *, device_id: str, temperature_celsius: Optional[float] = None, - temperature_fahrenheit: Optional[float] = None + temperature_fahrenheit: Optional[float] = None, ) -> None: """Simulates a `thermostat `_ reaching a specified temperature. Only applicable for `sandbox devices `_. See also `Testing Your Thermostat App with Simulate Endpoints `_. @@ -64,7 +64,7 @@ def hvac_mode_adjusted( cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, - heating_set_point_fahrenheit: Optional[float] = None + heating_set_point_fahrenheit: Optional[float] = None, ) -> None: """Simulates having adjusted the `HVAC mode `_ for a `thermostat `_. Only applicable for `sandbox devices `_. See also `Testing Your Thermostat App with Simulate Endpoints `_. @@ -104,7 +104,7 @@ def temperature_reached( *, device_id: str, temperature_celsius: Optional[float] = None, - temperature_fahrenheit: Optional[float] = None + temperature_fahrenheit: Optional[float] = None, ) -> None: """Simulates a `thermostat `_ reaching a specified temperature. Only applicable for `sandbox devices `_. See also `Testing Your Thermostat App with Simulate Endpoints `_. diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index 819ddd91..f6d140bc 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -28,7 +28,7 @@ def add_acs_user( *, acs_user_id: str, user_identity_id: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> None: """Adds a specified `access system user `_ to a specified `user identity `_. @@ -52,7 +52,7 @@ def create( email_address: Optional[str] = None, full_name: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> UserIdentity: """Creates a new `user identity `_. @@ -82,7 +82,7 @@ def generate_instant_key( *, user_identity_id: str, customization_profile_id: Optional[str] = None, - max_use_count: Optional[float] = None + max_use_count: Optional[float] = None, ) -> InstantKey: """Generates a new `instant key `_ for a specified `user identity `_. @@ -100,7 +100,7 @@ def get( self, *, user_identity_id: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> UserIdentity: """Returns a specified `user identity `_. @@ -130,7 +130,7 @@ def list( limit: Optional[int] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> List[UserIdentity]: """Returns a list of all `user identities `_. @@ -213,7 +213,7 @@ def update( email_address: Optional[str] = None, full_name: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> None: """Updates a specified `user identity `_. @@ -244,7 +244,7 @@ def add_acs_user( *, acs_user_id: str, user_identity_id: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> None: """Adds a specified `access system user `_ to a specified `user identity `_. @@ -278,7 +278,7 @@ def create( email_address: Optional[str] = None, full_name: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> UserIdentity: """Creates a new `user identity `_. @@ -328,7 +328,7 @@ def generate_instant_key( *, user_identity_id: str, customization_profile_id: Optional[str] = None, - max_use_count: Optional[float] = None + max_use_count: Optional[float] = None, ) -> InstantKey: """Generates a new `instant key `_ for a specified `user identity `_. @@ -358,7 +358,7 @@ def get( self, *, user_identity_id: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> UserIdentity: """Returns a specified `user identity `_. @@ -404,7 +404,7 @@ def list( limit: Optional[int] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None + user_identity_ids: Optional[List[str]] = None, ) -> List[UserIdentity]: """Returns a list of all `user identities `_. @@ -549,7 +549,7 @@ def update( email_address: Optional[str] = None, full_name: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> None: """Updates a specified `user identity `_. diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index bc5df14a..c5109c2c 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -22,7 +22,7 @@ def list( created_before: Optional[str] = None, limit: Optional[int] = None, page_cursor: Optional[str] = None, - search: Optional[str] = None + search: Optional[str] = None, ) -> List[UnmanagedUserIdentity]: """Returns a list of all unmanaged `user identities `_ (where is_managed = false). @@ -43,7 +43,7 @@ def update( *, is_managed: bool, user_identity_id: str, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> None: """Updates an unmanaged `user identity `_ to make it managed. @@ -84,7 +84,7 @@ def list( created_before: Optional[str] = None, limit: Optional[int] = None, page_cursor: Optional[str] = None, - search: Optional[str] = None + search: Optional[str] = None, ) -> List[UnmanagedUserIdentity]: """Returns a list of all unmanaged `user identities `_ (where is_managed = false). @@ -119,7 +119,7 @@ def update( *, is_managed: bool, user_identity_id: str, - user_identity_key: Optional[str] = None + user_identity_key: Optional[str] = None, ) -> None: """Updates an unmanaged `user identity `_ to make it managed. diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index 9b9b9195..d223c346 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -20,7 +20,7 @@ def create( webview_logo_shape: Optional[str] = None, webview_primary_button_color: Optional[str] = None, webview_primary_button_text_color: Optional[str] = None, - webview_success_message: Optional[str] = None + webview_success_message: Optional[str] = None, ) -> Workspace: """Creates a new `workspace `_. @@ -81,7 +81,7 @@ def update( is_publishable_key_auth_enabled: Optional[bool] = None, is_suspended: Optional[bool] = None, name: Optional[str] = None, - organization_id: Optional[str] = None + organization_id: Optional[str] = None, ) -> None: """Updates the `workspace `_ associated with the authentication value. @@ -117,7 +117,7 @@ def create( webview_logo_shape: Optional[str] = None, webview_primary_button_color: Optional[str] = None, webview_primary_button_text_color: Optional[str] = None, - webview_success_message: Optional[str] = None + webview_success_message: Optional[str] = None, ) -> Workspace: """Creates a new `workspace `_. @@ -225,7 +225,7 @@ def update( is_publishable_key_auth_enabled: Optional[bool] = None, is_suspended: Optional[bool] = None, name: Optional[str] = None, - organization_id: Optional[str] = None + organization_id: Optional[str] = None, ) -> None: """Updates the `workspace `_ associated with the authentication value. diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..9453d94a --- /dev/null +++ b/uv.lock @@ -0,0 +1,1583 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version >= '3.12' and python_full_version < '3.15'", + "python_full_version == '3.11.*'", + "python_full_version < '3.11'", +] + +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, +] + +[[package]] +name = "anyio" +version = "4.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, +] + +[[package]] +name = "astroid" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576, upload-time = "2024-07-20T12:57:43.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348, upload-time = "2024-07-20T12:57:40.886Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "black" +version = "24.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/47/c9997eb470a7f48f7aaddd3d9a828244a2e4199569e38128715c48059ac1/black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d", size = 642299, upload-time = "2024-04-26T00:32:15.305Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/f6/3adc48c210527a7b651aaed43824a9b8bd04b3fb361a5227bad046e1c876/black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce", size = 1631487, upload-time = "2024-04-26T00:40:28.969Z" }, + { url = "https://files.pythonhosted.org/packages/a2/25/70aa1bec12c841a03e333e312daa0cf2fee50ea6336ac4851c93c0e2b411/black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021", size = 1456317, upload-time = "2024-04-26T00:39:10.333Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7d/7f8df0fdbbbefc4362d3eca6b69b7a8a4249a8a88dabc00a207d31fddcd7/black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063", size = 1822765, upload-time = "2024-04-26T00:34:56.436Z" }, + { url = "https://files.pythonhosted.org/packages/5c/21/1ee97841c469c1551133cbe47448cdba9628c7d9431f74f114f02e3b233c/black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96", size = 1409336, upload-time = "2024-04-26T00:35:30.392Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f7/591d601c3046ceb65b97291dfe87fa25124cffac3d97aaaba89d0f0d7bdf/black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474", size = 1615013, upload-time = "2024-04-26T00:39:49.415Z" }, + { url = "https://files.pythonhosted.org/packages/c9/17/5e0036b265bbf6bc44970d93d48febcbc03701b671db3c9603fd43ebc616/black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c", size = 1436163, upload-time = "2024-04-26T00:40:20.267Z" }, + { url = "https://files.pythonhosted.org/packages/c5/48/34176b522e8cff4620a5d96c2e323ff2413f574870eb25efa8025885e028/black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb", size = 1803382, upload-time = "2024-04-26T00:34:38.665Z" }, + { url = "https://files.pythonhosted.org/packages/74/ce/e8eec1a77edbfa982bee3b5460dcdd4fe0e4e3165fc15d8ec44d04da7776/black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1", size = 1417802, upload-time = "2024-04-26T00:35:08.804Z" }, + { url = "https://files.pythonhosted.org/packages/f4/75/3a29de3bda4006cc280d833b5d961cf7df3810a21f49e7a63a7e551fb351/black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d", size = 1645176, upload-time = "2024-04-26T00:42:35.606Z" }, + { url = "https://files.pythonhosted.org/packages/be/b8/9c152301774fa62a265b035a8ede4d6280827904ea1af8c3be10a28d3187/black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04", size = 1446227, upload-time = "2024-04-26T00:40:35.195Z" }, + { url = "https://files.pythonhosted.org/packages/25/6d/eb15a1b155f755f43766cc473618c6e1de6555d6a1764965643f486dcf01/black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc", size = 1832011, upload-time = "2024-04-26T00:34:37.825Z" }, + { url = "https://files.pythonhosted.org/packages/43/24/942b22571b0171be7c6f701cdc3e3b7221f5b522ef02cf82503a547a657b/black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0", size = 1428800, upload-time = "2024-04-26T00:35:55.838Z" }, + { url = "https://files.pythonhosted.org/packages/0f/89/294c9a6b6c75a08da55e9d05321d0707e9418735e3062b12ef0f54c33474/black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c", size = 205925, upload-time = "2024-04-26T00:32:12.495Z" }, +] + +[[package]] +name = "certifi" +version = "2026.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/31/4971872b3ed8715346231fb6eb4da8fcba65a4143c189db151ee28a2812b/charset_normalizer-3.5.0.tar.gz", hash = "sha256:49bd5feb59b0bf3cbf6ebcf4352e371c95b9da9bacd4449f8b64d0ad2c10a26e", size = 169295, upload-time = "2026-08-12T14:35:31.624Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/8e/1e9a03657c2f12960c67b05e0550059787d4c8fc3fae822672f4fe7f40c5/charset_normalizer-3.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2478bd3b2ead3962a484fb802891be40d10049fb74f83e09cb4463fad023fea", size = 349795, upload-time = "2026-08-12T14:31:47.546Z" }, + { url = "https://files.pythonhosted.org/packages/76/e5/a42c8884ec0321c56124a5b43c135efe75260dad4f70ea0ffba4dcac85ec/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7cdded069549b5eae3d5d9bb6c2e5bb4fe83f9b81863e2a193cd747bf197aebb", size = 250677, upload-time = "2026-08-12T14:31:48.918Z" }, + { url = "https://files.pythonhosted.org/packages/5d/21/d94021b005d2e660fd13634eecfe4ad378b0d0bbb45134809aed42b94a99/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aff38231e3171c578b2c449a01afa44e9ff40844597a32873da102394f63d28e", size = 239868, upload-time = "2026-08-12T14:31:50.107Z" }, + { url = "https://files.pythonhosted.org/packages/47/d0/8b9b07e41501bfcdfe380b826481033ca2eb5fa4b5507eb64dbc4cba9885/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4346a693c08b1d0cfc0e3325bfb0ecd4322fb1a6904d68cf416f8da5e981b234", size = 280688, upload-time = "2026-08-12T14:31:51.182Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/b83b3469e596adff778bf3c8ca8f705822c1c3c0ada8477a0d00113bfa59/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b787efadba00f5da6fe89513bfbe3852d52ca3a448fdec165765cb3b44a80248", size = 277133, upload-time = "2026-08-12T14:31:52.362Z" }, + { url = "https://files.pythonhosted.org/packages/0c/aa/a080c4facaab4ece837558649eb6e2c760867d1c0ea52220cbbc4e89dcaa/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:143792a43e06dc3b27fc891948406e251502dc19ff9216cd80182b79131be5c5", size = 261492, upload-time = "2026-08-12T14:31:53.628Z" }, + { url = "https://files.pythonhosted.org/packages/f9/24/e23bcc9d4bc897bd620bc39c6b6052574f5ef93b85b50b175dd427a7dc07/charset_normalizer-3.5.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d74bcf1cdd8ac8267fb216473ce6b112efa07b163536288094541415084d131c", size = 259739, upload-time = "2026-08-12T14:31:54.86Z" }, + { url = "https://files.pythonhosted.org/packages/ef/85/f4c391c807f67c6483d02c02d28683e2fe229403728369c0cd33f61e4292/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbe543d957213fc9a3db4979a8e171b7aa7504c1d737029defdb03a6095a38", size = 252120, upload-time = "2026-08-12T14:31:56.227Z" }, + { url = "https://files.pythonhosted.org/packages/4e/cb/dcf5aa14e24270613fafa2f02b3979820c244610410e5508696ca96bd0d2/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3587d94b5c9f05c2dc4c3f3d47aba6375ff141a21adae3051d8d4d53e8a937c0", size = 241622, upload-time = "2026-08-12T14:31:57.54Z" }, + { url = "https://files.pythonhosted.org/packages/83/a9/decd1c1cc01ccbfce66845096e3945397e4ede354f31cc5cb28fc6ca601b/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7cb4cd266bd85613367fb85a30cfbf6fe6349919e87e18ca8dba584951bfb8a", size = 280745, upload-time = "2026-08-12T14:31:58.91Z" }, + { url = "https://files.pythonhosted.org/packages/8b/47/420ddefc04f089e4497b3bcbadea37586477713300c13204fb4a7c736071/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ffdd7ac514301d0a67f7c23b9f2b431ef909a3c3dd6c3766668d0a6f5900c94e", size = 258669, upload-time = "2026-08-12T14:32:00.069Z" }, + { url = "https://files.pythonhosted.org/packages/58/73/01d5173097a5f0a13417e77db9ba3aea24de54421344c8e0c4973bf8cf21/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3684ebbdffd51329ac44245d1d227d90b965797aa1a8abd026568a1f6ae88811", size = 277344, upload-time = "2026-08-12T14:32:01.211Z" }, + { url = "https://files.pythonhosted.org/packages/41/4a/300ef2598da520744aca7c49370e48732cab1f04de5f784f0e4f0e9e1d11/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b8788f114845c01f2b520e0b91ea58d143276cfc0483aa943e815f7b9555c15", size = 263440, upload-time = "2026-08-12T14:32:02.421Z" }, + { url = "https://files.pythonhosted.org/packages/be/5e/78dd366df9ae4810039c0f799ec47d325a5cb76e92bb707ca9039b7c2306/charset_normalizer-3.5.0-cp310-cp310-win32.whl", hash = "sha256:9a1d9b13e5e394e13e3c316f0d910d100b17681ff59797f30da1dba032061296", size = 181680, upload-time = "2026-08-12T14:32:03.932Z" }, + { url = "https://files.pythonhosted.org/packages/67/d6/455982837da317ce47cafe35260f446f218f5843090b77a59ee69aa0ad94/charset_normalizer-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a54587f93f2e289f8faf25b35c997d4cc75cf677485ac6f50c985715989f99c", size = 205665, upload-time = "2026-08-12T14:32:05.045Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e5/5b285d50d4fc82111c18ea4fff8b5e1916a257b98dcd5cd0abf1c165473b/charset_normalizer-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:38a395079f229a631dece74e24c69c1f612536dd51f345a7d6a98abe2d3e047a", size = 184409, upload-time = "2026-08-12T14:32:06.503Z" }, + { url = "https://files.pythonhosted.org/packages/50/42/71e4e3bfe59202feef062c68487f54c6adf501cfbe087ecd93e3cd597fea/charset_normalizer-3.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46a37ea7fcf9ae01d71b2e5ece19f1565987f3e308394b829197cbefc061f92", size = 349110, upload-time = "2026-08-12T14:32:07.832Z" }, + { url = "https://files.pythonhosted.org/packages/0f/dd/fd3386d0fbd358d3b5c7a2fa5bf312afe6159b04fafeb67d39fa971d7448/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cdfed4d7a59333c8220c67dd3be4e7a6c887b67453a64394022dcc919570add", size = 250773, upload-time = "2026-08-12T14:32:09.113Z" }, + { url = "https://files.pythonhosted.org/packages/17/ad/4901a66d6d3b17f1096725d7e50266132c16555aa6a70047fe1cf262b4b2/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9491f594859b68052edebd69e05fb045055a713b57a67974e6c1553b4e503c39", size = 240229, upload-time = "2026-08-12T14:32:10.43Z" }, + { url = "https://files.pythonhosted.org/packages/43/b7/1d790e0425e0f4c99e9b89a3956a94a6d2d0c6f01b2a5eca93d8f082d5ac/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:420b19411959eec115063229536788e6b32d0a7fa907d6b940317919120d702d", size = 280757, upload-time = "2026-08-12T14:32:11.628Z" }, + { url = "https://files.pythonhosted.org/packages/44/bb/4b8c8086c67636e52d6354ad17697f54a00b40041ca53dc765737e21709b/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a565303d118ea3b94a4b6c076bf568069726be414e43b06d58f7070b076ce11d", size = 276174, upload-time = "2026-08-12T14:32:12.808Z" }, + { url = "https://files.pythonhosted.org/packages/25/fa/690a11924c40c766d258d2b74d817cc5efe7bbcfceeedc5c0f35256d7524/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:815f143a91983ba3041bba066e492ae3c42de523fb1c699685a1abf3313b7d1b", size = 261808, upload-time = "2026-08-12T14:32:14.138Z" }, + { url = "https://files.pythonhosted.org/packages/53/2d/4a6945eff0c8f684e3f5b7b978644ab18ba9198da060dbaa1d9206bc6cc9/charset_normalizer-3.5.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c5e981a5ac8641381efe6f0029467500661616a530d27bc6eedfe45f840599f8", size = 259922, upload-time = "2026-08-12T14:32:15.485Z" }, + { url = "https://files.pythonhosted.org/packages/60/4d/10ac7e07bbf7ea569effeb9524e32f345f7e643800b20d538fb4706eab4e/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1a573e1e428f93908e79e04b349717f400e720f2f82285f0aaaf3ee0ff7f4c79", size = 252315, upload-time = "2026-08-12T14:32:16.764Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/1ddacf7aa7da12097f229a6a4e71a70200937a621b3617f6dc819fb99a66/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:32e6d56dd825205f81e5c45bcebb4df6a11fb2bbf4969a01ef156d6ced90c224", size = 240600, upload-time = "2026-08-12T14:32:17.956Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/293444d48c8d86fe51552262117134a9fc666d68cbbbc9b8c1b2b35a29be/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:30ae26a1adcd943690dcbbc47f28be762bae9e08ad7442b78c86b1c0dd5a626c", size = 280788, upload-time = "2026-08-12T14:32:19.1Z" }, + { url = "https://files.pythonhosted.org/packages/12/ed/0e34e40584f51eda38d4a5daf25fd8586366347efd4b2470dbf64710e778/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f51a19dc52197a20218b05ec5336d0c6b3b09935f838724722032c8d45dc91a", size = 258425, upload-time = "2026-08-12T14:32:20.207Z" }, + { url = "https://files.pythonhosted.org/packages/72/b2/c1b1c27f6f0ef35b21a8bdb592854bbf7f219629db3477f74c0b1380e0ed/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6e44bc2780516b3df986d6fe33103c7080cd9dcd5576fe3cb4b0f64309c8f22b", size = 277437, upload-time = "2026-08-12T14:32:21.345Z" }, + { url = "https://files.pythonhosted.org/packages/b2/bd/a1b7d959a37847675adb2f5978f81703306dd78df4fefb82ee5a1cc5e37f/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7faa47b56070b3dd6f4898ed28528843ab130d53266cb9948d9b1f3bb1a5c5e8", size = 262947, upload-time = "2026-08-12T14:32:22.613Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fd/a76a2d7e639bfc6a9c869371e34f28231eaca7d7126ec19895aa38eedb51/charset_normalizer-3.5.0-cp311-cp311-win32.whl", hash = "sha256:830c04a49998b5ed58c8b642c65b7b26419397f52392a64121ba9fd0e95e7f9f", size = 181313, upload-time = "2026-08-12T14:32:23.736Z" }, + { url = "https://files.pythonhosted.org/packages/97/84/6fc03e802578df41a2ab9b6a1f26657fb92e32285a603ad5852a4a4f68c1/charset_normalizer-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:8cb9b6892b53bd6d11fa4cde3dbee020b1f0b6656be1fbaa1ec0d4324a7839db", size = 206197, upload-time = "2026-08-12T14:32:24.938Z" }, + { url = "https://files.pythonhosted.org/packages/0a/27/7208360ff1901607359869fcc45ca0989d597f3e30777ba30c7254170587/charset_normalizer-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:2403b489c103e9a18c835863fc6dd54361355c8291d4cafdb37492b683440b9b", size = 184925, upload-time = "2026-08-12T14:32:26.123Z" }, + { url = "https://files.pythonhosted.org/packages/6d/3c/045ea64ea5a550870dd8ab60b2242870328d53f17d2be593b4f9f3121474/charset_normalizer-3.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:98820e1ceb25c6df7a80c4fd8efa59cb121f99bc7c4c1693ad94a2caff5b311d", size = 343861, upload-time = "2026-08-12T14:32:27.254Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1b/7502be709db899d5b4801509829188b3a5a10969411da9c846115a5f1b70/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:608553f476fca509537e804c4a71f5eb166ce63b75141f89c2c686ce1aa36956", size = 237550, upload-time = "2026-08-12T14:32:28.385Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ce/66392661375148d9455c17bee25509a54e28c39969e34befa48ec8777936/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6753de11eef42f1c321b26d682957d92c7f7bbce6530f34bbe0f9291dd37cc6f", size = 229673, upload-time = "2026-08-12T14:32:29.668Z" }, + { url = "https://files.pythonhosted.org/packages/6a/64/f58c32a8d4ecf55b82ee61ee9aa6a664d4afcd36c72feb4c926fd6fe9af8/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f76dc0a47f94cb9b69d86f01e477f4b0371ca70208b9ccea7e063c41eed9046", size = 260768, upload-time = "2026-08-12T14:32:30.891Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ea/36c90e59a96386174377e855479ec154221ef001e96637e0b23be92489c4/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c387c6bf91b4774e359a48a179e2872b8e8bf741e4fde06ba8d1665eb9a4760a", size = 257880, upload-time = "2026-08-12T14:32:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/23/35/5b85772eb82528ef22ba29487ad544a7049dfd27f35b1a5a55dbc0843048/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14f6904a3cf870abf044df3a8c4924ac6c8ef77e9896586fd37e73ae96cff2af", size = 247547, upload-time = "2026-08-12T14:32:33.495Z" }, + { url = "https://files.pythonhosted.org/packages/b6/14/ba11a99c2a22ab04c2d5383a700b378cb463a78ab15f36444cabc10cd671/charset_normalizer-3.5.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cce46dd29d73e135e8087b96eb62a4aca6d69391b7f97808c6588ebed3178f3", size = 243326, upload-time = "2026-08-12T14:32:34.794Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4a/cadba3f2400b45aa1d62a4ae0298bf58a3b30b1158baf15c338c7ce5b601/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b476cdb63df22da2b91837593380be3ddbe406f36c506c1c91d80e7196b66288", size = 238820, upload-time = "2026-08-12T14:32:35.984Z" }, + { url = "https://files.pythonhosted.org/packages/47/41/d5188b9342d75b72c2b05d3ee373f01a691397e770f001ee05e3b37925f5/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1f56ce84b317ef2a59d7d3461891c7597c79247d2192bb8114c68a1a1debfcc0", size = 231661, upload-time = "2026-08-12T14:32:37.191Z" }, + { url = "https://files.pythonhosted.org/packages/13/5f/df38fa972c4e945c3d8cee2bc4e610613af522fd359c7dc7a74c419f0278/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9ce0f885239357379d92fd9a5fddbe20f0e30e0527c29ba69f8e99eeb1304a76", size = 261459, upload-time = "2026-08-12T14:32:38.369Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d7/043ff7720067a3beee05523465ac9c1c846c68b7884930dd483f72ee5ab6/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:96ae7ab5d8155fde927aa0864fbc8ba3cc4fde6d41ab0c7cea9d6012b4978603", size = 242300, upload-time = "2026-08-12T14:32:39.505Z" }, + { url = "https://files.pythonhosted.org/packages/19/f6/33980b7b802a048e546a6d9ad2ea783a6cf6b10a86aaccd10db462d8b913/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bf91921009025e96ce57a03ced6d14604fc3baf0530351638e9504a55da6fa3b", size = 259101, upload-time = "2026-08-12T14:32:41.02Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b7/0d19bde844bff9165377c1da9ef3c4792a4c24bd49b5b7094d9e6f6ab58b/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0b2e44e6d42d1a4ff78ccc219a93c5449105d10b16198d1aea581080df8073f9", size = 249246, upload-time = "2026-08-12T14:32:42.47Z" }, + { url = "https://files.pythonhosted.org/packages/1c/cc/2c34fdfaacdf0e96e880ef562cbf80a9b5f8ea97e0dd9e57ba348a9c65cf/charset_normalizer-3.5.0-cp312-cp312-win32.whl", hash = "sha256:deb99535e9bf0bea8e274c6413eb939a21be35a3f492678dba4d5b1f4d70f142", size = 178025, upload-time = "2026-08-12T14:32:43.909Z" }, + { url = "https://files.pythonhosted.org/packages/76/d0/c34dbd1df23bcbdc1b5d2f48256340d72fa747f1eb03924a9d2fa35ed85b/charset_normalizer-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:e54dd1a66fa4bce0ccaf0db9dde336e49b3eec646dc4c1c0991279369d373a14", size = 200143, upload-time = "2026-08-12T14:32:45.254Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/4e6bf1465d60c3d8f488d5bde140d0cb91cd23ccab0dc2895cc6c6982047/charset_normalizer-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:b8ea208b304587d47931b36481342d20336e0d338ab052f8b4305926482598d6", size = 180046, upload-time = "2026-08-12T14:32:46.545Z" }, + { url = "https://files.pythonhosted.org/packages/1d/be/cc7b7b6fc41984902c0d31b06f5d9297e67705c1dae9352608e5540fad09/charset_normalizer-3.5.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:5c23fa4f6eccdd601949cb00f3988c01d64e671d8faba356397971077022e144", size = 211050, upload-time = "2026-08-12T14:32:47.81Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ae/e3ec8f17313609f43f7b323012fdb1ee37b83432277ca4eceba83e00366c/charset_normalizer-3.5.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:07f6f42b5a6325df35b458004fb5f9f29bf502d89287a33c7cdef3590e31de0f", size = 222768, upload-time = "2026-08-12T14:32:49.027Z" }, + { url = "https://files.pythonhosted.org/packages/c5/50/9f9c0d7ccc1512d49e27a0e7c12c58ec71dfe91698fa4326f058c33e1f1b/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8efc3f1563ed431882dd0dc0411b5f8ace1b1b89074981deaf6bd8af77dbe1bc", size = 193907, upload-time = "2026-08-12T14:32:50.414Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1d/cfe7b745ef7f4c3b7214581955b5a0869ba2ac551a58fc11036281ae167c/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:368eb2fc9482158b3a3386e8f01fa61f479c968e9a19ceab8f0188b86b312991", size = 197135, upload-time = "2026-08-12T14:32:51.605Z" }, + { url = "https://files.pythonhosted.org/packages/28/55/30fafdcfca9ba616bc394240545e4cd52f4f66dea43ded81b7d2d5274fde/charset_normalizer-3.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:826a295a039178479a325be1ae60eded1f0b10f7dda749df59e2440de8f61d64", size = 339892, upload-time = "2026-08-12T14:32:52.82Z" }, + { url = "https://files.pythonhosted.org/packages/f4/08/bdca5fc2bdc36ee443673dc7d12b23885a5a7b282bef85a1a4c3b325b40e/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70ff1c16eb0eb5ee6bb12739292347f981a5ba764cc4df1bc2e69b0405d4ac3b", size = 239439, upload-time = "2026-08-12T14:32:54.058Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9e/506c8d7a7722bba7c8cdd78c1b5ef23bda92bfbe0b3e28ea84673d519a0f/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cfdab178a4add5483e26a9bb1c16d8018ccf39b4be7a3aea6c3979e6828f2ee", size = 227896, upload-time = "2026-08-12T14:32:55.326Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1a/dd828f2b1d6f4bf10821b9a74d866be05ffcdbfcddfc501d6fe6428762a7/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6083d10a846218502d664375b9448508d9fa580bd834567423156c6abfbe899d", size = 262548, upload-time = "2026-08-12T14:32:56.484Z" }, + { url = "https://files.pythonhosted.org/packages/be/81/196d26f6bd78b93e0d451b69082a71027ceeddd4b0be9170b81bb038f824/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f211c21aa316cb6e2662e54a1194633a79d98a50a876addacfce7ba5b34b09f", size = 259986, upload-time = "2026-08-12T14:32:57.661Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6a/5b964a1eb0f9075ecd45083eeb21aaec215334f98bac3d400302ea73875d/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b08ebf9488c7ff5eff038e48e6ea938178dfd9dcc8598b5ca941e4ae27b20be", size = 249853, upload-time = "2026-08-12T14:32:59.123Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b4/aee3a9d82edd0e931091ef3e9f03e46491ae3590e96e998d0975dadbe17c/charset_normalizer-3.5.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95450fce59f00c6d08eff6572ec2e736e5054c9450253afd5748f8416f2eb9", size = 244217, upload-time = "2026-08-12T14:33:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/22/3e/33f72ca11c1b619b220fd9f35905ebd171cbd0e0470f2357e467b9e861ee/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5780a29823e1d2bec69b7a104ead4195a43f3e97782efaedbf1f79a0157af715", size = 241307, upload-time = "2026-08-12T14:33:01.589Z" }, + { url = "https://files.pythonhosted.org/packages/78/27/6029dccba958621c7f3a65136f87c5512d712aef9e890f09512cc171bd03/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:054420b5db984971d886e5e4e2c37c760ae6682aedbd066687ff0949d9ed5f08", size = 233305, upload-time = "2026-08-12T14:33:02.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/d7/691c967be459153fe9faf49bf78bc95639ef8bf6dd008f38cc6389a349eb/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d016dc857136c726958102c3b8a3986acdc65ace6fbf12cfdc09cc4bfa2935b2", size = 263465, upload-time = "2026-08-12T14:33:04.166Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2d/9202221be5c90b2a835924191e362690ed8dc8c7d6606100c2bd03fe0f8c/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f2ce3d39fb4a9d674e6639dd5d3146b2e273475d2260f10163228d66fc04433d", size = 245060, upload-time = "2026-08-12T14:33:05.325Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9a/298772fd0a0cbccadf36451a1cd7eef4b66a11e99b4a7f6fafc47cc62c75/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a5613a3a82c974227bde18f03409e30c467f8065cb56d822e3eb83708a5f223d", size = 261091, upload-time = "2026-08-12T14:33:06.475Z" }, + { url = "https://files.pythonhosted.org/packages/82/3b/1a11fe66e555dbe2f5714ade6ba74fa29edc9155d9cf1001d4d6ed096aa7/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fded2e82ff082e5d8e017e2ddcc1411bd8cb83b8585097fc401ef574f756b888", size = 251857, upload-time = "2026-08-12T14:33:07.784Z" }, + { url = "https://files.pythonhosted.org/packages/17/fc/73b817e8af3f1d25ec5cf458d405abba5a144cf9812238a61530f5eac186/charset_normalizer-3.5.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:8f006866047c6ec4b627ec144b1e0bbc7427cb31fd7c08d19897d0ac9032af3d", size = 139745, upload-time = "2026-08-12T14:33:09.123Z" }, + { url = "https://files.pythonhosted.org/packages/b3/fb/ddb66303c86f7dc5043a457dad9fa82b4d6d0cb97094f9bcdde21693fd58/charset_normalizer-3.5.0-cp313-cp313-win32.whl", hash = "sha256:196e270c4e80827b5072eed7d6aa661d133afada94fe366669f9609e718d305e", size = 177217, upload-time = "2026-08-12T14:33:10.305Z" }, + { url = "https://files.pythonhosted.org/packages/fb/88/6018cc8d76ea2b7cb02918f37e23e86c261d1a102713d7e88d2cfb8b211c/charset_normalizer-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:72982d9958a42f8132bf2d6b90214ed66477295ef1188731f98ae3511c6eeb5a", size = 198896, upload-time = "2026-08-12T14:33:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/20/2e/04c0bbfc8d9abf91959f7a3d207d45cbf63a8116984caae2381890019bb5/charset_normalizer-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:0b373bab0b867b68b8eb249da9478cab9181a42993437cd2f5dba5fb0b4fbd1b", size = 179193, upload-time = "2026-08-12T14:33:12.726Z" }, + { url = "https://files.pythonhosted.org/packages/43/14/d098868dac5ff27e0258f548b1c74c6484be528384965d8fcf8fc6a4011d/charset_normalizer-3.5.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d95244906ed69d0f79f190893c65e336c15959003e21449256dc05c001b52ea2", size = 211664, upload-time = "2026-08-12T14:33:14.153Z" }, + { url = "https://files.pythonhosted.org/packages/e7/da/a944b32a46601ae5a4c3499e8d64ecd14fe82313f00da74dcdf00273a0b4/charset_normalizer-3.5.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d788e2ded0c4c47efa4d73cfe59eaf975ee32f425219873d2cb3e3fbaa00f636", size = 224375, upload-time = "2026-08-12T14:33:15.472Z" }, + { url = "https://files.pythonhosted.org/packages/f7/db/eabb5996be2f529744755e7b2fc9396eff4a64961f034e7fd49d54b9afb2/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:f9f91d3e8382900f3a68fa0ce94294479de9cd2de6bc0c70acd0f0dfd511836b", size = 194364, upload-time = "2026-08-12T14:33:16.607Z" }, + { url = "https://files.pythonhosted.org/packages/78/65/4ad3c5be108930310d8003f5602861d5b89f728293b9f09c3a4837f7ba10/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d54625cbf4e6b60bf0639728cb8b4cb541e340f6d7cafae5806051a40ddf4c45", size = 197643, upload-time = "2026-08-12T14:33:17.88Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8fee3201b98d52289be60a775797d69be05a04fb6cfb48c1587dad33e649/charset_normalizer-3.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:1f99a8c3a1da5d955edbad18208b3d627bdd54c48a6e739fa877bdca98c686d6", size = 341384, upload-time = "2026-08-12T14:33:19.239Z" }, + { url = "https://files.pythonhosted.org/packages/a7/dd/9e757101d1f76c35c0643684ba499ac3a181fb2b264c68174bf727d627e8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf1e75dc07a3850b53d1e5f75e04d3ae12afe56284be7821771eaa2466350c73", size = 241637, upload-time = "2026-08-12T14:33:20.619Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/7857023015400bc4aa0a82fbcca29fa2dc7ec25f971a130764cb2dc7a589/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ac5a9cc079c67d75f4ddf343276031879eadbb333d1bb231cce297b8d7b9aae8", size = 226170, upload-time = "2026-08-12T14:33:21.773Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ae/8b52935b304f7b6bbf33151ed2b75266b09aa4b6f8f04230d948885b2577/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0dfe83c1b4d00abbf433998117a14f56a5c2bc68226c0d331709eed0d1ce539b", size = 265093, upload-time = "2026-08-12T14:33:22.999Z" }, + { url = "https://files.pythonhosted.org/packages/dc/78/6e838f6bb059f2c0afc60a4e7f294252f043c254656ad4114c50302cae4d/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e8fa586df2208ef040684751345f10f503834a757c9a74ecd19c1a2f9b1ccd", size = 262789, upload-time = "2026-08-12T14:33:24.214Z" }, + { url = "https://files.pythonhosted.org/packages/c2/08/189b27e51fddc9d6b3695331da0e31792c1d88b953ad854e57f06e9b2cc8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82cc5835997ec78afe293a192e385099355770a7db94b2fb1239d36b32796f1c", size = 250580, upload-time = "2026-08-12T14:33:25.707Z" }, + { url = "https://files.pythonhosted.org/packages/ac/55/64854e99b25841f83e8e37d9df2f3d1f96f693439f80e5fabd542a7e47ab/charset_normalizer-3.5.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:19e52bda45086df8a4be4bb5910af6f5d9d3b538c78712c8ae09ef10b85bf458", size = 245008, upload-time = "2026-08-12T14:33:26.971Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/7720c904fa635d4260b4dced6029cf3d298c57b26741365d5a8d28c54043/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3418edd0ecb72a0a3861cf72f31be0ad9b7fe338ce2b58fb5cc80b9aeb792700", size = 243892, upload-time = "2026-08-12T14:33:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/70/50/7bfcb327631d4870c720872b548745f6ec8baa044d51c21b5d1d32ac4e3a/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:125ee619611019471b177c70bc3e9d4cda9fad7e01d93523501d3b188df0193a", size = 230996, upload-time = "2026-08-12T14:33:29.511Z" }, + { url = "https://files.pythonhosted.org/packages/24/51/40c45d6d940c04005ed721aa54bdebf1ebb2930f8a2ae537e8d60484fb27/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a3ad0e3da22852533858663848608f3f24c0d35e5cde415a4903476f2b4c88ec", size = 265834, upload-time = "2026-08-12T14:33:30.689Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d4/ef7a227ef89d215b47f9df79c3966610b17faa13bb2f236989207a631622/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:4ebebb410bc517e1d284c52a123e82704b21e4e7e26a21ebecf7439d0647b8a3", size = 245544, upload-time = "2026-08-12T14:33:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/37/a9/a4ca9156964ded61c7718eba410ce11be2fd2b263fda4bcf08367b6578cd/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:91f9f7c151e772acebe489eaec96e96a2877202d7dd144e3f96b8676881715a0", size = 264110, upload-time = "2026-08-12T14:33:33.13Z" }, + { url = "https://files.pythonhosted.org/packages/38/6a/838364bb8702229c6e5f8b23f80ff0f052a12dfaf3113a12fd6acbe92a44/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:401ea6e7af9e7852ed818f64714b579c1935482049670847ca3bd7ba45dc63fb", size = 252303, upload-time = "2026-08-12T14:33:34.98Z" }, + { url = "https://files.pythonhosted.org/packages/9c/5f/d88032edce951f499a2321cf7ae0d35a043c74be12bc22d81084cc7afbcc/charset_normalizer-3.5.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f7496aed56b06325a1ad419c5bf23c6dd042558e874f71dd1b958f3e255f3053", size = 139964, upload-time = "2026-08-12T14:33:36.195Z" }, + { url = "https://files.pythonhosted.org/packages/37/ae/1c4a46b6b00d1c34d2ee355ef99ad6173674166800d1af0f05f85028d513/charset_normalizer-3.5.0-cp314-cp314-win32.whl", hash = "sha256:606a86c1c3196f3738de39a67a7490bbd61cb31c0e0436070bd0c6a48170b38e", size = 179790, upload-time = "2026-08-12T14:33:37.356Z" }, + { url = "https://files.pythonhosted.org/packages/01/51/f94dcf34fa8eba48c1fb89b6490a5f1426e19488fe5f38aac6c648c99057/charset_normalizer-3.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:ec6c464cf45867f66a2273e2214d9199a8fbad5cb95ca0fd45f6a2fe1d9d2cf4", size = 203723, upload-time = "2026-08-12T14:33:38.639Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ba/91d386870b5d9e4b0d8c4034f63877cc2e47b99c81ef05f3e6d42bf9a53f/charset_normalizer-3.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:dc28949de1bb5f7f30a46f15d74ce7ac5aaa63e03c5de04d68f571c7423af834", size = 183423, upload-time = "2026-08-12T14:33:39.899Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c9/534ecb17b7fb95f9052c4a44cf316316a27d4a8f73e8475ff55e778dcdd7/charset_normalizer-3.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:68b7e84ae8239a94f8d2c8f3f3a3a81bcde54805ec8f42a34de927d155688ec6", size = 368967, upload-time = "2026-08-12T14:33:41.093Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/ad7c3242d7fe55cd55126c22c65cb1b49779782cdf8932fd01d12232d86a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58ca5dc0a0ef99f2801ec0574214c978e9574055bc783830bbb6e7433218609f", size = 239478, upload-time = "2026-08-12T14:33:42.428Z" }, + { url = "https://files.pythonhosted.org/packages/7e/62/77f0b850048e430fc350ec58876b0c020f5c8d0d3956fd1a4d6ae2fa292f/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6c57af4084c10cb3286688d65e4c654190ff5edcbc2411d08cdca0a8a44c59a1", size = 227036, upload-time = "2026-08-12T14:33:43.635Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ba/47d951e1a51dddbaad0a1410baf49fb1d897ceb00281568f1183b79bce9a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1619a3cc174a7e3963dd34348e6fceb6e50db0ddeb0031bd7c73a58286454fa", size = 260772, upload-time = "2026-08-12T14:33:44.96Z" }, + { url = "https://files.pythonhosted.org/packages/b0/61/8c7ff4c81b2a88271126acf4b83ab3e31f6d63868b0f01d331eaa0f9cb67/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1328cc57dd4372be1265f68232cee890e087416e3e6e93e6ffb32c2bad4d36a4", size = 259273, upload-time = "2026-08-12T14:33:46.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fd/36129689be08dc287b951306946657ff70d76e287dd57018861f86d0e474/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06f4fb62a9139bef056b8b2da6773c94c2f259f90e4b8e53b166f3d0372d7cf6", size = 248086, upload-time = "2026-08-12T14:33:47.54Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f8/bcae67f994c8fd31dda445e5ebf84045823c31443fe46f0e9ee6aca99aa0/charset_normalizer-3.5.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:478650a70a750d75d5add401606c77f77069c32e4ba2c9131dc6cee566962ca0", size = 242671, upload-time = "2026-08-12T14:33:48.746Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/0472cdad1061c2f0e4d3aee29973eb6e81bb8fe256ff2860cf115b15f1c9/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48920bf6fe83eb2226756ac623fa54940487154eb18f80889d5735cf234965c0", size = 241311, upload-time = "2026-08-12T14:33:50.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/89/04a03de5d27c77c624d9fcf6287073754bd438df1b58cb7d030c57c2824d/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:168a0cb536b5123a77bc42ecf5e0bf6f923d0d9ae43c42a14eb0677c19ac6c19", size = 229898, upload-time = "2026-08-12T14:33:51.523Z" }, + { url = "https://files.pythonhosted.org/packages/42/a2/639c4278adcb7ed1f4db608dd9ac19b6774fa2285a96b1c0bdb9c124ccbd/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f278e131afa96a3622cef9211c406ea2ad1b68eb06f8837cd443684a40e0ae50", size = 262852, upload-time = "2026-08-12T14:33:52.924Z" }, + { url = "https://files.pythonhosted.org/packages/9d/95/02e34c97bedfd0c5574efb9179c850591acc7f967ba039ed8dd29d332b73/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d6100f877d2ed95f0856a3fde25334153add94bf2224c43f45f88e7039262aaa", size = 242913, upload-time = "2026-08-12T14:33:54.18Z" }, + { url = "https://files.pythonhosted.org/packages/a3/64/0946aeab6462dad9f160a50dfb4704d3f58a5ee708f085abc2105fbbff0c/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4c440122e1ea68b1f8b44a631ebf49c39180f6869b1da22d76e8a724208ec6e9", size = 257938, upload-time = "2026-08-12T14:33:55.802Z" }, + { url = "https://files.pythonhosted.org/packages/79/77/36787d41ead124746506a4425c729f4f17c68280af8a6a5baa0a598cae86/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e5f834965c2fe589837bac1002e07e25734ff70381903ccd95b3d649e22bfa40", size = 249467, upload-time = "2026-08-12T14:33:57.081Z" }, + { url = "https://files.pythonhosted.org/packages/65/10/d9f6c5589cd24198d4ce6cd2948191c18e657272f433e5a00d258d9f5c22/charset_normalizer-3.5.0-cp314-cp314t-win32.whl", hash = "sha256:076cf9d3f3c7e410295c09d96355cf3b1bcae74990034d80e4371e20fe1ba4c6", size = 190624, upload-time = "2026-08-12T14:33:58.449Z" }, + { url = "https://files.pythonhosted.org/packages/6c/81/43e0584a802051a22c725795ebe1df78263abc7de858eef6cdc9b36637e9/charset_normalizer-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3288a560dc3114d5d2ebe309b1ef43f8af355eafe25856832415c2a8196c9db3", size = 215902, upload-time = "2026-08-12T14:33:59.753Z" }, + { url = "https://files.pythonhosted.org/packages/30/f3/af6a1160fef0eac4510d035241e11eccf78e5350e4cd4de79e79fe02a5e5/charset_normalizer-3.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a284c36b9c6616bf0a8aa4aabba668a0c75ba65ccf40a79868aeaa69ad996897", size = 193452, upload-time = "2026-08-12T14:34:01.017Z" }, + { url = "https://files.pythonhosted.org/packages/42/a4/dee470afb7a55c4f78b6fef37306c51fed17ebf94dbe530798c91d394350/charset_normalizer-3.5.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:c38d1e9bc2073b0984d2099ea647fd7f6c0d8f83a1e14e0cd32926f16e4c44ce", size = 341595, upload-time = "2026-08-12T14:34:02.4Z" }, + { url = "https://files.pythonhosted.org/packages/6a/32/9c3126dc429c6d9d7f79c52681a7c4453ed20a26267c9a8275d7ab620aba/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9f45186390aee4d1f26f723c615b67df346766c3b16df000d84d6e374f06757", size = 242177, upload-time = "2026-08-12T14:34:03.741Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9d/5b616a887301ff4cc0916b39ba44257390d3da80deeed6e8b6f2f26b14a8/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f0fde5e5100c735b2274ab898f0742a5dcde492796296cfbe7e0ad6a4cd1a396", size = 236730, upload-time = "2026-08-12T14:34:04.991Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/d6d3e70be93ebe5fabef65e4c7ac113e1d1705cbaeb5fb72467e713aca17/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d00e18e7bbf47e332ab63903d18bae31efc701b1d8cca0382b97784a621fc44", size = 265158, upload-time = "2026-08-12T14:34:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/3f1fafa87e2643257474f9c4eec609f2193a61d907dce7dd4f3f2390ebd5/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b81980668800dd1c69faad8aea6e85a8cee0e13bcd3bba7671695ff16260293", size = 262931, upload-time = "2026-08-12T14:34:07.511Z" }, + { url = "https://files.pythonhosted.org/packages/0a/df/ebeb224a949d91829e5e114c6b64372a3c792b00762a9e951ce416f3a32d/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb3e0d1345b9c0fe73673ea656375f38a78ec679c2edeae0c24800f04798a85", size = 251388, upload-time = "2026-08-12T14:34:08.949Z" }, + { url = "https://files.pythonhosted.org/packages/a0/64/9a6ce2e7acc5cf1b4636f78f82e89ff581e06a0216a40678b28bd4d832c4/charset_normalizer-3.5.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2401f7671242e921e604f609d429f6b282ea4ca787a6ffd22ed7372011ddb9d1", size = 251821, upload-time = "2026-08-12T14:34:10.138Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b1/6e69b8056f615e5ccff6b91ca16db2d47922251f016821a300c115267fef/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:96720f2aeed3434bc48f4d52fbad64ecc820cfed88915d664780ed9ba09ede78", size = 244507, upload-time = "2026-08-12T14:34:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/0f/34/02c15d6a0aa6b934dcdc136b111da63ae857b9fd51cf5505b0736337c2eb/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:c455829625df983f716cbaecbba77f2d1dc2e0e0ed1638c059cece15a279344b", size = 240951, upload-time = "2026-08-12T14:34:12.991Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/0fe893d3e1c7d111280bd6c4bd4c1e431487a1124a1bcbce78dfeda3a3a8/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:c41b067eddcfa5ee6b1169c287605be7fb6b0ea22bba6474c5bb978a668def4f", size = 266162, upload-time = "2026-08-12T14:34:14.232Z" }, + { url = "https://files.pythonhosted.org/packages/55/ea/eca03527307670f5d102c295671a800c404ca958cf94fefd10fc963a72f0/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:e31786a947b136329bfdc458c82c06d4ec539b4a4436b7da4df4aafc9902ee80", size = 251835, upload-time = "2026-08-12T14:34:15.48Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/fee5633081e595fe9e191df6f215106c791ad596eddf5e41e39b8ea0f2e2/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:c5c6d47a865147e0ae3322ce92e7fb52ba3169d94b447deda56897ea2aa6fac9", size = 264314, upload-time = "2026-08-12T14:34:16.679Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fb/17f47ae6ca35b562fb6e6f4b05f7aec6034217353eb4a23aaa3566dc7340/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c75191e3c8052045179646cb40e280800a4e0bdfda34d9c949c2f268d44e80e4", size = 253194, upload-time = "2026-08-12T14:34:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/59/88/f2b0f7ebb92493e925889ff29239b3b0073ffafd91230dbfc69e5cf9389c/charset_normalizer-3.5.0-cp315-cp315-win32.whl", hash = "sha256:83b62410bd36bb1178a7d563e2ee0cf21eb1c980c912ab99c2c78f06227f1731", size = 179800, upload-time = "2026-08-12T14:34:19.409Z" }, + { url = "https://files.pythonhosted.org/packages/e2/f0/afb5bfdea52fd943b1960403847a276b8e900c6e4cd6a38752321b4eda64/charset_normalizer-3.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:e3b9eaa99a6d8c9ace4cd303915947ef55088d4cd87c6676874f98c5c03aa040", size = 203726, upload-time = "2026-08-12T14:34:20.656Z" }, + { url = "https://files.pythonhosted.org/packages/fc/71/219783eb691aa2ec879c0e521afdfe2b826f9678eed51b9c039d03e0db2b/charset_normalizer-3.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:fec352b793cdc183cc9e7e0b6c10fd7bff38ec54ba44cc43599b9b56f7f3db2e", size = 183428, upload-time = "2026-08-12T14:34:21.975Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d0/14aef3b9f80f2593c039d897e89034635b9eb0eb44b6ce5173bbd79ff338/charset_normalizer-3.5.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:c9bde7a960720c8b8e1b5ef7afaa0c9a2f3b55c44abd635b2b29dd066b298e3a", size = 368728, upload-time = "2026-08-12T14:34:23.221Z" }, + { url = "https://files.pythonhosted.org/packages/10/fc/b249466ddbbeffa448b6597631e9091d1f01b5132ff8e7a0e21a6eb72b63/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8cd1283a9fe6c2065c807e9d5da81afe5e1e004caef39adc0d8ae86dd883698", size = 240925, upload-time = "2026-08-12T14:34:24.504Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b9/c17e72aaa1b3e1ca6c184e8025cf138ed492d01a54f85286ff7d31253a4b/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1c010dd86d3f4c4433c9634d33ce8147393b270dfa54f217f965540b8ae8e075", size = 234932, upload-time = "2026-08-12T14:34:25.822Z" }, + { url = "https://files.pythonhosted.org/packages/18/d7/f84ef0966bbe216f71029e34e7fa425a16b1682e2a40265e679dedf2b655/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5e68229977b2dea28e7061c0c0630a23f2f9f6e9c6fb38d77d3d6dbfe3768b74", size = 261733, upload-time = "2026-08-12T14:34:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/3b/73/3e887fa0781a395339355ed934ab6561ceb5bb52574160f070224039c630/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a60773eb5fda796e6e6f76b9c152d270fe59f9788a51a6ff8ba44082d8548ae4", size = 258460, upload-time = "2026-08-12T14:34:28.431Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/52ebd9849bf9e35d0b21fff115cb6543162a8e1f2f564e8f87121a336b8c/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9419f44e568f7fafcdc0b3b5c766a2364e705a9b34fb8a56b431e0d1f3f4258", size = 249894, upload-time = "2026-08-12T14:34:29.698Z" }, + { url = "https://files.pythonhosted.org/packages/85/f3/9366492b8a5fe0187de282e001d61345740cf79eb4a5f20181d769be02b5/charset_normalizer-3.5.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75e243abbb528c1a774390ed71e3f868a9f37b1373442e4bbadd401cfc505ff4", size = 249540, upload-time = "2026-08-12T14:34:30.96Z" }, + { url = "https://files.pythonhosted.org/packages/0b/af/28bb5e5dbd3e67cb9196a62781ac2b6d79492f4fc7a069b6ca7d6d6c8d58/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:54c963ce6404e52255b737e8a06d356fc762d59096ae566203a67cf2b7d050f2", size = 242734, upload-time = "2026-08-12T14:34:32.481Z" }, + { url = "https://files.pythonhosted.org/packages/26/d6/7ccfa62b53b40fc06b2d3504825aa400764740bd10cf248fdc4272441b93/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:63ea0cc840c66670183578c2630d138c0e944aeadfc33f25173ee240f5db780d", size = 239580, upload-time = "2026-08-12T14:34:33.916Z" }, + { url = "https://files.pythonhosted.org/packages/0b/82/71c0c9b046697b8da66b3acefa8d5f92d00a9ef433ad7c3522b971d0369a/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6c06875a1d4a7537bef70f659b55c6b55b9a47ec3ba8f2db610350c2d9915e6e", size = 263281, upload-time = "2026-08-12T14:34:35.333Z" }, + { url = "https://files.pythonhosted.org/packages/d6/01/d027583c869f40ba980c1c76994adbd522c360a6327e72beb44d7c267385/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:4253da1b4456b633651a8d59eb1dc7a8a8fa38241014dd7c217b353e547ae394", size = 250027, upload-time = "2026-08-12T14:34:36.991Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e9/6475d739e0ec8bb1236e06263dc3affaffdf947d8114ad27024932f325da/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:f044cb1cf44012184715f46584658993b5fee9344d71c4b0c455a17a299730c0", size = 257547, upload-time = "2026-08-12T14:34:38.277Z" }, + { url = "https://files.pythonhosted.org/packages/a5/60/d1f502fcaa048a2aca3ab80bfef8407659c131e4f1792fa805fec14b4960/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a17864853f7c518ae7d4b368af98f427f9396805476af40af8698560f09d7d97", size = 251718, upload-time = "2026-08-12T14:34:39.562Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/76343dcf4381a698807ff8a20d89f66bbdd9f6222b0b17740f77ab764335/charset_normalizer-3.5.0-cp315-cp315t-win32.whl", hash = "sha256:9e726478d7a213847860219d74665a6892a643ac93b8f76580f6cf9ed39996b7", size = 190757, upload-time = "2026-08-12T14:34:41.053Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ea/d18147626a1667cc773c42104ab155a4ca5d6d4d174b7a35e01062213ea5/charset_normalizer-3.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:d7229a99120c6c2792d96f4857c2648ce5530e93667a2c2388c5ef69a6b84775", size = 215431, upload-time = "2026-08-12T14:34:42.501Z" }, + { url = "https://files.pythonhosted.org/packages/47/21/4869598aae0872d94faa5933918a4fe37ab2c5af9d095786e241f9506fed/charset_normalizer-3.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:527e28a5e751d9e11369b9c5f9ab35c748eb9c109101920c7deb40d6eadf8d03", size = 193205, upload-time = "2026-08-12T14:34:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f3/7b523d807cb5e73562ef8acf21d39cdb9d704955327362c781bc3478a73d/charset_normalizer-3.5.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:5a4ee37248dfac25107c758bda99d545ce73e60b44d2dd39e4a2bb9f2831e9f5", size = 330840, upload-time = "2026-08-12T14:34:45.06Z" }, + { url = "https://files.pythonhosted.org/packages/f0/de/fc68978fe78ca97063c96d764e41ff92ca639948f319271e0ff450e577a2/charset_normalizer-3.5.0-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a864bdcacd8bff58bb4845304e031f821a3ec64b2b7259f2d409cd49c9e59ca3", size = 251862, upload-time = "2026-08-12T14:34:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/a9/cb/82b41a0ab7fb1a88065f1d78ad32696ad88ea3fe8e25b8189d08833938de/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:84b736e3b391601bc47b86da381c749c0f894e9191aaca9f31f30c2632206df3", size = 239484, upload-time = "2026-08-12T14:34:47.869Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0c/19608b631f4538f908098d4a2d56a8f79a665e27cc58e9d90479761a9227/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6abb1f356fb865baeb6ebc3fadd843e9a96fbf49b9adcca55037f3cceccb7438", size = 230602, upload-time = "2026-08-12T14:34:49.265Z" }, + { url = "https://files.pythonhosted.org/packages/29/db/f648eb30e14eba301aed61e11672156f137905c1bdbb530151abe8065943/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d366548d2ee28a8cfdcc4296363978cc644a728333be9824d2de4652e83df0a", size = 259208, upload-time = "2026-08-12T14:34:50.632Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e0/ed2c8bdbac484d69614d6993143aeb6cb0f4dd1561c883402517b623c8ef/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d672f329ae504ee240eb39b6effb3318aa8e7e8924c0ce8eee5760b3fad98539", size = 253659, upload-time = "2026-08-12T14:34:52.11Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/b4907cb9ec5b521d9d024ced13611240b86ef065c2eb15b3ad2334dc9940/charset_normalizer-3.5.0-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c54036a518748b6c02e666f6d46c3817561998fb904c3be25b56fb4fe3dc5706", size = 248821, upload-time = "2026-08-12T14:34:53.399Z" }, + { url = "https://files.pythonhosted.org/packages/12/b2/e2d1abcfbc05822f0030869efb4e9f8a3658e13b4821796d4b62da917327/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:22a1889f1c9b752c63c36758a0c2145458e3cadb20fced7a0790002e9dd12b26", size = 240271, upload-time = "2026-08-12T14:34:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f9/4ba127ad610542fa3eabfa41c45bf12d357860a815b3566374ec0188e213/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b7eb3eab5c646d3de7dcb14a7c9caebace5249c5767da39e1761cb1576e521a3", size = 232155, upload-time = "2026-08-12T14:34:56.543Z" }, + { url = "https://files.pythonhosted.org/packages/01/68/40613182366d00bd6dbd5f6c84a926cbd120960e038a8269e9ae7d782762/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:2080aa129a28267984cdc902898993d788c995c384e285d0d19199f56760d52e", size = 259674, upload-time = "2026-08-12T14:34:57.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/53/4574a14fa4c9de4a6c9f31725354bfa40b67f653e6d594ce1654f9a41b32/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:fd68c825548a611158230e2f9222e210ceb2e3391995c0aa5865cbdf3ab4bd49", size = 246122, upload-time = "2026-08-12T14:34:59.337Z" }, + { url = "https://files.pythonhosted.org/packages/5a/02/bd8030d13d92c058ca7b2b9615bbb3169569e144db64d65c149cd45abf5e/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:d8a9316f4da85e937242642b537c6d55d7e9287dd38e5634732f8233932aff45", size = 255221, upload-time = "2026-08-12T14:35:00.71Z" }, + { url = "https://files.pythonhosted.org/packages/77/9d/10ecd3bcbe2666b3d4d4026c97b48f73990682815db516052a1e8f4a31c5/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d90254c8f609338c53ec180fcd4c4f9c16502e238e3fc88ca7fd4c2f38d445b8", size = 253450, upload-time = "2026-08-12T14:35:02.217Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0f/d044c4872c0938a84f87b5027a698c0e61bacfc5c3551a4e749ca9b7bc5c/charset_normalizer-3.5.0-cp37-abi3-win32.whl", hash = "sha256:8b3e9e29b8b07cc461b9ce7768db7693a93979d0dadf22046f6f3555ded2f516", size = 173594, upload-time = "2026-08-12T14:35:03.845Z" }, + { url = "https://files.pythonhosted.org/packages/10/6b/6046773901f1944b9a89436351529811ee958afc7b774563be9d74a6f0c3/charset_normalizer-3.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:0c8953d9d1617794cfc40d81179571c9ba3805dd029623a15c93f1fb70e60a74", size = 198959, upload-time = "2026-08-12T14:35:05.187Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a6/b57708ac92aefc8e8389d51d5178129b81f03196da61ee2c23e687b8178a/charset_normalizer-3.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:562d24ca7797c1af8852994950c2e623a907b201fc4b0ed29e92af173d3828ca", size = 267055, upload-time = "2026-08-12T14:35:06.533Z" }, + { url = "https://files.pythonhosted.org/packages/22/c7/754d09943a616937df61e4ba367c409ded2a987e872972098d51a6fcf73b/charset_normalizer-3.5.0-py3-none-any.whl", hash = "sha256:993dfcbe75a85a3784abb5084f2c41b915767c90546fcc92803cffa28611baea", size = 67943, upload-time = "2026-08-12T14:35:30.363Z" }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.15.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/c3/4f2195f512fb172aa425a8803a874b2baa9ba7f80ff7b6080998761fc701/coverage-7.15.4.tar.gz", hash = "sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00", size = 936952, upload-time = "2026-08-06T13:50:24.442Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/70/b052a519a584663a7bd052841a2debe11c8309ec49a7786340003f9c0a02/coverage-7.15.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d0be6daac4cce6b8c8dc65886bae1b082ddbca4da8e5cbb5e15166acf253e264", size = 222245, upload-time = "2026-08-06T13:46:55.253Z" }, + { url = "https://files.pythonhosted.org/packages/67/39/892fa511aba3d1c3c8f49509a0ff5c71eab9f9f88d08e1a38da395821660/coverage-7.15.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b24e078eabcd6a9caa8b0713f9bc1eeb310bcc960a29d45a3b4fcd4b16d5b11d", size = 222762, upload-time = "2026-08-06T13:46:57.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/95/b2c724ce1e64bc23cb5b1d7eeffa9548dc3d811f7a6297b2d01607f4e062/coverage-7.15.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe20cc8cf8821d4fe54f89106cbf06aa27f37b5bbe3535568065a81539b4150", size = 249498, upload-time = "2026-08-06T13:46:59.012Z" }, + { url = "https://files.pythonhosted.org/packages/0b/4f/b1973f67a1382af65b572a31ed692f8e490a6ad707191eab59148376832a/coverage-7.15.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:83cf06cdd687677742caff1a9134833b7a8b75f111519d2cb0e0ba1b9a851e15", size = 251328, upload-time = "2026-08-06T13:47:00.764Z" }, + { url = "https://files.pythonhosted.org/packages/a2/09/03efa6722a132abcac91b32a60b64b240dd707c189c64eee697e48992c96/coverage-7.15.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8fa4de68e2a752468ff14b4e15db7def689a71be759e826a31ccecbef69c5fd0", size = 253194, upload-time = "2026-08-06T13:47:01.976Z" }, + { url = "https://files.pythonhosted.org/packages/45/63/8299201d9c80fb65551ce99c966cab83d706ec4066ac999bef08201346de/coverage-7.15.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4dff9daa47d83120c3ec38ce921214242944a832aa04e903e50b5b7ebac8972d", size = 255106, upload-time = "2026-08-06T13:47:03.281Z" }, + { url = "https://files.pythonhosted.org/packages/ee/16/26fd8a691eb8d9a230128685f6d23309d7402cb030aa553001788c8c50fc/coverage-7.15.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a093fd37229918976f602aa07aa59e0973cde82186f220c8e197f721f5be0ce4", size = 250177, upload-time = "2026-08-06T13:47:04.713Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ef/3c7556f33783a0a566e01443ca62bd8eb2cdfe22d271efdc02e08beb5654/coverage-7.15.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:317db01a2cb02552fd67e2b1cca77a4b528a2a277176c5e0bf2cecbb639d3f54", size = 251234, upload-time = "2026-08-06T13:47:06.104Z" }, + { url = "https://files.pythonhosted.org/packages/29/49/640a34043edac950738f36a3567832db5731d4cb2ed84b59cdb89c6bccbf/coverage-7.15.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8ee3838dcb656602c3b51e16aed9bfb0822f8d8d6d1c5966d32ec8c104be8e20", size = 249237, upload-time = "2026-08-06T13:47:07.467Z" }, + { url = "https://files.pythonhosted.org/packages/48/f5/e80f212669dd1be954ff844f883ef11a437ef4fd0089c6e0effc7b66b15d/coverage-7.15.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:425920379052ff1fe465268f3361d35804a241bbdd5a1b592c8cb60df4c52325", size = 253050, upload-time = "2026-08-06T13:47:08.748Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e9/e5da0fe39f7fde1bca9edc09c60921bb5fdba4cec7db5bbad41ddfd8c230/coverage-7.15.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:69bb2400abef928e365ea7d4d9925169ada78ed2295546780002d4b65de3df88", size = 249508, upload-time = "2026-08-06T13:47:10.072Z" }, + { url = "https://files.pythonhosted.org/packages/7d/38/41bf25774a0c8bba6b467f917cb1c9a0a2605e02dc93aad489fc7050ed59/coverage-7.15.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:81661f82d302484e3119e7c80c519c02fa9bcc2a6b339baf67d67bc89c580f04", size = 250110, upload-time = "2026-08-06T13:47:11.35Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/26f2e54b79acc29d179ee4272922625aedb69198c4eb61f7ff4f098f3c78/coverage-7.15.4-cp310-cp310-win32.whl", hash = "sha256:cb476b2e828ecb71cb6b6a928d23fd20a7ddb501188022dae1c37499149cc338", size = 224294, upload-time = "2026-08-06T13:47:12.753Z" }, + { url = "https://files.pythonhosted.org/packages/7b/06/9a318fc3ae040d4d6cb2d86101c6aa963fab20899a5c58666adf52cde0ca/coverage-7.15.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fc2130bf37df31852a8384f12601563a45a0024bccc6624f38355cba7a8d360", size = 224919, upload-time = "2026-08-06T13:47:14.17Z" }, + { url = "https://files.pythonhosted.org/packages/2a/66/edcec7d7a0b524aa8923e22925fde6fe50ce005a113dca13ae1581455c4c/coverage-7.15.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbac5abad70df71019988f83f26ac7092ff2642975def4429e98dc7585ef3490", size = 222367, upload-time = "2026-08-06T13:47:15.578Z" }, + { url = "https://files.pythonhosted.org/packages/e6/c6/ab8de429e2e8548faf58ec7e1674a4ce00414b4113942d3fe87109cf0f68/coverage-7.15.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:357a173465c7ce028d07a95cc2b63b5bf59f50ecdd5ad75c5cbb78ada984048e", size = 222874, upload-time = "2026-08-06T13:47:16.961Z" }, + { url = "https://files.pythonhosted.org/packages/be/c4/3b7b49587e8a6b9af79b3eb468d443d6042b6d65b47aa26586846a0d6566/coverage-7.15.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:21b803935e2efc3acebe9697197a294fccf5dc4e5382bd6369542ff7a7d2a1d7", size = 253287, upload-time = "2026-08-06T13:47:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/fb/65/ec03b743a2a229c72cc1eff3e57be9d3564e9c6b4d5aba2d70744a3fc0d8/coverage-7.15.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7a2b580774a4786c1053157c0165e04476e03ff293993d7c148eee784a94bae6", size = 255199, upload-time = "2026-08-06T13:47:19.765Z" }, + { url = "https://files.pythonhosted.org/packages/41/4b/5163729e4b6582d61975cfd3ccab45b4ec53e21cf156d9941cb025188468/coverage-7.15.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9464451c4efffe8d47ace5a540b10b0dc10e879066290f8600872b7f54a419d", size = 257308, upload-time = "2026-08-06T13:47:21.206Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/2167a0f08fb87d702fa423a48578a32865464b7c9e1db3911ad7812ab414/coverage-7.15.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de602f34123c2f4af1c1869c6dbbbd60da6d5983bf01937367295d135cccbfce", size = 259268, upload-time = "2026-08-06T13:47:22.503Z" }, + { url = "https://files.pythonhosted.org/packages/1e/e5/68eebae3053dbd48508edea559c21b23fbdf3460784f91370c83a86a6acd/coverage-7.15.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6879ded16a27f3eeca19b900c147e81616e7054db451471a611b2755ee5249f7", size = 253392, upload-time = "2026-08-06T13:47:23.88Z" }, + { url = "https://files.pythonhosted.org/packages/1a/46/fd4ced40a2b691c774e515c9b69500bfa64c7960b67fcee4b2f6fad97fc3/coverage-7.15.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:986be58c3ab54aae8d3496a6225eea74f760fdbe739b38bd442c7e8d133aa53b", size = 255001, upload-time = "2026-08-06T13:47:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/53/25/ae2e5fa710bb6957a9aadeb9e3598d3b3e4af6587ce857ad42e8639a3f30/coverage-7.15.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c6103639613fe6c1e989082948419bc77a2d26b6c825c99d7fad25f7d3d87afc", size = 253061, upload-time = "2026-08-06T13:47:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/d7/31/67ddc0365db2c6e93ac8580bc4bbc50f65273262f973f63ebcdbc15c0495/coverage-7.15.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d3af93dddb5659276c63bc16ac6466ac2033a70ca816097bbc06345b8ccdf571", size = 256831, upload-time = "2026-08-06T13:47:28.217Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/82b8fd18f57fb13f12d98fe874995bb2c4f9f17be8aff762c426323fdb96/coverage-7.15.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b10075e5421d04265766a6d1dac809bbeb8a946fbb23c8f82c227409b2190719", size = 252781, upload-time = "2026-08-06T13:47:29.712Z" }, + { url = "https://files.pythonhosted.org/packages/0a/eb/6c74ef4dd12b252e573c49bdef9e2ac265bf3dbb79b8d7feb3266e084e9e/coverage-7.15.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a67a9f78b2942d87ba8ce3059c642164d2aedd65337377fb52fe9803656bc5c7", size = 253692, upload-time = "2026-08-06T13:47:31.192Z" }, + { url = "https://files.pythonhosted.org/packages/5a/66/eb9aed1c3fd2d36ee00eb173f434b14fa607fc056739c9a89ff4244010ea/coverage-7.15.4-cp311-cp311-win32.whl", hash = "sha256:69484d1aca26e322e1c3ce03f09341e84524ababad2d7202161738d83cc9f82e", size = 224461, upload-time = "2026-08-06T13:47:32.572Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6d/81fa4161dfb3ed9d74e40d58647eff83a56b7612e78352581280fce2f477/coverage-7.15.4-cp311-cp311-win_amd64.whl", hash = "sha256:63fd6fcd1dd6e158f7eb78606e72933b3f6d01e7b747f99c6c12d764307a0fdc", size = 224937, upload-time = "2026-08-06T13:47:34.205Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c1/d8dacf683c6cad3cf85ce68fd3774a6774ec402128822fdfaed920f11e6a/coverage-7.15.4-cp311-cp311-win_arm64.whl", hash = "sha256:ea82116c9893fa89e929b7f197ee5a1950a76e91cc5c85ba503fc02379d04890", size = 224479, upload-time = "2026-08-06T13:47:36.118Z" }, + { url = "https://files.pythonhosted.org/packages/1d/48/bc8d4ba7b37551a767bd863f15b3f80182b271c2f55975356f5f7dbe94c2/coverage-7.15.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4fedd1f7f428f9fe83b1ead5e7cc87a43427be31aadafbac3ac0636dc7abb22", size = 222543, upload-time = "2026-08-06T13:47:37.562Z" }, + { url = "https://files.pythonhosted.org/packages/20/dd/88d6f83f1fffc974a3691a34a97951c5b12df7512a6782c5963883cbc058/coverage-7.15.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37e2f0cdf58e2e1fed4e4d5a8f8786ae2f7eb80b478016876667dc4a01d60a97", size = 222905, upload-time = "2026-08-06T13:47:38.927Z" }, + { url = "https://files.pythonhosted.org/packages/bd/5c/54ee0d4748585bb0acab9891cd8d92f2d3593165b4e59fc9de113bfb3140/coverage-7.15.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb55d0e70bb15f2e81477613627286581414693d74ac7963c93a790dd453ca9d", size = 254407, upload-time = "2026-08-06T13:47:40.488Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3f/f0642a372f494bd0d7dad3b497083b910194a5f1c88be2c94fef707c3b59/coverage-7.15.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:899b9da30f3c6c336566e3707495bb23e8302d39d862f01fa78c48b99b9437e2", size = 257145, upload-time = "2026-08-06T13:47:41.931Z" }, + { url = "https://files.pythonhosted.org/packages/71/17/8b46d0ed68251016002ec972c8fc0119961a765d0984cafb8bf317c43758/coverage-7.15.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d15715e8c46552827e5e4f30a35575a2dbcad14454cf3284c54483946bd16931", size = 258257, upload-time = "2026-08-06T13:47:43.527Z" }, + { url = "https://files.pythonhosted.org/packages/30/b8/8498a0e72d0adbe15477dd07463d2b3bb2c9f6a4815e8589e50939e2c3ae/coverage-7.15.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:002a438859f7b430bc99afeaf01a6d187dad1d0dc907b64cdeffc632a5db8fd8", size = 260517, upload-time = "2026-08-06T13:47:45.121Z" }, + { url = "https://files.pythonhosted.org/packages/41/e1/7dce19c3bdb1e3dd63e769508216500edad81bd5f69a26d724e32aceaf78/coverage-7.15.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e4193a04b518f7968f3099755f5509ee7cccc6dc2b92a6b14841934d22e222c9", size = 254785, upload-time = "2026-08-06T13:47:46.541Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b1/e1494703c675a2561723cd9b89f45c9168782c31280c611b1f767851e57c/coverage-7.15.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e98dcc55d572b38e69d117da7e8e8efb8500f1f5eaf81ecd460a63220790b839", size = 256176, upload-time = "2026-08-06T13:47:48.155Z" }, + { url = "https://files.pythonhosted.org/packages/73/76/a5629d270fb638a43a4b10466f51e2f49d532c1aa4da2913cbbb150bbe0a/coverage-7.15.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:af6c538498ce66c10d3fd541c2a8d5b03da5850355add34e6cba564210cb9e72", size = 254321, upload-time = "2026-08-06T13:47:49.757Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4f/9c44447218435d5766b911534f9d798144a5560f85e9a54ebe5f3f5d19f9/coverage-7.15.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1d10025d96ea89fc2f73714dbc4cbd433fe012c1ac9e23f895d7728b238b6e52", size = 258390, upload-time = "2026-08-06T13:47:51.248Z" }, + { url = "https://files.pythonhosted.org/packages/de/36/c1e127616fb3fa18a9ff71e76c417f2fd7424332a4870015ac224ef4c039/coverage-7.15.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d802e1947603162ded419bff83ac7489820355d2b856dfb09206574e3a37ac0c", size = 253894, upload-time = "2026-08-06T13:47:52.816Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b9/fdb92c8ae7a8bb9b850cc253b7b3b9c8526f68130002048b5671cd510d09/coverage-7.15.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2de40895718f91951b86712b4c5b694acaf9a0a49be13874896f599a1eed3f4", size = 255763, upload-time = "2026-08-06T13:47:54.296Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/a7d51b2587c7bdb76e71b0896d2565bf7d60436b5122fc83e511adb1f7cd/coverage-7.15.4-cp312-cp312-win32.whl", hash = "sha256:5c3431b2161279b7db5c2a1aa58ae02e5cb8c3c42d93a5094be3f5537bd5b11b", size = 224597, upload-time = "2026-08-06T13:47:56.074Z" }, + { url = "https://files.pythonhosted.org/packages/49/b9/5c5f80cc55f5acaaca6dee677626bfcec8c87204a7809b438b08e84f4571/coverage-7.15.4-cp312-cp312-win_amd64.whl", hash = "sha256:6befeab5fb2b51c958ca4ac6c5d141a1e8240f4f76e46350f1911963deda49cd", size = 225135, upload-time = "2026-08-06T13:47:57.52Z" }, + { url = "https://files.pythonhosted.org/packages/47/e4/2a4561f89ff6bf7c925c287d0f2cce8bdf139c3a33735c87e3203401cf94/coverage-7.15.4-cp312-cp312-win_arm64.whl", hash = "sha256:67bc345491ab55b837277d76f5775d057e8c7f1ac44d890d8c2c82adde258c6f", size = 224515, upload-time = "2026-08-06T13:47:58.977Z" }, + { url = "https://files.pythonhosted.org/packages/f1/84/651a9310859673aaa3b3203f1aa1641ca60fcf2494683e1c9474c7172780/coverage-7.15.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c705b28feb2775dc82a25f1d473a370bc37ff93f5177f4e29ce2425f560f6921", size = 222565, upload-time = "2026-08-06T13:48:00.796Z" }, + { url = "https://files.pythonhosted.org/packages/82/f9/4dcf700137e8af550670f4d74d1b63828ce93e1e2b05e5f10710eb2ea987/coverage-7.15.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ff205ab5e3ecc670f6a4dd19d9cbf12ede53dd41cfc1e15716ec961ea6d314e", size = 222936, upload-time = "2026-08-06T13:48:02.391Z" }, + { url = "https://files.pythonhosted.org/packages/07/4a/612ff1e780b3fbfd637486f542f84adc5503873d8b5d279dec1ffeef9414/coverage-7.15.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5172326e861a38b48b48befca15e0f477a26b283337a33a739c8fed229934e36", size = 253926, upload-time = "2026-08-06T13:48:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/b0/04/d1cff1c2ead4708a6a79c01d3736b6a25bd38a36678398f72a8dd33dfad9/coverage-7.15.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:12b59c90084e3234fb11184886bf4a40f4f16a8c8f867be2e087b81f8e8868d4", size = 256523, upload-time = "2026-08-06T13:48:05.996Z" }, + { url = "https://files.pythonhosted.org/packages/b9/80/d34e13fb4b293cbdb9665838cf5522077b8ad14ef947550631a4bced36a5/coverage-7.15.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349062d66f00b40fa2c1c222438bad25fabf755631b5d82937fe985c8008615c", size = 257759, upload-time = "2026-08-06T13:48:08.036Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e7/2c5fe7636fdb0732fe0f09f308a5b066864078b7fc61f6678e8478554f2e/coverage-7.15.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4256ced708e598e05209bc1a8ab4074e04a51dba4c62fb45926a229af675ace7", size = 259890, upload-time = "2026-08-06T13:48:09.834Z" }, + { url = "https://files.pythonhosted.org/packages/92/28/9689f0858dfff59c2ea688938ab9fa2925631235df67126a42b6c5c70ae1/coverage-7.15.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d80f974b20782d9612c8b4c9beeca867074c7cf4079d1419843fa25a26428b25", size = 254121, upload-time = "2026-08-06T13:48:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/785077c230c157243eb5aa9a26c3be260ecd02001bead54a3cada3df8e03/coverage-7.15.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e179f19bfe1d31f8eeeaa12990194d761c4f62f0759661000bca6cd8729f40b", size = 255891, upload-time = "2026-08-06T13:48:13.209Z" }, + { url = "https://files.pythonhosted.org/packages/d4/90/e20371b17b40f912f21305c2db2f30efa3de306f7320fc916804872c85a4/coverage-7.15.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8bc16bb47b7679670eceff71d78bfb7d6e5b143f6c2cd117487ec7c75e0d4b78", size = 253859, upload-time = "2026-08-06T13:48:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/05/49/25371987ee459a5f67c0427fb75c74f9358e65f2c71fe75bf41c1b6c5fcb/coverage-7.15.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd685005cd2c4200adfc14cf39a603b9320efab3f18a8f7f156d20c9cc3345f", size = 258011, upload-time = "2026-08-06T13:48:16.464Z" }, + { url = "https://files.pythonhosted.org/packages/30/6e/32e67467f6154bf4f1c4f63b05acc5097cba4237d45bbeeea446b52e8ac1/coverage-7.15.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:337399ad2c93b3acd2a937627dae8b3e86b66707cd3d3e856347999aadf1ef8d", size = 253676, upload-time = "2026-08-06T13:48:18.493Z" }, + { url = "https://files.pythonhosted.org/packages/03/c1/8b24192e89286399765155251f99ee9f070a9d637109018ac23d99b99f6f/coverage-7.15.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96e257121228ec5cd2bb919276e94ac11074471bc37d68dbae0e8308cce15fff", size = 255453, upload-time = "2026-08-06T13:48:20.057Z" }, + { url = "https://files.pythonhosted.org/packages/16/6f/8b41ebdf67c87854e17c035336a90f1cfbad0c14c2a584301be6ff148718/coverage-7.15.4-cp313-cp313-win32.whl", hash = "sha256:c65a9e0dfc6143491879da4e13b5e30f8be192055de508d737fb14601edbd22c", size = 224605, upload-time = "2026-08-06T13:48:21.655Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e2/2946c7f0b42b152ecb21ff1bdad72e3d301e790c0c487e4a86e8c9f69347/coverage-7.15.4-cp313-cp313-win_amd64.whl", hash = "sha256:2ff8f5e9b8f7a94f0c11c45631eee103dbcb7d63274edd12c56efe1be690b3b4", size = 225148, upload-time = "2026-08-06T13:48:23.376Z" }, + { url = "https://files.pythonhosted.org/packages/9e/83/3f4a69957f48ae7a0aba76c34743f88963d607b19e03f3f8e66f91cae0f9/coverage-7.15.4-cp313-cp313-win_arm64.whl", hash = "sha256:6e0a8a5083b096487d6cfced94cdd514d8f5db6f113610fb36c0620edb1028cf", size = 224536, upload-time = "2026-08-06T13:48:25.117Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ac/748cf29eeb2d6be34a3176ce26a4f49e38085ee08e8935f05f6f26ed7e0f/coverage-7.15.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:770e9325ab5ea6d56f77e59b29ecfe0ac20b57a82a601876f90494a4dda0386f", size = 222608, upload-time = "2026-08-06T13:48:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/0b/02/1abbf5c984677b0aa439cdacaccbf38d248939d8ef8fe1cc7a50d73edb77/coverage-7.15.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d12b33a3a50a1676b7784dc8d00a0c6d66a9f2add4b85a041c19b6a7e53ef23c", size = 222940, upload-time = "2026-08-06T13:48:28.432Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e1/ff8f9f53d9fcf586125b55d0b1f04ec1c14955fee41e83d5814bee141bb5/coverage-7.15.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5669c8378ebde86f5def7a25d29586631b58acc27ffde04399f678f3dfc6e082", size = 253985, upload-time = "2026-08-06T13:48:29.995Z" }, + { url = "https://files.pythonhosted.org/packages/a1/26/595759762e514e81be1d7d01ed03444303bcd152226a6529998d253f9201/coverage-7.15.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff97a14362eef486483ed44042ca2027ea257df6ff768e62358ee0c9776925ac", size = 256492, upload-time = "2026-08-06T13:48:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/b79aabac54d482be23b5fcdd4f4662bff24a78edc4ee29201726929936d5/coverage-7.15.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a325e815318638aed1655d9c06e6d7c2d3d46c09231ce988070428a8762d734", size = 257837, upload-time = "2026-08-06T13:48:33.186Z" }, + { url = "https://files.pythonhosted.org/packages/09/0f/bf7f297885a5bf6fd71e5782404e0ff059ca09e8711ceb3a08544abde45a/coverage-7.15.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:474223409d88eb20d2d6a0d37ea60e8647a65a90cc008dc1f0410af5f64f1e0d", size = 260152, upload-time = "2026-08-06T13:48:34.75Z" }, + { url = "https://files.pythonhosted.org/packages/fd/f1/296744e854ff8368542343457414380465e9ceefb9192342feb9d3bc461d/coverage-7.15.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f2f62ae3cd189dd2e13aece758c57b3eecbd27be070dbd4cbd10936049e5dbf", size = 253978, upload-time = "2026-08-06T13:48:36.434Z" }, + { url = "https://files.pythonhosted.org/packages/55/b0/bbdb2e9057493e66220a2e149ca2d301ba0e3a58a83bd6b90de9826d16f3/coverage-7.15.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:39ece820e29e0a2ba34b3ecb3be83c27e997eed8926f2ba6fe7ce7a0bda5843b", size = 255846, upload-time = "2026-08-06T13:48:38.317Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/38015b2b6d21258713bd17e76b59d033b191efb5703589cffd037dfbca20/coverage-7.15.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f21b56dcace11dfe013014201f577dcd592b2a9b72182d930361b47cf6f73f25", size = 253808, upload-time = "2026-08-06T13:48:39.993Z" }, + { url = "https://files.pythonhosted.org/packages/0b/64/0d515c1e60ee6fbfd1a0e79c07cd87d388a233b7adc37758735677203808/coverage-7.15.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:93a3a0b662abcc10c73a47cbc72cd60f63618d6989fb2d1286e50eacd974f303", size = 258081, upload-time = "2026-08-06T13:48:41.971Z" }, + { url = "https://files.pythonhosted.org/packages/91/71/04d9e7a3642146c6351338aef4ef85ab11dbbb54744c13245caba1aad1c0/coverage-7.15.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:141fae2cabf5569b782c10afc4c850ce10f618c13f8db54765cba99cc839da1f", size = 253624, upload-time = "2026-08-06T13:48:43.731Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a7/6c28b74c81ebff66987b0e2522ba5cffa3e90b0c33cb6a2eb264d4ee8cf1/coverage-7.15.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:81294c7e6ab30c5f74c0353b11b2fd6320e72d9bee6ac73b357caa8b916323a5", size = 255280, upload-time = "2026-08-06T13:48:45.58Z" }, + { url = "https://files.pythonhosted.org/packages/52/af/bc19996a7014b98d7bbb0f0939453c67074af65784a3aa16a789a07381fa/coverage-7.15.4-cp314-cp314-win32.whl", hash = "sha256:7bbd7d6418e0dab31a206af5203bd43ae36edb8e7fba1940b055d3e9249290d7", size = 224768, upload-time = "2026-08-06T13:48:47.525Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/219484e476d6e101ba0a444852579e05f5b75c37c611a42ed1190f73ef62/coverage-7.15.4-cp314-cp314-win_amd64.whl", hash = "sha256:f0204ed122758782970526057093f448051a39db9d810d4e344bb87a3546f425", size = 225259, upload-time = "2026-08-06T13:48:49.513Z" }, + { url = "https://files.pythonhosted.org/packages/b7/66/fa77daf4e383e5f776dac62c2409b6af81910ae6fe326bd5170dba74cc63/coverage-7.15.4-cp314-cp314-win_arm64.whl", hash = "sha256:9e71e7bc71c686a123347ae47a0de33a175e797a85bb57b791492adf4eec8ed8", size = 224684, upload-time = "2026-08-06T13:48:51.235Z" }, + { url = "https://files.pythonhosted.org/packages/58/5b/f03bf0ce362bbf3f785fa5219620d00778d4ac6fc9e407734828e9c672f6/coverage-7.15.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7c922735321eef3f87c280a3d39afff6b646723a2880b862cda4ac7a093b8aa8", size = 223338, upload-time = "2026-08-06T13:48:52.896Z" }, + { url = "https://files.pythonhosted.org/packages/0f/76/e77d0ae22501831cc9f92193e8a957a5caa1dd177f90a6d1d9b106242d92/coverage-7.15.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f41c17c4668a655ce96d090d8d5ffdc24ef64b5a02f9753884d08483e8a4a41a", size = 223609, upload-time = "2026-08-06T13:48:54.688Z" }, + { url = "https://files.pythonhosted.org/packages/82/1a/b1f089da8d38ac612fa2dd6dc7f4a1a7657d12f3e261d2996edd3a838d0b/coverage-7.15.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46822e9b6ff1c6a72b518c162c44a8f45a61a1d609c51084bf5b16c023c5037b", size = 264970, upload-time = "2026-08-06T13:48:56.403Z" }, + { url = "https://files.pythonhosted.org/packages/bf/31/e66d98d6e9c7fcc88470f1e234eaf6b1950dc0dfbf797f7282c1c861da24/coverage-7.15.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d6f4955b73b5445271379a59e3792b0d978f42d4a01e0cf7a67d9c33a3bb0a5", size = 267088, upload-time = "2026-08-06T13:48:58.41Z" }, + { url = "https://files.pythonhosted.org/packages/59/a1/ae94eb2c541add426378408379f233591e069040b1e2cdb33df9498a0682/coverage-7.15.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3fc9e047706fb4a9abb54f719d3aa643e80e5bb3818182c40aee01ac0f0247ba", size = 269508, upload-time = "2026-08-06T13:49:00.42Z" }, + { url = "https://files.pythonhosted.org/packages/9c/c7/88a10694a1c6a213569766aba9f25847b28155d4ac731b13226db216356d/coverage-7.15.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05e491d4f3165d62d4f5c8fd48dfeabf2ae8f42cbbd484319af33ea851b78982", size = 270629, upload-time = "2026-08-06T13:49:02.234Z" }, + { url = "https://files.pythonhosted.org/packages/b3/34/d8b8232e5e55169933b59aabcef2fedfa4b9d8897361bb80fcbda146505f/coverage-7.15.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:226c66e80ec0598d3b9b4874123df167ccca342aca8714f77cac6829688ee09c", size = 264043, upload-time = "2026-08-06T13:49:04.102Z" }, + { url = "https://files.pythonhosted.org/packages/7e/35/58b009dbf8c471c7224716478b9fed4a7e1af15320e1ed41660978504663/coverage-7.15.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac41cc14bebda0dbfb0628036b7f75706935c95bcc07fefe9a0f93614aa60a57", size = 266963, upload-time = "2026-08-06T13:49:05.821Z" }, + { url = "https://files.pythonhosted.org/packages/62/aa/57fbda1b42c892968273c56b6ee9dc0f1310850859230a507bc7873b1f65/coverage-7.15.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8af623e5cd92080acddd02b38f2f406a2c3a0893c38950b211890361448fbf26", size = 264569, upload-time = "2026-08-06T13:49:07.706Z" }, + { url = "https://files.pythonhosted.org/packages/98/8a/360e6e7f24d477b7e889703af0afa878d15b6d4d8d2a822b2835c169a879/coverage-7.15.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07545711d4f0f32852a18f18ad11f76f0109909d09e78b9008b4cfc67e829429", size = 268299, upload-time = "2026-08-06T13:49:09.587Z" }, + { url = "https://files.pythonhosted.org/packages/4e/89/6f701261aee21b6b5fa8f7872229406dc917e125069448292223bf213606/coverage-7.15.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a0865421cfdc53654b342d515e5a233187590882d20b95752150e53f65460017", size = 263413, upload-time = "2026-08-06T13:49:11.604Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0f/6f04036edc260ed425af83e834f627fad48941ce97b50bfe6edd8b6fa623/coverage-7.15.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:460115e32ee40566476db5048f9bec1e842c127ad8e6f8be745aad3ac9cbc839", size = 265725, upload-time = "2026-08-06T13:49:13.38Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ce/d19b5d4d5c49a7bfb925fd74310fee7d28bc99520ac3367ccbc54e662518/coverage-7.15.4-cp314-cp314t-win32.whl", hash = "sha256:cbde877ef9dd7baf272b9bfef2b8a25edd45d9170fc326951dd20eb480335e85", size = 225079, upload-time = "2026-08-06T13:49:15.265Z" }, + { url = "https://files.pythonhosted.org/packages/26/bb/7aa1b3b173faee0679037ca950bbbe1247273656697994d8d13f80f8d4b4/coverage-7.15.4-cp314-cp314t-win_amd64.whl", hash = "sha256:3da9e92d1c551fd7563833e9ade686efb0c4b7363ab7681a94283958c950bf5e", size = 225911, upload-time = "2026-08-06T13:49:17.279Z" }, + { url = "https://files.pythonhosted.org/packages/81/1c/4ea9e47426d80038d9222db3c4534cb6021a74b237d3ff97ffd33b6600dd/coverage-7.15.4-cp314-cp314t-win_arm64.whl", hash = "sha256:3a54f5a0d85050c73a38f6793090ee83974531e67fe5e57a1da9bee11398aa5e", size = 225219, upload-time = "2026-08-06T13:49:19.293Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c4/dc5d2ac8f9142e7ec7de66e7bf0591db29d78955a040bd915870d9c0e657/coverage-7.15.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2c9872e4d9dc5d3cf616bf4b382f5a00359305a5be666a3dd0b5cdb4e49597f9", size = 222604, upload-time = "2026-08-06T13:49:21.279Z" }, + { url = "https://files.pythonhosted.org/packages/70/39/33e63df81fe2ee100897451841c821467635923e58e37c6bd4b46dd8106c/coverage-7.15.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:e101dbb4b9b72f0cddd8cdc8c9c5b47f456766f5e0ac82dbfb75e5c55409b78a", size = 222944, upload-time = "2026-08-06T13:49:23.187Z" }, + { url = "https://files.pythonhosted.org/packages/99/1f/ef3ffb5557febc75a0d97aa459d0266d7d741110265121cc6d8539343d44/coverage-7.15.4-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7d1abebdb047729e852b9c77a00497dfbeb11eb3a117e037d7dbc3ac8e5f5c54", size = 254050, upload-time = "2026-08-06T13:49:25.008Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f5/1f0f6f77698c3601ca0ae7431e34b24c62ca2f06fecb23b73ed1f651d2be/coverage-7.15.4-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d28a4a899354d0ea6214cc59b4fa19eefbce1b9ff1688ab579acf49e894bd3fb", size = 256967, upload-time = "2026-08-06T13:49:26.896Z" }, + { url = "https://files.pythonhosted.org/packages/03/7a/2ed9bed79925f4367c83c77f66a89e5ca7229c288d2d19ad5f36d1ca0070/coverage-7.15.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb3c2aacea411cc7e1d27712490c11108e2de1d39019ae32915493a59a8b9ed", size = 258587, upload-time = "2026-08-06T13:49:28.692Z" }, + { url = "https://files.pythonhosted.org/packages/45/8c/fa34044f71b7cc4ecb6da9c2408770959b0591fa9b5fb6fb6bca38f94298/coverage-7.15.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9447978a92f405d301123cfd39ff49895490efb769a758fe2734c7f631bf8ce", size = 260785, upload-time = "2026-08-06T13:49:30.472Z" }, + { url = "https://files.pythonhosted.org/packages/4f/54/d5727ce36b4524a7394ab9f5f1df378e1f23affcdab01037dc8655185cc7/coverage-7.15.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:050467a7983b8e2fe7dd41a78bb30c3e7f8c0b8cafda14b1c46f8b5e3cf2dd3c", size = 254545, upload-time = "2026-08-06T13:49:32.271Z" }, + { url = "https://files.pythonhosted.org/packages/dc/e6/6e3783e576719590194bdffb6dd6d85490801785b7c331e35a245d8cb8b5/coverage-7.15.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d003b7a5708ddad5c206c79607a6b92abb6fc13c57d99d8a4468cc03a2941ced", size = 256682, upload-time = "2026-08-06T13:49:34.089Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f2/bacdbde18b69ed2de424fcf64d9fb0a4913753d4f0eca8bae9daad69f4bd/coverage-7.15.4-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c38efe30fd74e5c19e9433f11fb1f5dc9c6522770971b7c6145bbaa413dc8800", size = 254560, upload-time = "2026-08-06T13:49:36.052Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a3/1fb927196e3477c1b48831169ab58ba08f451ba87ae311ff1de68b26a616/coverage-7.15.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:1f4f826d70f772ab8b0c052329580d7fe8b8abd191e4ce0c8f81aec6614665d3", size = 258792, upload-time = "2026-08-06T13:49:38.01Z" }, + { url = "https://files.pythonhosted.org/packages/41/58/30d4c149c69053de0edfe325614c1d28d508f62b1783e0e4a234d2e49136/coverage-7.15.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4a4bf917c9953f57c957be31c1cd504e3bd2f34d4a352b9d391a3025336f6768", size = 253968, upload-time = "2026-08-06T13:49:39.934Z" }, + { url = "https://files.pythonhosted.org/packages/89/e4/77f639371b918aad30dda4051f95404b43578f7f2e2f87ba73e02ed1ff37/coverage-7.15.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1c9bf40ebef178a45192c75c4964760bb261b0e6ad725da5fc4c93f674f19753", size = 255893, upload-time = "2026-08-06T13:49:41.825Z" }, + { url = "https://files.pythonhosted.org/packages/5c/62/13be29b3ddab35f14c87967a4820a05106d2a3eccb4fa4ff550bf30b75e0/coverage-7.15.4-cp315-cp315-win32.whl", hash = "sha256:43619d04c3671792d2c4706ae8bf45e265dc87bbd4078189ef8b847ea1e74be2", size = 224768, upload-time = "2026-08-06T13:49:44.08Z" }, + { url = "https://files.pythonhosted.org/packages/a1/70/af0c6be0f964af6954f6b74bc109b0dbca02824696d2520fb17fe1ab06e3/coverage-7.15.4-cp315-cp315-win_amd64.whl", hash = "sha256:be619439dbcd31a2eab10b32de9fff62c26ed4bab69dc32b8363fdaaa0882809", size = 225242, upload-time = "2026-08-06T13:49:45.899Z" }, + { url = "https://files.pythonhosted.org/packages/4f/2d/f3bd3aab899fc9efc18b53133ee68f5f98574ef480649b23e12962226387/coverage-7.15.4-cp315-cp315-win_arm64.whl", hash = "sha256:def597967dafc2e8d97c9097ea453c464e0bb8ed38f193a43070f10dc623bb6d", size = 224674, upload-time = "2026-08-06T13:49:48.322Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ca/f69251cd63eabc6438321aea22148754cce758a26bde07dd490e3fe7cfc5/coverage-7.15.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c7dbc748ac8a1e3e59a2b28bea47675e6e778081dbbf081bde0d75def2fcbe1d", size = 223333, upload-time = "2026-08-06T13:49:50.293Z" }, + { url = "https://files.pythonhosted.org/packages/a7/a7/037b53b2885b0d8447064432491a4d5a1014cd9f97a594d53acd0c04541a/coverage-7.15.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2413074a5ecbb61a01a7888fc72db0ca324d13588c5b38bc0dd8564cdcdfea26", size = 223630, upload-time = "2026-08-06T13:49:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/80/4f/152b8a4779ae90da11bb24f7467df8a59f0be48a5c52acb856325ca48289/coverage-7.15.4-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4e6f6f632b7b2f714bf7a1346e8f97b650ee71f3c298aaad42a2ab60f0f07645", size = 264489, upload-time = "2026-08-06T13:49:54.52Z" }, + { url = "https://files.pythonhosted.org/packages/10/2d/84b4b9e0e1dd6528a51920ff7031f35b789382e467a28ec6a5a578cb8812/coverage-7.15.4-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8df457da2249d3c75ca2e5e835d59c725abfe92d27fdff6cd99eed85b51d5e9a", size = 267567, upload-time = "2026-08-06T13:49:56.721Z" }, + { url = "https://files.pythonhosted.org/packages/53/fc/ba01cc25299f9f8a2c8b02d3b28c53f3543d9fbfbe4e74fa2760b48f163e/coverage-7.15.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:050f66a08805acb5b8a23c6d4a517b1ecf82c08e81ed0e4bd727df065e5c6624", size = 270123, upload-time = "2026-08-06T13:49:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d0/db2647cbf40b14f8c308f94ff7bf89c06d564e59f396906edf50086ec788/coverage-7.15.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1587fb771d1ccceef708fdde1e5af8c7ed24b486b61d13a321acb7d8145390aa", size = 271107, upload-time = "2026-08-06T13:50:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/4d2d17924552c458bb4f77dd631f0e3bc92fbbdf2d2d916cd4b33bbfd5b1/coverage-7.15.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8b4f1c3a69ca580f3fbd6b2046915f536d7f586874f25c1bb23add2a3c88d50f", size = 264955, upload-time = "2026-08-06T13:50:03.023Z" }, + { url = "https://files.pythonhosted.org/packages/ee/de/dc010c7a3691f396d93bbc26bfcafa1c2a3a351cd520470f15faf5795bd5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:ffb58d7eff5b7f6ecc6fa21d6288ab7f968a212cb67d682c269c09b9eba3b66f", size = 267949, upload-time = "2026-08-06T13:50:05.557Z" }, + { url = "https://files.pythonhosted.org/packages/78/ea/dc96a11375e83c045c2f7c61fb6918277cfe9401db7c0f7b1d111a84b2e5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:d9df165544774574ee004b953023d1bebada1894a80b1052a43d798b0f676e67", size = 264421, upload-time = "2026-08-06T13:50:07.612Z" }, + { url = "https://files.pythonhosted.org/packages/c8/86/b77131a0f9503ce461cd577076147d7a9040f0c5dda772686f729e2cc9cb/coverage-7.15.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:f9de0a24a4079b53e523b5c5e2c5945ec251ab486652659955187cf255a259bc", size = 269121, upload-time = "2026-08-06T13:50:09.58Z" }, + { url = "https://files.pythonhosted.org/packages/24/24/944bc35007862955e7ebf05754e645419dcf5d7526c52735cfa2715e8ebf/coverage-7.15.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:150089274bdc9f940628552cb92844e0223c987f1902ab8efe9f45a2ec758d88", size = 264565, upload-time = "2026-08-06T13:50:11.722Z" }, + { url = "https://files.pythonhosted.org/packages/c7/cc/a3bb9f93e7e740659163e2ea584f8196ddcd2c456a5dbe15f6c50105fec1/coverage-7.15.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a58a94fed5da6997d258e8f7668c1e195fbd04a691d781b7558f1e468f9e68bc", size = 266522, upload-time = "2026-08-06T13:50:13.786Z" }, + { url = "https://files.pythonhosted.org/packages/49/dd/e0e40f3560d878d888c580698ff5ad1179f5e1c3ac949684ef66b41a3817/coverage-7.15.4-cp315-cp315t-win32.whl", hash = "sha256:ebd5a6d8466ff30836572f3ba2cae8a5e8f85029b1c6d5e2ed338dc472a5166a", size = 225068, upload-time = "2026-08-06T13:50:15.825Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7e/37732ea80eebc30e976e4cdab15c190bc42d96959a42e38ddf6f8c60468f/coverage-7.15.4-cp315-cp315t-win_amd64.whl", hash = "sha256:288bde2a2d7ab6b6c2d7252fcde8b524387f2d970bdba9658fc6f8bbcaef0f9b", size = 225895, upload-time = "2026-08-06T13:50:17.928Z" }, + { url = "https://files.pythonhosted.org/packages/c6/08/1e00f7923eaaba45fb3d51dd794125fc766304b1df264f3a9c6557bfb30e/coverage-7.15.4-cp315-cp315t-win_arm64.whl", hash = "sha256:68be5e1de60ff13c9095bbec0e5a7fa45b33b101752215b91345ea1f61c4a278", size = 225213, upload-time = "2026-08-06T13:50:19.981Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d9/e70c286c979378f061d8266e279b686ab0b0b688e1fe0af864684f23a77d/coverage-7.15.4-py3-none-any.whl", hash = "sha256:964730a1e9de9c0cf11be6a1a3c79ce419c34882842abd256086ba4698705e84", size = 214332, upload-time = "2026-08-06T13:50:22.192Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "dataclasses-json" +version = "0.6.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "typing-inspect" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227, upload-time = "2024-06-09T16:20:19.103Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, +] + +[[package]] +name = "deprecated" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "docopt" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, 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 = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303, upload-time = "2023-12-13T20:37:26.124Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310, upload-time = "2023-12-13T20:37:23.244Z" }, +] + +[[package]] +name = "jh2" +version = "5.0.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/b1/b2b7389b2e0ddac90a1aecbf4a761db8790de85dace7695c01173ed083cc/jh2-5.0.13.tar.gz", hash = "sha256:f8c78cffb3a35c4410513c3eb7989de36028c84277c04f07c97909dd94c23a75", size = 7322505, upload-time = "2026-05-29T05:21:43.516Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/96/c38d3555cc9bbb1c309c896e99dbb0b1f76308bed7f2d6226cdbeaeb3eee/jh2-5.0.13-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2195557f5953ceee743d0eaab0b09ec537bbc1b9a2c6c1463106bfca9b03805f", size = 601454, upload-time = "2026-05-29T05:19:23.763Z" }, + { url = "https://files.pythonhosted.org/packages/4e/84/a3043ddaf636cec0f4c3aa179a9ef3a59db3d06f770ab6007133ca31bc5f/jh2-5.0.13-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e734f8a5cf002316cc81b80d8a91a0f7c62d631270abd913746644fb9ae288d", size = 384187, upload-time = "2026-05-29T05:19:25.504Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8b/72dba3fb64bf01355a0bbd78cbe0a2635759d4a6f273c1b118760ca8ecf0/jh2-5.0.13-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba5e545b3209e8aa5b6af88e7032814473e57d60d32d3fc6b6185453733a4b26", size = 389346, upload-time = "2026-05-29T05:19:26.926Z" }, + { url = "https://files.pythonhosted.org/packages/8e/2e/6f07e42de6101d4555f14be1e4f42a056c9a44d7ee8f745b3c188c96cbe0/jh2-5.0.13-cp313-cp313t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2a37164eb8f2462268e6252db93b009cb685fa060b62ad217bf1b333b450c68d", size = 510278, upload-time = "2026-05-29T05:19:28.175Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a5/85682dcc959682aede0f80206a16574c51af51ed26eb1e87252fd31c6ded/jh2-5.0.13-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4eae2d5bc1a91033450fd4381c04f33db7d4c9cfd31046c4708c8c1323c06d0", size = 503090, upload-time = "2026-05-29T05:19:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4c/a8a301d494c0a1c2859951d44b4fa54f3b2db91826c719ce5be8b662c5c8/jh2-5.0.13-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d0a10036cdf8bf1a36b07c10b69ad0d08ce31d99b8962e3d055ee417fe3423", size = 402843, upload-time = "2026-05-29T05:19:30.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/5f/5f9e0933f458a89005af3b2aa7d1c61cc3cc57c8a633fd54a95acff85121/jh2-5.0.13-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ad4d68340e38a8474a48c424e2929d3e2e82e35e4e8ff1188decb1985d1b4d7", size = 386327, upload-time = "2026-05-29T05:19:31.988Z" }, + { url = "https://files.pythonhosted.org/packages/75/3d/353ec4a6763e8b6be8d162e6b23f7e1499e75b1bb934cee1e5b5fc062f1e/jh2-5.0.13-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3f8c5e81254b986f8abe9fd974e7ed595aeaead3f3066ce9ed95dfff236ceca9", size = 406978, upload-time = "2026-05-29T05:19:33.438Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e5/cca58b58cb8bab1891f45da43ef91b498ff93d573a1a2836af11962ccfb8/jh2-5.0.13-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:da030729eb8ef386569e78c10e2429adb10026ce82989fdeb906b037314d66c7", size = 560030, upload-time = "2026-05-29T05:19:34.879Z" }, + { url = "https://files.pythonhosted.org/packages/c7/c9/c3391d8607781016daa1881d8d80f81e0cf0302ff06c86342c492279cf48/jh2-5.0.13-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:a5d2ceb2e9d42ac236f0da7b2f9e8237818d32a10d892b06f30f7160ee4365ae", size = 664407, upload-time = "2026-05-29T05:19:36.138Z" }, + { url = "https://files.pythonhosted.org/packages/e3/b4/3375744e2f33d097da241dfd5262c9997a87c2b948db2385fbf100c7dd12/jh2-5.0.13-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:1f4dffe1c6ed0d4b0d2d9a45d064622bb786cbf87db35eac469dd4c376ac6fb6", size = 625242, upload-time = "2026-05-29T05:19:37.93Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2f/e33763b1f66817bd8c848777f05cf3781ad9ee1c642741458bfb66da1921/jh2-5.0.13-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:bdeb48657db508e8bc299744aa431798efdf090feb3959cf0123167c9251ebb0", size = 591124, upload-time = "2026-05-29T05:19:39.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/92/5a8de2f6936c7af77555299732795f4607d2cefb974ad485acf25b3f998b/jh2-5.0.13-cp313-cp313t-win32.whl", hash = "sha256:b41867d5decd7cd62e12cdacfc45c5d8f6eae872578ae01d78900174d5e47b82", size = 238101, upload-time = "2026-05-29T05:19:40.917Z" }, + { url = "https://files.pythonhosted.org/packages/e7/71/0f7ba74bb6681e60e871284772a216a50cfd63f4f62ef52edd13d8a7c057/jh2-5.0.13-cp313-cp313t-win_amd64.whl", hash = "sha256:62aee4bd128e17871980e4847cc4b94b67651de612bc96dd3d7ebf647c68a6e4", size = 245923, upload-time = "2026-05-29T05:19:42.338Z" }, + { url = "https://files.pythonhosted.org/packages/b1/dc/11434a6deee70630f5f38cc40813692e1faadd4259f242f4f810f35709eb/jh2-5.0.13-cp313-cp313t-win_arm64.whl", hash = "sha256:267aaa7a96f54452e1e07c7701799bc7817e1f3bb2de09301b2c492841e4c98e", size = 242596, upload-time = "2026-05-29T05:19:43.853Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f8/e9e82b7b51947edbf762edb26eb9bcfadda10fc7526d092244528e9cea8c/jh2-5.0.13-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1dd137086b13d55a53c4b7b8b75869c824060a4d35615e4f49180ae58c7783f5", size = 601736, upload-time = "2026-05-29T05:19:45.14Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/e82b4c5797c20facb2f1b54f893a422f8ba44f83bc66da3d60d90a1c3064/jh2-5.0.13-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75d0b2bffb2e260a0f3578a11a4999a713c94d1bd8de7cfe692c840e20ae83cf", size = 383983, upload-time = "2026-05-29T05:19:46.687Z" }, + { url = "https://files.pythonhosted.org/packages/27/df/f439704e7a884b5bd0181188b30217d71d48ce6bb9a9d7e8378d00ed3497/jh2-5.0.13-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f681e197bf6552df3bb7a3abfd676e81316b69e36e7263e72383477174032ca2", size = 389233, upload-time = "2026-05-29T05:19:47.973Z" }, + { url = "https://files.pythonhosted.org/packages/49/9e/c2cbdb2261724edb83fa0bdb260f75ad9b091189ada1651608f8ade71906/jh2-5.0.13-cp314-cp314t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bde7e7ff0f17e87aca9ef23f66b80540a2de0ff8e2c26af55eb2e288a2a5f74f", size = 510268, upload-time = "2026-05-29T05:19:49.183Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cf/65925f2aaea92e5f4a90a578b86a975bf6327ad53d82035a4d94f8f63580/jh2-5.0.13-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f8d364d53cc0854d910f2305726a8254acb527f13fa933dc213a591e21e7ffa", size = 503003, upload-time = "2026-05-29T05:19:50.699Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fc/684ae4efe7568ec9f8ce427b6ecdc7387f870b3bbdcee75c370326685865/jh2-5.0.13-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b572daaafbd867bb5c5f324c918704ddd6d76af1cc7c2be483c3649b76ce7940", size = 402856, upload-time = "2026-05-29T05:19:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/0d/41/009c8b95f471ca83667e63e87a845f634f7b100f9fa2c1cbfc090c142ec9/jh2-5.0.13-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0772ad66f346038fe05cb2928866ff1b21bcaa9ec3f14709fa1d419ed1dc287", size = 386592, upload-time = "2026-05-29T05:19:53.201Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8b/5b49f66cadc6d89db5339b602c06a9567ef493c33b860324f24a96b390a4/jh2-5.0.13-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1d8313b13acfdef19c5ff793fdf397bae3d8d75181f6eee62604f13e4ec20a8", size = 406930, upload-time = "2026-05-29T05:19:54.441Z" }, + { url = "https://files.pythonhosted.org/packages/55/a4/7b997c7787ee6316c6b4f7bc0ff4d80c062ef3288038fbee870ed83f0946/jh2-5.0.13-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:99aedd20daacb32f377152c9103d8d0271f32d1932bd39a5ed2cd2eef97f7e07", size = 559781, upload-time = "2026-05-29T05:19:55.659Z" }, + { url = "https://files.pythonhosted.org/packages/ba/08/b75afc38215f961ba8d92af64a9e7cba57767829786484c5cb98fc1a1d2f/jh2-5.0.13-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:53b1df29ffe966cde1d2f0085b6acfcd237504627d406a51717ef3849ec88831", size = 664237, upload-time = "2026-05-29T05:19:57.031Z" }, + { url = "https://files.pythonhosted.org/packages/a1/92/0fad513d95376611b337c631abd0263e641b78b0adbe80f3cf561e614d6e/jh2-5.0.13-cp314-cp314t-musllinux_1_1_i686.whl", hash = "sha256:0ee6a5dd24a1dc0772a89b3498ed20f60167fbc633404b1e02895f04ae66c47c", size = 625249, upload-time = "2026-05-29T05:19:58.447Z" }, + { url = "https://files.pythonhosted.org/packages/79/28/4f70df7bfaf1cdb560340400b46849cfa96e4b08d2430793501013d7ee8a/jh2-5.0.13-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:521f871864880cabe361060f070516a56a54abc3e96f07bee9ba8b061eada891", size = 591017, upload-time = "2026-05-29T05:19:59.839Z" }, + { url = "https://files.pythonhosted.org/packages/dd/58/ed891762aa68542fa7969c4cbb109817231740f342931165f60ea7a2a923/jh2-5.0.13-cp314-cp314t-win32.whl", hash = "sha256:5909e2273f8b6fcea93c95564aa0f366ff9261a544a7a5d640ea95341321cc37", size = 238041, upload-time = "2026-05-29T05:20:01.228Z" }, + { url = "https://files.pythonhosted.org/packages/48/13/46ae2bf16baad7841526dba44668083f9de3e427027e4fa44a567cb778d6/jh2-5.0.13-cp314-cp314t-win_amd64.whl", hash = "sha256:2cc4923ac6f57753e0cb4aef054a04f5d5c744b274dabf863f695fa3c2fd75f9", size = 245861, upload-time = "2026-05-29T05:20:02.757Z" }, + { url = "https://files.pythonhosted.org/packages/79/49/3652903fc82c0e5c82e9caa3445e3e514b168969c2dae3da15c46627661f/jh2-5.0.13-cp314-cp314t-win_arm64.whl", hash = "sha256:d3cf203de31371289d4c1e6b4d30c9f6de95b2bd2b300fe5468ff946299d0e48", size = 242699, upload-time = "2026-05-29T05:20:03.9Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/8c151f371f7962902e6f1f494b50abc1b8be27305a9d12cb88cf049c5a80/jh2-5.0.13-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3f54095cde010ff4e52f23dda92b31d3d3160c64f5f23fc6e181f8c7938afd33", size = 618581, upload-time = "2026-05-29T05:20:05.043Z" }, + { url = "https://files.pythonhosted.org/packages/71/3f/a2d5458586c024e0076ae8182dcbb71450bd9d4dd65ffdaa659313461bd4/jh2-5.0.13-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f810dd02b4a9647eacbe4eab617111c0e3ffc68e923e3ff172948db601d2ba1e", size = 392776, upload-time = "2026-05-29T05:20:06.178Z" }, + { url = "https://files.pythonhosted.org/packages/7e/1f/cf205104cfcef4bd26aa2b736f36ddf3739b69ddaf14d23a5f7697673d74/jh2-5.0.13-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29c23f3937f257beb5cd5b17f8727404f1267ff25d26e54c0a35d5defd0c895e", size = 397943, upload-time = "2026-05-29T05:20:07.479Z" }, + { url = "https://files.pythonhosted.org/packages/cf/82/bd218132e5a081293cf3d7de5cf41d871f3f88808c23f6c6aebbe073fb5e/jh2-5.0.13-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c697b250a935030a128b45b41f507a4d33a082d8b8e12311c7e73fbb3970f057", size = 520351, upload-time = "2026-05-29T05:20:08.936Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/78b564899237909ddf4103569be59ea31b6c00f8b4f1c178cc57c6e4e45d/jh2-5.0.13-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47d3203ed6fec39df4922764817979fad3a13d22cf750adc9cf9dc2b3399eacf", size = 511162, upload-time = "2026-05-29T05:20:10.239Z" }, + { url = "https://files.pythonhosted.org/packages/db/3a/b59157363139436cb60620353aa8b687197c9a4ec1eb87952004a8843e6e/jh2-5.0.13-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5146468a76bbe8f4035f23a78813c5c433c70c0ca44eb0ba5558627a9e644b8", size = 411838, upload-time = "2026-05-29T05:20:11.515Z" }, + { url = "https://files.pythonhosted.org/packages/15/a4/18b35000b00d9fb72003e0eacf66b3af828a0bb897c21a287a33d2025138/jh2-5.0.13-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85d3d338284fbfbe173243c3cc62e510c6fa0d6d023e67da2292bd7578c734e3", size = 395975, upload-time = "2026-05-29T05:20:13.157Z" }, + { url = "https://files.pythonhosted.org/packages/ab/42/f017b6b7b666e6ef615f7c64fd748201d0b20edeed094182c4f37f23a6e8/jh2-5.0.13-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:641f65f5414b8990251bd7609513a696831558f69d2ca77d65dc3f7087f31267", size = 417493, upload-time = "2026-05-29T05:20:14.394Z" }, + { url = "https://files.pythonhosted.org/packages/bb/fc/054443bd40a9827ade99508482185692530cf56a50d4ad8ed2aba761f11d/jh2-5.0.13-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:cf3dc6f7c14340241260bf63e5d6006b2ee7db45dafcb39c16fdf164ea0240a0", size = 568373, upload-time = "2026-05-29T05:20:15.914Z" }, + { url = "https://files.pythonhosted.org/packages/47/48/dde093626bb976722c940053ceaa82407bd358e07fa6a1690590456d21b5/jh2-5.0.13-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:fb8e37ecf37382c02af83da521df5a77561441bc4cbb93b00627e015b7bbf349", size = 673766, upload-time = "2026-05-29T05:20:17.136Z" }, + { url = "https://files.pythonhosted.org/packages/96/94/edae999301bb28738e17cf54ecd50b7d7d9fd96d9536376bbeb9b8498526/jh2-5.0.13-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:296ee44dfad08ccc3857daf433dfa4a555d5b45f2df39503ccdd90ff10b32943", size = 635045, upload-time = "2026-05-29T05:20:18.633Z" }, + { url = "https://files.pythonhosted.org/packages/c3/5b/d9fd0fc71e43c37c30d581affbb7371fa8177ce211b663457939bbed9e61/jh2-5.0.13-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1a75eed545420a31ea356e8046b8d86fcf694234dd1035191734a03720fb8a44", size = 599870, upload-time = "2026-05-29T05:20:20.117Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ec/66c17d8aec77d41c8f93145dd7faa7138b9f1bc370fa685008bf92d75f9f/jh2-5.0.13-cp37-abi3-win32.whl", hash = "sha256:4812710810010c428db04e9290a13117f7b7c5a3a663b46a0586fe101cf25c9d", size = 245231, upload-time = "2026-05-29T05:20:21.275Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/107f16a84c8becef5281040fdb50681f06e4615bb4a754f211abe7d61a6f/jh2-5.0.13-cp37-abi3-win_amd64.whl", hash = "sha256:5d61bc0a62b4eee11039bf29d4bdbb26efcff760dfb9d8e0739740028aa52b0f", size = 252085, upload-time = "2026-05-29T05:20:22.426Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/e8baeef7655449a6c9abee0165fcb1c63d12ec5432b8d810ff5910184690/jh2-5.0.13-cp37-abi3-win_arm64.whl", hash = "sha256:3f74dad9036e599eed51576492b00955237b86cf886f96844708a81cd0f5c710", size = 249139, upload-time = "2026-05-29T05:20:23.558Z" }, + { url = "https://files.pythonhosted.org/packages/64/5c/5f006d5bec874c9b12f36f20fbdd3fce545994b3d43eb216266d36c1cd09/jh2-5.0.13-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:72122b850d58c4ae0612c9b9bbabeddbb092b503eb39d87249da323ba1b074a8", size = 614502, upload-time = "2026-05-29T05:20:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/61/9e/ee99e108c6b458ec3298a76b59385ffe0e8f82d93ad30efbfb23632f6991/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c4d4ef923464dc5b39d39b1a41209a23e387698a254e81813d0757f76ff00a", size = 390823, upload-time = "2026-05-29T05:20:25.959Z" }, + { url = "https://files.pythonhosted.org/packages/3d/64/740cc33301c84ba79304567c3d199bf269805b7baebe8e0082739b97524a/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc238641d3bc35dc79965f330c7f0f16ca992cf999c8892dce0bd36c32056eee", size = 396388, upload-time = "2026-05-29T05:20:27.202Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ed/522f67aa1946c27ef66dd85408fb16b94fe280b84833f06455e2ead119bc/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f9a82f90a21660fd0313a81c838bff89a13c7921c317abfc3117e49f9d46c84", size = 517651, upload-time = "2026-05-29T05:20:28.353Z" }, + { url = "https://files.pythonhosted.org/packages/ea/5b/e5d8568a60b265e6b34672269407ff6d3a6c43be821b433be54dc8ce63cd/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1ef1d6c90389c366ade34bc6b100e823569c60bbb0b0f2cc3b308031090b252", size = 508661, upload-time = "2026-05-29T05:20:29.739Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ea/1490d995d08cfb67350625f306297b9407ce7012ddbb83f97b1d18c63be8/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95d72124353a530ffa5a65f1f94a4396521f5472f0fde79b93dd66a3addaadff", size = 409884, upload-time = "2026-05-29T05:20:31.016Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b1/f8a48046f17d927d5847504b1199212755baba5f1d842f9a0bc7abd5d7db/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50d224b482e1e18fa6bbe90d05125993122c5530b0b83f48829d7a535e2b60f2", size = 393947, upload-time = "2026-05-29T05:20:32.222Z" }, + { url = "https://files.pythonhosted.org/packages/12/f0/001ea64504515c7990394ebaedb285611c7dd8786ad2005a4220e4e0575e/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:49531e0042b3a4d35d34ddb892bb734d77969c9b787e6f2f0253cb10b498d4e8", size = 414876, upload-time = "2026-05-29T05:20:33.519Z" }, + { url = "https://files.pythonhosted.org/packages/1c/4d/25126b7a084c946326d3fa73d24473853229dd8d3f5f0e26d3ef6ded36f5/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a8bfbc5f77e95f485bf046d4a19bb773866cdc9d1b21c27a194366a55d7fe361", size = 566960, upload-time = "2026-05-29T05:20:35.12Z" }, + { url = "https://files.pythonhosted.org/packages/bf/bf/2bd0a1eb5f8cb1e516ad774efa827647ade125aa9bf658944ee5366dbb60/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3e5e73bbfcf0690561a1e2bf8b2d8f1e0bfd1aca67ebbed2a753cf9440f912ef", size = 671979, upload-time = "2026-05-29T05:20:36.383Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bb/e58646c5764ff6f6c1ee9f437c43247a3043cc7cb524d9777368c390ba32/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:4efed5de773df44042a07a50ea8dc476a4c120bfd3fcc4fd7a7ea145196832b2", size = 633173, upload-time = "2026-05-29T05:20:37.632Z" }, + { url = "https://files.pythonhosted.org/packages/3e/54/0bcf70845cc7ae1c0cf0da56f81460bbc2f774a03708d0fbfa164439d684/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a915e77adc126bd79867b7cfecc930021ef1a2599475dda058ae80537a7068ec", size = 597680, upload-time = "2026-05-29T05:20:38.919Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b2/7f5e5305e261df12244d28eb5d8150affbe6e93e3d301df7d71ca872d89a/jh2-5.0.13-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f3768fa39d0c5a2578b285e49e8d42230bb182bb2f7984840549f89c04ce4a56", size = 608031, upload-time = "2026-05-29T05:20:40.212Z" }, + { url = "https://files.pythonhosted.org/packages/ac/8a/68b232de0eecc6e52548d9ef7ca20baf18d8cfb5cc64186d8015dda0188e/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ff9abf4b4769c9c8117d929f87c90415cb65f54faa22f64fbf044427127e65f", size = 388655, upload-time = "2026-05-29T05:20:41.707Z" }, + { url = "https://files.pythonhosted.org/packages/2c/31/dacd2315290e309df81b931434f30e3e73e0fededb9e0487667a5287635f/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:797e8846a4c7a18a9b3abdd437ca2fa06d39223df7a6fdcae57adeb7cc3d7a95", size = 392725, upload-time = "2026-05-29T05:20:42.912Z" }, + { url = "https://files.pythonhosted.org/packages/04/3e/a9fa9e093e984fd778bedcab2246f16b0e5a5b147466dd1d5784348239b5/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:31fec2270a204669fd5183465e3ef6a666a0539af61bf8e995df91f16c945c0b", size = 515975, upload-time = "2026-05-29T05:20:44.213Z" }, + { url = "https://files.pythonhosted.org/packages/48/ab/2bd00b443b4cb018ed522b1ecadb1edcfbda2226b5f1dc4b733488cd1a9f/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f48eeba6c0bb91321dbefc60527d945f20ccee3f0066bb3ba0d9b71120ec9417", size = 505682, upload-time = "2026-05-29T05:20:45.38Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/63d467fda5a86abec281ebfbe7d21f49689d97e0473800f11815fe92dd10/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05849bda038c2f8cee50559e3fd363af39c80fb1f24ecf947ccbc7e1a890683f", size = 406385, upload-time = "2026-05-29T05:20:46.568Z" }, + { url = "https://files.pythonhosted.org/packages/71/8e/146902bf3b16bcca18daf217ee9a1af34a47f6913f28c24bb403faf9832d/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:882b13542d7bd20f79cb92fc31ecc9587037278d0a6b36c14b734f9ac2cb8890", size = 391626, upload-time = "2026-05-29T05:20:47.916Z" }, + { url = "https://files.pythonhosted.org/packages/4b/bc/d500231f338aa889b29cdab977d99bbb94bec198f7fa0adc741b51118446/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84d42f63b1e7368194704159637e534280540369b35758a4daa712bf277e80ef", size = 411317, upload-time = "2026-05-29T05:20:49.336Z" }, + { url = "https://files.pythonhosted.org/packages/08/b8/0f7e84ab491aee9348731e4ef266595b8153aa9da63d5376e96970fb3795/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f6e95bfee4a72a7eb6fb4ca057ac5da297df68d302c63c300a5f400a0003fe31", size = 564695, upload-time = "2026-05-29T05:20:50.604Z" }, + { url = "https://files.pythonhosted.org/packages/8e/fc/776a56566dacc3918f51bbbaebab6c23a2e979a70b40ba1f526272f7b9c1/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:6eaa9919f7faf162d2acf66e1632c4588840589eb0030688907c9eb6a5ecae17", size = 668164, upload-time = "2026-05-29T05:20:51.905Z" }, + { url = "https://files.pythonhosted.org/packages/9c/54/ebcc2970d6ba34fdc6be0b8fac07a6fefea7e07e704654fd6dcc33d57f53/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_i686.whl", hash = "sha256:7586a835ed5e213593e35e0d6f90197dd76412fdcba62e05b15df72806af1e2b", size = 629234, upload-time = "2026-05-29T05:20:53.281Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a7/cfa24ecc4f3a70d16d4a33557b57f1c6673fd9fa0fd10b7a932342bb27d4/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:6d84d87f6c1b2131273b8078f5863d980269fa53139d7f7fa76e6099e2a00bd1", size = 594400, upload-time = "2026-05-29T05:20:54.63Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/be44f6e9ff5689fcdf9a2c0ed30880fd5da303676d89d6457a603450b7b1/jh2-5.0.13-py3-none-any.whl", hash = "sha256:2b311414989da48a155721fe47d0dc3e3b75360efd62d779aaff72d786be91cf", size = 98909, upload-time = "2026-05-29T05:21:42.086Z" }, +] + +[[package]] +name = "librt" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/12/e2e9ca532cf5a0e08c9489826c4a35c6958c92ba0313fda70e8c6c3912be/librt-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1a49adf16a7c9d9646816c2946135527197b6fcf4347c7b8b761cf1bfbf4489", size = 148673, upload-time = "2026-08-07T10:46:22.569Z" }, + { url = "https://files.pythonhosted.org/packages/6d/7c/02005e23478bd5950618d9712e0fd2b4c511657857f3efd8ba6a5feabcdd/librt-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81a398f45b45a59200e13cd5ad1ae1d3f44334de98b148331afe2cdfee701c52", size = 153547, upload-time = "2026-08-07T10:46:23.931Z" }, + { url = "https://files.pythonhosted.org/packages/a0/90/d8848a735f5642077fc4b3b4bebcdb08edf10178e3add45597f5201a368f/librt-0.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4eafbaff06b9563f8b1c850621ce51605de05208e09d4d71ce490bc972b7b9e8", size = 494355, upload-time = "2026-08-07T10:46:25.122Z" }, + { url = "https://files.pythonhosted.org/packages/e1/0b/8604f41ea02feace490e9e405a338a15f9905369f55b239a9ce31c946f24/librt-0.15.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b0411b4066db926b80258c60dcb0e6db4c9cee312eab45b7e8866b17ddf9ada1", size = 485459, upload-time = "2026-08-07T10:46:26.447Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ac/84153bda1ce0da609182527ab92b40d961809e544eefdc5a1c2422971416/librt-0.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d", size = 498398, upload-time = "2026-08-07T10:46:27.701Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3a/5ca6cd282b2c244bec8ec84102e09773264e9c02891d56ab3a8f0e4d7083/librt-0.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b230acc1c3bfe2d6f2627ba2b95dc92e58aa494600e9722d0e6ccbc931e59702", size = 515474, upload-time = "2026-08-07T10:46:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/73/d3/bd34110234779eb843c6ed66aba7c9b2091d3dd85989f1fb9922f564cb7a/librt-0.15.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6da110e5f314c19ab8478464d02ae18808ae73d522c15260fa4918acdcd64da9", size = 509484, upload-time = "2026-08-07T10:46:30.124Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/43c3f7f071d71631a7daa3b835ef2168ea39f20692d81464d4e47fbaa6d6/librt-0.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eab9208b00ca55bf75983ec99f7bf13acc746a36102e98953addaad7f7ea1e1b", size = 532534, upload-time = "2026-08-07T10:46:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/c5/1c/b854adf036ea817c40408873a5b794d65a91d9f0f39826f2ad2a2d5d7f48/librt-0.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c013cd3a1721e69e14380ada97eaa4b7b0cdf1c6b96fa765d4ea47c875088db", size = 537087, upload-time = "2026-08-07T10:46:32.734Z" }, + { url = "https://files.pythonhosted.org/packages/25/5c/c9a890e244e7dd725d3bd8b560e41f0aec787eaf343b46956a290ab7b841/librt-0.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:567b1c430f8bd560e689421468278ac5941bab4a05303b5d95b6ae10db03f451", size = 536575, upload-time = "2026-08-07T10:46:33.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c5/c8e70b60b704299555f55db468eb46b1c81bfc60201ffbfe20407d89870c/librt-0.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:29c4cab9df457b19672c39be7f384ebb2bc925c4e2684b8780c222b43eb36389", size = 517142, upload-time = "2026-08-07T10:46:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/56/d1/767a90c41f5d381b3195bc88ac0ec4afda35777c9c781e1f9848fedd965e/librt-0.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bccbd8e5b0bffb7106cf18eb1baa3d7194b1cebb3b4b1cdbd4bdb19382a6ee6c", size = 558714, upload-time = "2026-08-07T10:46:36.829Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b4/3c0624b8dc8301ab808f2b3a910995bcabe28df070fb9a0e5505ae997dae/librt-0.15.0-cp310-cp310-win32.whl", hash = "sha256:8ae493ed5f659a7761c43d42f183db514536073ded9bcf671d2d1df47e29a07e", size = 104426, upload-time = "2026-08-07T10:46:38.594Z" }, + { url = "https://files.pythonhosted.org/packages/31/98/e91c0382304bedb2db9c6801897319a9dcb68daac5e975819b562362f20d/librt-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc25fb356d0c7810bb49ff3df908ad1fda6995d660ab099ded69244ed7ab6053", size = 125057, upload-time = "2026-08-07T10:46:40.052Z" }, + { url = "https://files.pythonhosted.org/packages/59/52/06790ced2ac7117f890c21bda43c39c958ec82aa665c0718e821d33ff939/librt-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823b92cf3c18ecd08afc70c42473888b41b6e8ef5046f3b82c05c154a2fa3d22", size = 148039, upload-time = "2026-08-07T10:46:41.165Z" }, + { url = "https://files.pythonhosted.org/packages/e7/1d/8e150b7fc449a1f33c8a760965cc1f43b14fc1577d9d0b50ab2701420e74/librt-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70bc1b602cf59917e8f0c7a2cbc8bcc6fbc14d5486136b00707a79619121d63", size = 153067, upload-time = "2026-08-07T10:46:42.418Z" }, + { url = "https://files.pythonhosted.org/packages/51/87/a162bc5a66a35599dc619ecb215145f4de7d68e886b479b6d12593139f7c/librt-0.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:814ff83a25b5fce8b9c80c4dd803153fb5c5599fc74db9e022466938368957ef", size = 493087, upload-time = "2026-08-07T10:46:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/e5/3a/aeea1fc620cf48060d3065b37614edbf97043c099d0f50782bc8ca61d897/librt-0.15.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:57f5eeb6ad4c180de583b1038e61fe5fbd9796bb69a8a1c1a0c7ddbec4c8c60f", size = 485608, upload-time = "2026-08-07T10:46:45.038Z" }, + { url = "https://files.pythonhosted.org/packages/52/ff/fe571ad416f0856fd0d5578ffc2e6dc531891e586e36b647bcf50569cab8/librt-0.15.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82909c8f7eb9952656b65d3147afde4cf8e6d5a991eebc86418b5e65843b0ab8", size = 498723, upload-time = "2026-08-07T10:46:46.35Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e1/7a65eb5dedb1f00aebd948cdd8e17add48bf066cab3514e9daf84ab45a6c/librt-0.15.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f779070399f991400fc451719e0ea388eb7de313388bada2c127a35de05f798a", size = 516002, upload-time = "2026-08-07T10:46:47.599Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/59832b0ebfbd08c2742e6ece372ceb53f18bf1faef5d33c8daf3abebf749/librt-0.15.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bac89069bc496ebdf4f79ebb57bbd10d0b214c8454225deb672d91002bd17e18", size = 508607, upload-time = "2026-08-07T10:46:48.873Z" }, + { url = "https://files.pythonhosted.org/packages/ea/0d/37fa73f3b43ebd8259f91ae9102a15e5a54e65d581e48dea72df3e81d7a4/librt-0.15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e0d00c708fb2f5822b152429b1ac80a58dbbbc3f6c232c4d13a3f7fcf2ea5b4c", size = 530422, upload-time = "2026-08-07T10:46:50.45Z" }, + { url = "https://files.pythonhosted.org/packages/26/02/e046c6fe7a5881ac34623242192f484426ba8a75595fd18f22c53a3f530f/librt-0.15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6c6624fe268625869485553dd7cc1daf30d22558215bb2a4ff16f67a9801a31a", size = 534303, upload-time = "2026-08-07T10:46:51.693Z" }, + { url = "https://files.pythonhosted.org/packages/95/32/d5e6d861ab0366f3edf74f887ab0c9eb9f535aaf01d32b80b4f734daa179/librt-0.15.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f56b397858a23dacf35ede366ed2212fdc03a6a57a1ad36468ad6e9dc5fac091", size = 536084, upload-time = "2026-08-07T10:46:52.951Z" }, + { url = "https://files.pythonhosted.org/packages/2a/de/d69d725513fe53fc90c6d7a1f86e4428939bad2fb905b17fe4c18d413dde/librt-0.15.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4388184646efe2054911c5b00a1077d6d1ee86a95b7e8ba96dc7850a809f3f40", size = 514307, upload-time = "2026-08-07T10:46:54.194Z" }, + { url = "https://files.pythonhosted.org/packages/36/93/f8aded0d6682b4f25820fa86e0690f87f01df9fd7bd09ddb04d9167ad021/librt-0.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97335f59082f9fe2ce6c2a9cc6433a0114bbb6cd4d5c09dd76c95c68b9f9a8b0", size = 557686, upload-time = "2026-08-07T10:46:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/74/09/ffeb6bdeb6cd862b4272fddc8ad05f938dd25d020ed517e631813917d80a/librt-0.15.0-cp311-cp311-win32.whl", hash = "sha256:83380ffde38062a2e9bb55d83e74474f6614665528b98a6928720fc006dfffbb", size = 104917, upload-time = "2026-08-07T10:46:56.605Z" }, + { url = "https://files.pythonhosted.org/packages/96/28/7e2313a3ffbf0b4de7ba3da58a09e488507b4bd1ea2b5e69378354a23415/librt-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:f75720477ee05d509a310e856cacc8d909adc182f7b91193c207bcc26d7ee6db", size = 125886, upload-time = "2026-08-07T10:46:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/39/9e/04b8c3cde014ef255ee785730425268354543acc38902093a40afa0dc164/librt-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:256237037a3ab001ae8d9803b2d43562a4c3aa38739843694349e4d5ebb0fd56", size = 111885, upload-time = "2026-08-07T10:46:58.787Z" }, + { url = "https://files.pythonhosted.org/packages/ba/39/99c25030e782bdfb7a21be8c05254806a2e4bbb05c8d50c2a2130acbfa05/librt-0.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e87bc679f86a99aa3b26e3c78eeb821a247c9a28eae48eaafcc32c3bf4c3bb9e", size = 151021, upload-time = "2026-08-07T10:47:00.057Z" }, + { url = "https://files.pythonhosted.org/packages/14/43/f4b1bd1b2888798a1409808889a25ea1ba49eaabce7d681ed27734c2df9d/librt-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71599e011ac880e8e45d46047d714871894c7d4ab6f25626f8d4f89da21f368d", size = 155267, upload-time = "2026-08-07T10:47:01.311Z" }, + { url = "https://files.pythonhosted.org/packages/0c/db/3ad9c965c72f1e1d6beeec44ec10a54e17be8ae042fbb4baade16cbadced/librt-0.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c802434092b769b1d613ed2e13fac15fbfce1934a74bd10283b03c0fae231cd1", size = 503136, upload-time = "2026-08-07T10:47:02.45Z" }, + { url = "https://files.pythonhosted.org/packages/4b/07/5888a6d76acd62ebce66c61b74d94e9370b9c32929f111e487bb6546f8ed/librt-0.15.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5500eeae393a184d14e1f35645962c27129d20c81afa4069e6ef826ebc2b3aaa", size = 496670, upload-time = "2026-08-07T10:47:03.675Z" }, + { url = "https://files.pythonhosted.org/packages/29/39/ab57cc2f5b276156da02bb7f5a8921bada1cb1993ffec99acf811c602c23/librt-0.15.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6ecfc32dfb46fb7b565bcd6abf9412acf978775a998273d22888a6d7953730dd", size = 513688, upload-time = "2026-08-07T10:47:04.981Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/bdbb0b648b5c2befb031f4c6f3b1dd857415e8fb492a25a3c764a6681e6c/librt-0.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cc46cfd15022e35084355478c9ac809d90b1152222706ac9a7655ec21df6fa", size = 531904, upload-time = "2026-08-07T10:47:06.211Z" }, + { url = "https://files.pythonhosted.org/packages/93/26/473c2e4b6c104e9e58e27ce95fc8005c8bd4fc36cae4f254371125a92db8/librt-0.15.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5f51401d102c885b9ca509e62c79b1dbff286e1b9b047fde6f763780789356d", size = 524427, upload-time = "2026-08-07T10:47:07.592Z" }, + { url = "https://files.pythonhosted.org/packages/26/60/03b3abb82b41714671b907bf6989b228e31e6a8af52dec82b5b0728dc250/librt-0.15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cc30523e3f1a23fb7511cc659834a0d01a1042bb9de359bc1c131cc4ec6c9656", size = 543155, upload-time = "2026-08-07T10:47:08.866Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0e/9bb1f0a4affbd0a1888f4f79dc03ed2a299d9a2c26c59ab2a97dcbf11903/librt-0.15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:59fe030d8ae4a57e3fb7756bf35a858de74e04066fc8555c53d0af979132af81", size = 546890, upload-time = "2026-08-07T10:47:10.327Z" }, + { url = "https://files.pythonhosted.org/packages/dc/84/6937a280d461f7de6e031ffb02edc2b7c3c90d49d630565ce8ff27cbc5f2/librt-0.15.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a6526a2a956bbb1e4ae3568c82e650fc99119c66bb011ea60715744955a2b4d", size = 555163, upload-time = "2026-08-07T10:47:11.798Z" }, + { url = "https://files.pythonhosted.org/packages/bc/95/2a2853c1ee014bf102116e7f897a04beeaeb2461b45b79af98bdfb95f1ef/librt-0.15.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:85ea21ec6730194d67156b0e0b5430ccb1d61f8b8b907e39b37f9812b74a13f0", size = 535812, upload-time = "2026-08-07T10:47:13.279Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4c/cf9601c1b4c5f09280acd5d83abdb2e68527a2be8257136eb42304218622/librt-0.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e47b8ba865d7ede071a91a7163073bbaeb72541f1ef8a07d512c45c7b5007f2", size = 573688, upload-time = "2026-08-07T10:47:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/47/6d/9ac7cbec46189a7625af4b5acbd25f10d827f4141b2002181848c8418923/librt-0.15.0-cp312-cp312-win32.whl", hash = "sha256:a5207ec414d1c4a2a7231b2086970dc036f94293cdf338190984958a013a42f1", size = 106138, upload-time = "2026-08-07T10:47:15.973Z" }, + { url = "https://files.pythonhosted.org/packages/38/d0/2ae99c83be86ce23f925ac1aeeedc777e97f427c4a8d190c70d0a16e9a87/librt-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:73b30cfa976659b3917c8f6153bdb0591c6a9ec6583599fd24a689b690622022", size = 126974, upload-time = "2026-08-07T10:47:17.049Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ef/dd24f9635c730b86b87587967dda7516b1845e8b17684603d31607fed598/librt-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:a54cf9e0ef47b96af580849db5471142200568ce1e02cbf416addab551369570", size = 112292, upload-time = "2026-08-07T10:47:18.222Z" }, + { url = "https://files.pythonhosted.org/packages/e7/42/467b53a601b406ccd7b97c1fd54b59cb34f9185ad5ce7e9d5c3c4e8961c8/librt-0.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:db13ca398005abcbe538deda87b686d9bd08b7001cf40c4c06b444960ae10a26", size = 151029, upload-time = "2026-08-07T10:47:19.312Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e6/36c2299b7a94b84fdd01220d8a777a71be5be0925bb0dbdf71c0a06a34d9/librt-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa1f1995789dca3698bc550aaceb09a51bd5df0a057ff84ff15296cd1975b801", size = 155194, upload-time = "2026-08-07T10:47:20.398Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b6/ed5071f9325845e670bd36012757419767fbf56af77ed483077b9e4db541/librt-0.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55456ea87d8df21808446d03817be2f65e20391c1c615d9187440dff28cd08dc", size = 502568, upload-time = "2026-08-07T10:47:21.652Z" }, + { url = "https://files.pythonhosted.org/packages/7f/81/6450c67c3615d87704bcbc21323fafc69c799b06a044c447529f725d4b01/librt-0.15.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5a86a5a08c2235316bdb359d5dbb6ce0abfca7fac06363103e2c5af571d92f95", size = 496153, upload-time = "2026-08-07T10:47:22.925Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d6/5f52b722bc75076954b3bfd49be15ea362df4d580c6fb315d0f617100d30/librt-0.15.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56b6a368529bed262da40ce13f8fef590db0479819cca84f16a1f01ac356d0b", size = 513336, upload-time = "2026-08-07T10:47:24.213Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e2/c08fd1d36ce63ea5a12b85c5d37f4550b5f86a692167e41e5a74222607ae/librt-0.15.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:234d8d394721fa0d786af15ebf1f3fb7f3ed82fd1cd0cde45c2f247b5d4281d2", size = 531661, upload-time = "2026-08-07T10:47:25.507Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d8/d9482fcbeb177b9eb87bb3899eeb3b42be690313c652f9e146b1d0681fb2/librt-0.15.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8363d7accb0286ac3a0e633f396e93800dafb8150494505daf9515bbda591f3", size = 524487, upload-time = "2026-08-07T10:47:26.79Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/075171517b41f861753034fbb151b42cfc83bcc853849f24f5e66fd60ccf/librt-0.15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0f0ee3644d951f31055ad07d77d92520e84505dd7a432cc4cd501dd70ee06785", size = 543201, upload-time = "2026-08-07T10:47:27.999Z" }, + { url = "https://files.pythonhosted.org/packages/b0/03/42c2330f37eeb475b6affeedd06518f60035f323af3a839335e3fc9fef2d/librt-0.15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cfd1a81a648806e6a7717be4cc4d1bb392fa229752bf8444ba365e381e984d6", size = 546467, upload-time = "2026-08-07T10:47:29.396Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/1ad4c5638f7e64d8560328bd25c54b409a661bdb6ff254b38ff90744288d/librt-0.15.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6cd22c9da0d866558e46a041f1cc0c2bbb26b61b137b2347fa834c332e1d101", size = 555139, upload-time = "2026-08-07T10:47:30.815Z" }, + { url = "https://files.pythonhosted.org/packages/49/41/39fa7d15db1204cd1cbe6514680fbdc243adf754a0885061308f43afc013/librt-0.15.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6d5225ef8801e4ea5e482fa9b5dfb891dd9ef6f6d870f1f25d449ca2c70ac218", size = 536050, upload-time = "2026-08-07T10:47:32.222Z" }, + { url = "https://files.pythonhosted.org/packages/1e/88/c6dcf0dd8e26dc0c9a499a2abab8646c86dcaf9ecea9524cb46d3686331a/librt-0.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d28a05796b99f749bf8794f17ba9ba1612d0076b802e9cfc62c554634e9ce3b", size = 573700, upload-time = "2026-08-07T10:47:33.527Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9b/ab54c71a7918a7c34fa5327fb61390a77446a07a146fbfb1165250a61035/librt-0.15.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:2067ff438048cead9d223ca5675bae2a25e520a7c3e6c1498bf9c6892d22caab", size = 82194, upload-time = "2026-08-07T10:47:34.835Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b2/4f9a243bb892395f3becb80789ade13771701091f9f07ab8230247953ba8/librt-0.15.0-cp313-cp313-win32.whl", hash = "sha256:1cd3b721f24c206398b9e26da3c3a9c011e6e89d06f318ba8ebefc30f1003890", size = 106231, upload-time = "2026-08-07T10:47:36.251Z" }, + { url = "https://files.pythonhosted.org/packages/bf/af/64aff4885a40b93132382f2c314647d722574605416504379184ef3045ea/librt-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:f395a4a9a03ac062dbe9a9f82e0c720502e590a38feee6a757bc82e9c63afbd8", size = 126996, upload-time = "2026-08-07T10:47:37.453Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/335bccf6c7cb9028cb0b54aead27d9ece3f01f83bc6baa2abace5da655c1/librt-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad", size = 112188, upload-time = "2026-08-07T10:47:38.729Z" }, + { url = "https://files.pythonhosted.org/packages/a8/93/949053fb462eecc4a9a5ee770a81f4b40be7b79538b245545d4aebc6b58b/librt-0.15.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f5de7feedc56337a088eb15cd9fafa9938367362221d8cc62c642b7f94821993", size = 149833, upload-time = "2026-08-07T10:47:39.86Z" }, + { url = "https://files.pythonhosted.org/packages/61/ca/8281aa6cd560a3420e4497729f6b704b53be3eeaaef82d5aeadddaf7441f/librt-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c0eb900c0e91f4aebe680845242e614f1864edfd44106380d0752ac29522bf8", size = 154088, upload-time = "2026-08-07T10:47:41.065Z" }, + { url = "https://files.pythonhosted.org/packages/dd/02/1a1662dceaba6a086360891448d5ce9a7d3555976cae59a31a39d744b9c7/librt-0.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8c9a650a188e38bac005048cbe6342e81407782944d01934540ab75e417df21", size = 494215, upload-time = "2026-08-07T10:47:42.388Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/99211619dc656370a3740c33d2b0b6d5a3fb1e73689314f6ed477a397dc4/librt-0.15.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:92bfed8deec93df30286b9fe9e3b1dd17329cc076a192b4ee5ec223841d54953", size = 491173, upload-time = "2026-08-07T10:47:43.683Z" }, + { url = "https://files.pythonhosted.org/packages/d4/aa/5448d0b05f4579b635d3899176817ebf561af0e57bacd425b5b1887264c1/librt-0.15.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec4b19788f835711a2072f9dbe6b03b3bf32ed1f0fb30cf399bdd59d9f0c33fa", size = 505512, upload-time = "2026-08-07T10:47:45.314Z" }, + { url = "https://files.pythonhosted.org/packages/95/82/01940e40b83c43a546c4a3c896cf34ca272a9690899d55914e4827b3dcce/librt-0.15.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4c7bacb70930f3d0a56f4ecf1be474a1f0d941b01dd73b756f3c256d42cb879", size = 523073, upload-time = "2026-08-07T10:47:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/88/fa/759c0030f3ee371439eb26de34fc745807caf0abb878af7af4b8b7c3dd3d/librt-0.15.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e79f05e4a08b4d880342673312bbc895b56df7765605796f15902eb5367d3ae", size = 515080, upload-time = "2026-08-07T10:47:48.319Z" }, + { url = "https://files.pythonhosted.org/packages/0b/27/894e072228fcb159703c655da69f8cd10dbed489c36e3df7dd032a2483be/librt-0.15.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a417149c0cba4d50b61e992e5a15e69eaf96746609b461cc4ed168aeef6b79dd", size = 534164, upload-time = "2026-08-07T10:47:49.875Z" }, + { url = "https://files.pythonhosted.org/packages/98/a3/0078e91c1f36f8815db17827de15650b9a3fe56c55fbf998c854b34e40d3/librt-0.15.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:da7a94d6a3411f579d72aa3e3bc5fbca7ed4549f3dbd7e5de3aa567333374285", size = 540616, upload-time = "2026-08-07T10:47:51.408Z" }, + { url = "https://files.pythonhosted.org/packages/86/33/81a29b796dd52a45e9ef7974c7732926e8f10f15b8d2be505665979f896d/librt-0.15.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:856f743ae607f2c1380eccb566c0038a9fb3eabf0fc2be2704d76d9f73557239", size = 545890, upload-time = "2026-08-07T10:47:52.818Z" }, + { url = "https://files.pythonhosted.org/packages/05/82/8be1baa1350e5d30cfd70ae79d0a6f4dc5862ef47f7bb2808aabc9bb86e5/librt-0.15.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:779a6e7c894737e5983e7790a9c78c4000c30e23c9aada08081bdbea53b0fa60", size = 523287, upload-time = "2026-08-07T10:47:54.165Z" }, + { url = "https://files.pythonhosted.org/packages/c6/4f/d1be6a01a35c20ef734e0e44113f87d4af756a9354a89dcfbe3b4f8af5e1/librt-0.15.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96bb17dbe8bab3c0954fbebfc69ed395599de75b6bbc35e3270a878e15d4dd65", size = 565868, upload-time = "2026-08-07T10:47:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/67/88/649cfa33f5825927b160610f670bdab012a64d627eddb94fa795ea4292fd/librt-0.15.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:7220697efaa6e5348fc3d18ee7f8563d4bfecd9872b37ffb915bfc1d08840622", size = 81619, upload-time = "2026-08-07T10:47:56.886Z" }, + { url = "https://files.pythonhosted.org/packages/22/31/8e88a8d5e48fc8d1a817787fb6811dfff6499acd6c8683dd83934aa6ede0/librt-0.15.0-cp314-cp314-win32.whl", hash = "sha256:f54598964d357b1c5ab77cf5d92f21e598fe0e23cdbe9618480807f81b4eba15", size = 100138, upload-time = "2026-08-07T10:47:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/80/92/20fd6c4b6a1b1a564b076d55cd3d427d8428217d7638dc25a654cc4791d4/librt-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ff5893a2c23d886aa9ce786de5ac6ddc74aeeaf90743682b74d920e117d2e28", size = 121258, upload-time = "2026-08-07T10:47:59.564Z" }, + { url = "https://files.pythonhosted.org/packages/fc/28/6af430b44d9ebb897b865a3c363b6dcace51357be2347cc0f8f869656a86/librt-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:3722a099730704c9a3d70c879fc0f51daec25fe5f1555672d97bc595abeafb95", size = 106467, upload-time = "2026-08-07T10:48:01.097Z" }, + { url = "https://files.pythonhosted.org/packages/7e/aa/b42bb798942ced219f6d63b27e07f91237887a8d0bd0921666db79a13790/librt-0.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:38c0c7d4b6fc06c3324b3f9162c8391bfc4fd9dde53afe1033ce7edb48d5a714", size = 159523, upload-time = "2026-08-07T10:48:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/75/03/1b53cd4ef904e73b1d828a5f90143bf94a2967d7cfff0b9ccf93e12aa9b4/librt-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8b2fdd7ead3c995c37940a790690660d0ca006c302db26cc51933f6766866fc3", size = 161638, upload-time = "2026-08-07T10:48:03.725Z" }, + { url = "https://files.pythonhosted.org/packages/ac/c4/9f9c9fba097d49e9e694c2b4dc331df31884645ecbc58a93b4b5fc69d2c5/librt-0.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fde98cf1fc4bac144ce23c2c4c017b924ba714509ea9334977b0b27050c837d", size = 701795, upload-time = "2026-08-07T10:48:05.135Z" }, + { url = "https://files.pythonhosted.org/packages/4c/05/0966840bda0380c8ae167b9043c6230202941cc90ea29c48e096964c765e/librt-0.15.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e3b461183c5fa7681b48560f91515f53a953122fb30c71e07abc67d7ddf58c38", size = 682147, upload-time = "2026-08-07T10:48:06.555Z" }, + { url = "https://files.pythonhosted.org/packages/18/af/1c47ca573c30ea47d195aec26133af522fea1104afaace028d7b32247ea8/librt-0.15.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4bbcc257e3babea20a91715c361b24554ec4e8f51aa578568afc230799fe1a19", size = 696397, upload-time = "2026-08-07T10:48:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/2e/0f/1aed6223d4f9f9d1171a8596ff100ea4c3f7699fea7a4ba657c3e60daa6c/librt-0.15.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b845b8d48088fad0cadc84be4b8fda63203be7e9237b71015b3925443c1f35ab", size = 722542, upload-time = "2026-08-07T10:48:09.569Z" }, + { url = "https://files.pythonhosted.org/packages/c6/22/9e3a929aea456c97d69e6ef3884efea56d4807f97399471cc946baebd8af/librt-0.15.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b30e600e8f337b9bd7f39b86d9fdfedc73cc46e3d0f745931a23a234220bb7e2", size = 729709, upload-time = "2026-08-07T10:48:11.129Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1b/c327ef6018e3a9ca0b8e7c5eddeeb331ba8f9b76c24e126d37d0f6d62faf/librt-0.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:64b0c8c35aa4c4ed79896359f3e0b285cbe4e610042106500da4811c322cc108", size = 752891, upload-time = "2026-08-07T10:48:12.558Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d1/d5f1ea02c56930087009e39db9b70660a663e76c730b27b925d786718457/librt-0.15.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0da0d94cb802f32a0524653e7201f2cef72d5f700a5407678f5290483d4fcd08", size = 745301, upload-time = "2026-08-07T10:48:14.55Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3c/5f7c585d15ebb2250c73e7c0ee4e9e47be72c65d520c07ddbcdc62037674/librt-0.15.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4a6369168d371207339b1e50d4532b06a7121586141f82599505a3f315751d47", size = 747921, upload-time = "2026-08-07T10:48:16.453Z" }, + { url = "https://files.pythonhosted.org/packages/7f/52/1443a446486eba966bcbca1696b472e4f210320ec42f490a47f48fbf0fdc/librt-0.15.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c434e072557ade9cbc642d052c89d031efe47d5c9614523619d0d74a02378e81", size = 727561, upload-time = "2026-08-07T10:48:18.089Z" }, + { url = "https://files.pythonhosted.org/packages/79/91/2270a9380f11725cf83ce1925a5e32dd1dde2be9bba597f25c10a38644e7/librt-0.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7eec6a42018bc1d45763b1c162d3d2bf7c3b9a1b0ed30d3e91dcba390efefcc", size = 774417, upload-time = "2026-08-07T10:48:19.611Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/f4b1548d4f5b99186737fe27aec238e9823e8d5d23bf4df007c030689dc5/librt-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:6912fa5e635d74529ac7cdb1bdf6ca3af4453da8d1edbe0110ee1cb4ad407ebf", size = 104381, upload-time = "2026-08-07T10:48:21.048Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/134afad262def1de04c0843c376d02135f1168af43f22e09a52bd8394727/librt-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8e11699ed745931c395acd3621b07062e0f840efa6935aad87a64ed0995f0915", size = 127034, upload-time = "2026-08-07T10:48:22.561Z" }, + { url = "https://files.pythonhosted.org/packages/99/5f/1b6846b20572bd699c9e9ec321a5f781845bee477df2aa2a43b28bc40119/librt-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5d2a91724463bfed4f573cd7a9fdc856d2e230d0c0e5a61416a93481dccd8605", size = 110827, upload-time = "2026-08-07T10:48:23.804Z" }, + { url = "https://files.pythonhosted.org/packages/c6/44/4de9f4ddadb009a55c7758eb5736d62534a7daaf27bd71bc50e64b606b06/librt-0.15.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8443e38dcfcfdbcf5add5118c623efd788d65ac2e25756d6251a54a06a4d0aca", size = 149843, upload-time = "2026-08-07T10:48:25.148Z" }, + { url = "https://files.pythonhosted.org/packages/1f/eb/5d9ab71e30119c44094e0275f38b47dd327aea0f843a080396677029d508/librt-0.15.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6d15a29033c57490cfe2069097c6fc4049e4e65ffbb749be7dc453b7c4c68965", size = 154510, upload-time = "2026-08-07T10:48:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/8505d1b8f5e8c19587bd03f7429993b3e9ce5c06819d856bfb11d919374c/librt-0.15.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2c05c729b589e734c09578bf5964be48a911765484840d017bbc84f49d4c4ad", size = 497543, upload-time = "2026-08-07T10:48:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/1d/9a/3a8390775cb095765aded027ac9c63e7c8ea74e731498607544c6505de0e/librt-0.15.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:fa60887537e1d0cd2d9982269d33a709bf54b195cd2b9364fc0a758022af5bd9", size = 480452, upload-time = "2026-08-07T10:48:29.531Z" }, + { url = "https://files.pythonhosted.org/packages/e7/40/258a4a7117ee915d66de5cd9b8ade65a440993161107ce3a686f1859955c/librt-0.15.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d8bc24219b24c0af375718942ab75e3544b2763085f40f965be4326734ae8328", size = 507768, upload-time = "2026-08-07T10:48:31.007Z" }, + { url = "https://files.pythonhosted.org/packages/6b/c6/2f4dd296c97a0b85b98894519b279408ec9dd602d4f692b1ea0e25dee670/librt-0.15.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86a21a7bd3fe3a419512ef424cc1c020f6771d0b29cfddff36d1635a855e63f0", size = 525122, upload-time = "2026-08-07T10:48:32.7Z" }, + { url = "https://files.pythonhosted.org/packages/49/dd/29eab42be13b2bf0ea8cb227135a45d44693e30a7e8b92871981ff56b82b/librt-0.15.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dbab647e88d90b3167b91efe7091e248653688ed4337e4f90907a722c7361bb9", size = 520371, upload-time = "2026-08-07T10:48:34.294Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/4bad71adeca8fe208b775c2a35417fa5a2584c8f4791daaf89a89450fea1/librt-0.15.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d8edcf6f550e918dca779c069b9e156385c60b406f99fc7641f32c52f7193659", size = 537258, upload-time = "2026-08-07T10:48:35.88Z" }, + { url = "https://files.pythonhosted.org/packages/4c/63/59dba6143fdcc7240c54458b629f3250000a61b8945890fc9efd451b19c5/librt-0.15.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:8b62076030baa2d8b1501a46bf0e19c27a489aa90671c55665bff7887f7660b0", size = 527432, upload-time = "2026-08-07T10:48:37.466Z" }, + { url = "https://files.pythonhosted.org/packages/ec/21/21a24c6a2327d8362580efebe77286bf47b0f4062ec5ea41766e609d3c7d/librt-0.15.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:d00d20d1818e82a07a0ee0aa89a98b17ed7916b92441090b683719cb20a59b6d", size = 548108, upload-time = "2026-08-07T10:48:39.384Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6d/fc68c89a7971418b41f9a873623ff935cb864097544c6a2f8ce491c8ef5d/librt-0.15.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4e6ee93fc3cf848dcbf0cce2eca73d8e7dcd0cc2b6df3a529d57750b30a4c55c", size = 529681, upload-time = "2026-08-07T10:48:41.392Z" }, + { url = "https://files.pythonhosted.org/packages/65/7e/c2d98766124400d722063a630b0fde38a9fc768705d37eecca15c47dc192/librt-0.15.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:32896a0af72508ea979e0acb4e4c04cbeeae04938167950d535c83c45597167d", size = 567736, upload-time = "2026-08-07T10:48:43.124Z" }, + { url = "https://files.pythonhosted.org/packages/55/6c/f8c34a95e3a515c6e1c192b89511e7253c89a7760c6b500d57ffdb8d2dc8/librt-0.15.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:ec3ba415afaf951f6951b1dd16d3c8e4f540065fc382d7e70b823a79567ca374", size = 81673, upload-time = "2026-08-07T10:48:44.645Z" }, + { url = "https://files.pythonhosted.org/packages/c9/9e/e23fa8e78679ec45728188650b39e8ff476c83b691c96f749217df3b1b7c/librt-0.15.0-cp315-cp315-win32.whl", hash = "sha256:d2813ba2503764f0450680c533d13df7cff9b49df1411062eded5f67db4195b9", size = 100081, upload-time = "2026-08-07T10:48:46.171Z" }, + { url = "https://files.pythonhosted.org/packages/e1/dc/3eb4c5e297343f0620a55532cd7c8d764d3001fa2159212dadf480464827/librt-0.15.0-cp315-cp315-win_amd64.whl", hash = "sha256:b87d67e33afaf265262f2a66db578284b88ee2e6fcd224579cb5c15518677ad8", size = 121228, upload-time = "2026-08-07T10:48:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/97/70/43abce19f04e49762f8ec834c8fafee13cc40fd6b94a72a24e534febfcd0/librt-0.15.0-cp315-cp315-win_arm64.whl", hash = "sha256:713bd7df21170b982e729e46870f31d6b437bd1a9b4648cffb529bd3c2ec5c4b", size = 106487, upload-time = "2026-08-07T10:48:49.095Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/83f2deddb9368b8951ec8c9477269b5b9b8bd9bbf15e57402d0f38817dca/librt-0.15.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3de789c82752730f94782a5ee518baf9c05edf85733aeaf73bb6e518755cdf54", size = 159448, upload-time = "2026-08-07T10:48:50.649Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/043097353f9b3c73b583d07f6b8e552795463f4bfc8caf85e42eee50c26a/librt-0.15.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:e0b5deec9a8664eb722c797241970fd4aa1894d25fda36a1ddac0f7407606bd6", size = 161686, upload-time = "2026-08-07T10:48:52.174Z" }, + { url = "https://files.pythonhosted.org/packages/f4/2a/8ae77f9719d42ce71cd708560a3557b38ac3c17a0383e57f87084de45bbe/librt-0.15.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5563302a8359bc2295bb7084d1a8ed1519df96afb30eb2aa4e0bff7b54228988", size = 710668, upload-time = "2026-08-07T10:48:53.782Z" }, + { url = "https://files.pythonhosted.org/packages/61/34/c0436ea134deb9a0d6da80a396a2739a81cb31e0418f7227239e23140898/librt-0.15.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:22d6263b9d39d7bbb286fa791945646e3218f1be2d693e36fb630f1d0e59cd13", size = 679396, upload-time = "2026-08-07T10:48:55.645Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/001e0d99aa9250d5cd5715a9081291a20656083459f9019cda15255329e1/librt-0.15.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39ffd14646190c454f0d86e0d256b33f00a87a26ab410e619773b841d0e41416", size = 704313, upload-time = "2026-08-07T10:48:57.46Z" }, + { url = "https://files.pythonhosted.org/packages/2d/53/b34fa9d0ff00f136f4d58ebb4c411ff634baed1eb412bb602a2bc8dcafcb/librt-0.15.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47318cd3a61401452de11282242937e3e057c4fd3dbaf601e269d0928a06c0a", size = 729847, upload-time = "2026-08-07T10:48:59.231Z" }, + { url = "https://files.pythonhosted.org/packages/86/ac/fa4d7a424665040e95baf480a6d523446057684b6758624c85338e8a23b2/librt-0.15.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a56a1d4f859a82ca5b99fc4b82c9b027b15e3c455c5cd99e7d0719f27bb20b6c", size = 742736, upload-time = "2026-08-07T10:49:01.151Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/e17a9bb5de6fb8c3186ed1a7d68d21618b027ac2d3633e03d3b6109c67ae/librt-0.15.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302", size = 763454, upload-time = "2026-08-07T10:49:03.039Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ec/ecd02cd30935b931b9cdbfed6ab5a099c51b280b4e7baa274da80978ed27/librt-0.15.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:411ca4d1b905b860ceba7570dd6717a71dedaddcc4b0f77ece710aa41ee11f8d", size = 743296, upload-time = "2026-08-07T10:49:04.941Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b5/b3c2b8353ce820a4854f78d19321344242f89fa71c975b71132ba9bf242a/librt-0.15.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1256589e0b0adb31751d685a68bce29d73407ddf4ef05d4188f49d5dcf9566d9", size = 756217, upload-time = "2026-08-07T10:49:06.825Z" }, + { url = "https://files.pythonhosted.org/packages/3c/52/6cc22542ba59146b05cca2a656f9ff8bb67e38e63d12c3b0cc183d837bf1/librt-0.15.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:f42b74a53e5f26a0ba0007411a7455b66c67ce4022a39cc1f56fc4efd65bcbab", size = 741934, upload-time = "2026-08-07T10:49:08.839Z" }, + { url = "https://files.pythonhosted.org/packages/40/32/a04b72b1aa86e3be23b2ecff8c1aad2dcc955bd3956d6d26e7e34267e57a/librt-0.15.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:291bf73caf78b9e88d6fae9bfd693207ff7d832e2fdbe2cf8e746bc13f5f892b", size = 783763, upload-time = "2026-08-07T10:49:10.661Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f0/89eb11dffbe9279ff37144dec786927314502ae0b114f1449dc78c458aab/librt-0.15.0-cp315-cp315t-win32.whl", hash = "sha256:c16d15ee371643ab48dc8248a3e680ebbeca573a13af2c3dd0c985b142d77162", size = 104313, upload-time = "2026-08-07T10:49:12.305Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4a/1f1978c200f563beda63c36adff2d65bbecb81e365e8e69e572f5f70fbc6/librt-0.15.0-cp315-cp315t-win_amd64.whl", hash = "sha256:dbd605739f228912dc49027cb764456b9757750bdc2b6b7773164db7096c6fd1", size = 126889, upload-time = "2026-08-07T10:49:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/38/a6/800800bfed7b1fb10fc3f3d557785c3854e80d3f7a9800d784b176a1fc2d/librt-0.15.0-cp315-cp315t-win_arm64.whl", hash = "sha256:84d244b00604d17df3fc7736c327892d6bba66181254aa4087be807b6c342bdc", size = 110700, upload-time = "2026-08-07T10:49:15.499Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "marshmallow" +version = "3.26.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/79/de6c16cc902f4fc372236926b0ce2ab7845268dcc30fb2fbb7f71b418631/marshmallow-3.26.2.tar.gz", hash = "sha256:bbe2adb5a03e6e3571b573f42527c6fe926e17467833660bebd11593ab8dfd57", size = 222095, upload-time = "2025-12-22T06:53:53.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/2f/5108cb3ee4ba6501748c4908b908e55f42a5b66245b4cfe0c99326e1ef6e/marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73", size = 50964, upload-time = "2025-12-22T06:53:51.801Z" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +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" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "niquests" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "charset-normalizer" }, + { name = "urllib3-future" }, + { name = "wassima", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/1c/836ad34fb18fac781722019100e2bffd81eb9a0011d7a91c434a7094460d/niquests-3.21.0.tar.gz", hash = "sha256:5b7d10a05f4c7ed08cede0af74f492ae7a8a5a71291833d029e23365fc3ea80a", size = 1070211, upload-time = "2026-07-29T10:26:24.993Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/9b/c08403f905f9aaffc74b483fbb0ad4cc372d1da22cccb4ca771a3e65a185/niquests-3.21.0-py3-none-any.whl", hash = "sha256:dbea441b9e9d5d755e02caa2b57b94cdb97b23d754ead21b80a4ac18ef66805f", size = 230382, upload-time = "2026-07-29T10:26:23.194Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.11.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/98/0bf930c4f97d0266b58a89e36c015f56232c52b5d2f207215d48cca9e8f7/platformdirs-4.11.2.tar.gz", hash = "sha256:3a2ae5fca3520a01ab1be8b45613537f52ddf5b5f6f53d88233892dfbf0cd82d", size = 32716, upload-time = "2026-08-10T15:48:06.092Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/e2/4e6eee633809c376c024821b91ade709cbfd040ec53939ffbcc292aa7eee/platformdirs-4.11.2-py3-none-any.whl", hash = "sha256:7f89089b6ea71bda7962953edcf784b2e2d9d285b40ad88be2bb75c6e9d82ab4", size = 23361, upload-time = "2026-08-10T15:48:04.855Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pylint" +version = "3.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/4c/b561478a1ccb91e9b02965cb999d2281894d43e68c0bf3777d023af15f11/pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8", size = 1505895, upload-time = "2024-05-20T07:22:43.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/23/7a546224d2931cda031ee3cddc9e723650ad8e491d7c64efbab97e43e16d/pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4", size = 519092, upload-time = "2024-05-20T07:22:40.191Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.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/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042, upload-time = "2024-03-24T20:16:34.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990, upload-time = "2024-03-24T20:16:32.444Z" }, +] + +[[package]] +name = "pytest-runner" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/7d/60976d532519c3a0b41e06a59ad60949e2be1af937cf02738fec91bfd808/pytest-runner-6.0.1.tar.gz", hash = "sha256:70d4739585a7008f37bf4933c013fdb327b8878a5a69fcbb3316c88882f0f49b", size = 16056, upload-time = "2023-12-04T01:03:30.835Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/2b/73982c02d28538b6a1182c0a2faf764ca6a76a6dbe89a69288184051a67b/pytest_runner-6.0.1-py3-none-any.whl", hash = "sha256:ea326ed6f6613992746062362efab70212089a4209c08d67177b3df1c52cd9f2", size = 7186, upload-time = "2023-12-04T01:03:28.706Z" }, +] + +[[package]] +name = "pytest-watch" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "docopt" }, + { name = "pytest" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/47/ab65fc1d682befc318c439940f81a0de1026048479f732e84fe714cd69c0/pytest-watch-4.2.0.tar.gz", hash = "sha256:06136f03d5b361718b8d0d234042f7b2f203910d8568f63df2f866b547b3d4b9", size = 16340, upload-time = "2018-05-20T19:52:16.194Z" } + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "qh3" +version = "1.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/ae/9d42d6df0ab9a014138332346fd690f7b0be0556861421d2459caec28d6f/qh3-1.9.4.tar.gz", hash = "sha256:bd2ea9baf19656c544a48a56a195f2ac257cd973b566f5f2998fa3b7446281a1", size = 346724, upload-time = "2026-07-13T06:36:32.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/b2/7a8f6dcb93a7a23db07c556fe57b167be7b880dd1771d3df62ab1dce4e56/qh3-1.9.4-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:66567176a55bc92f12a7cdaae6d0245f808333a6a55221f37577f79c56848b71", size = 4359072, upload-time = "2026-07-13T06:33:03.308Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ef/343c02a1fe2ee1215120483fee965eab0ee72ea4a9ae122a9b31add14ea6/qh3-1.9.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a931eba5b6b11292ed65e164a416b78a50139fb9c9dc9dce90006cbd853d1fdd", size = 2159084, upload-time = "2026-07-13T06:33:05.531Z" }, + { url = "https://files.pythonhosted.org/packages/57/c5/c92e03e4711a1b3e09924d654f3c17dd62b91233302a77ce5695215a2b5b/qh3-1.9.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:325ee1f729e7b2f8600e71e354715db18ec4e669a001449f5924ba0521b9bafe", size = 1862100, upload-time = "2026-07-13T06:33:07.147Z" }, + { url = "https://files.pythonhosted.org/packages/73/9d/88f5088430d25f3a0bc414b15b2b0b22a117468e49bba981c140337bb90b/qh3-1.9.4-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9b38c9e5c6552828118676795b0a270e2337571e25d17bf57c18c2c5c346df5", size = 2032620, upload-time = "2026-07-13T06:33:08.738Z" }, + { url = "https://files.pythonhosted.org/packages/49/0e/26470349a310715c05776c26e289e54c6a49f08289249445d5a660d364bd/qh3-1.9.4-cp313-cp313t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9cf35ac8f4133a88ad3d8e35d63bfd566f369071a496a16ed69ae09b01b9a64a", size = 2034678, upload-time = "2026-07-13T06:33:10.398Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ae/ad55ebd12852e5c714e1c2956c4497d6d09417e9af5c66eb672b79cdd7f6/qh3-1.9.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e5458cdc9e5214b111d13b86207c2ac799e1191d2e084c442d4fae1ec73fd9b", size = 2032574, upload-time = "2026-07-13T06:33:11.973Z" }, + { url = "https://files.pythonhosted.org/packages/2d/27/62b09bdee46d603fd581bec43c98a86c9380f2722d10cb7885d623c1d4e8/qh3-1.9.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9486a8dfc1d8eba625e7f92f3a1c45e4a131c7d96d33b73c20d584cf6bb99055", size = 2089542, upload-time = "2026-07-13T06:33:13.591Z" }, + { url = "https://files.pythonhosted.org/packages/96/b4/b89476bfd17c47c29425f3b9cdf5bfbe27872853c1d0a53c9315be05c89b/qh3-1.9.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3e2e90656a46c49ec04e920fbdff28a14eb4439fb660e5de3074c3fbab4ee13", size = 2367301, upload-time = "2026-07-13T06:33:15.142Z" }, + { url = "https://files.pythonhosted.org/packages/46/a9/f21458c4ee1b527030f3d23d67ecf97caa75a8a9c0e021ddaac02083413a/qh3-1.9.4-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:4555d49cb3167904a48dec772fb3a71279282c05e70cc2be9d59b597d1fe71a0", size = 2015315, upload-time = "2026-07-13T06:33:17.003Z" }, + { url = "https://files.pythonhosted.org/packages/df/9f/30d83600b6f6bf8a3a995e05b46578e333c8ea5e0a799ea28d5f6f28c262/qh3-1.9.4-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d5b6a7af8eb57dd168c023ffb041274b35b120a8eddf0a37f9bebcf4d69bf3c9", size = 2349006, upload-time = "2026-07-13T06:33:18.962Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/7dc1ac91848f2774c68935da37ecf5e5d069a02b4f98934a74f8868a531a/qh3-1.9.4-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:593165d62fb259ee9a756e1bf07aa66cb445680e5bbb7fa15d8d3ba59dcf923e", size = 2133242, upload-time = "2026-07-13T06:33:21.12Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/d7d59c1474be53394555b575614eb086d10ed23b7ef3d83f93571ee40fd2/qh3-1.9.4-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:2369dd42c97de4cf628503fd5c02b359b23cabd0ff1773d33b135395748c6e42", size = 2230318, upload-time = "2026-07-13T06:33:22.584Z" }, + { url = "https://files.pythonhosted.org/packages/3e/42/eeca764d3e78b6a3910f95bd33af638f15fc88c6af98807abdc9fb0d75ec/qh3-1.9.4-cp313-cp313t-musllinux_1_1_riscv64.whl", hash = "sha256:eb99f81ad441f64f29da560a0619c9e5b8d9f4532e611c1d6319b536a9d36f53", size = 2125385, upload-time = "2026-07-13T06:33:24.284Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/41d6e4f8d06052873a590557769dfce8a14627e1cc03f74600313ed0c552/qh3-1.9.4-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2a875175ab7c03d06fdb6784bf207d6aa1d30d2bef8170cde49b6960a0ca96a1", size = 2587129, upload-time = "2026-07-13T06:33:25.841Z" }, + { url = "https://files.pythonhosted.org/packages/65/79/05804bdedc1be747e319091ec770cc8ce5499008b16a70ce2cde035d2558/qh3-1.9.4-cp313-cp313t-win32.whl", hash = "sha256:72c37f9fcf7b1136f245a333bceb7582fb4dcc955bfad0937ca6c857748b5638", size = 1855980, upload-time = "2026-07-13T06:33:27.522Z" }, + { url = "https://files.pythonhosted.org/packages/86/2c/4eb17c6e4743b8b003ed9c4fffe3acfaa29c000de09a9d8ce6ade1f0b428/qh3-1.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:257a1454732d261b3cf8af90d52cfa13ebb213a899d7983d7707b0c30f4a2533", size = 2122099, upload-time = "2026-07-13T06:33:29.075Z" }, + { url = "https://files.pythonhosted.org/packages/e2/bb/b8101f7a96237516a57b6fbcfebacc2c3349ab0b6ff528784f1c90d69edf/qh3-1.9.4-cp313-cp313t-win_arm64.whl", hash = "sha256:a21c68c0724521fedc4df1abc4402020a1eb37500d138a1bee1c3e50c1b0edb4", size = 1970072, upload-time = "2026-07-13T06:33:30.535Z" }, + { url = "https://files.pythonhosted.org/packages/0b/89/1b52aa142d772479393c4868f41255ddaf29627db72a36c1f635f51987ad/qh3-1.9.4-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:100368855b0e103774438aaf8b38c711fb51b79f18e3b9dc873ea573648bcc3d", size = 4360094, upload-time = "2026-07-13T06:33:32.269Z" }, + { url = "https://files.pythonhosted.org/packages/f3/71/c9dd5b4093999b651869e5277ed33d88fa2ae4eca064896c66319563dd16/qh3-1.9.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9d45e544e0a556380bd2a92af180ee98d32bca6cf622f28350ebc000611bdf", size = 2159530, upload-time = "2026-07-13T06:33:34.077Z" }, + { url = "https://files.pythonhosted.org/packages/ec/74/b4b34a93b1acf1476a757b1c5bac9d38d5c7cb2272d68b3ce1457476101f/qh3-1.9.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43f80d662078c26563a1aac32a4df22649473d996bf2422e073c8aa55b1c837c", size = 1862303, upload-time = "2026-07-13T06:33:35.801Z" }, + { url = "https://files.pythonhosted.org/packages/31/cb/dc3004df9af3922cab529c53bd6b58a7d44cce02f6b6dd7f795d8a3350ce/qh3-1.9.4-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:551f60d487d849d6f2313dda94e54ebb16b1a29acb93676540ae790a56308c24", size = 2033172, upload-time = "2026-07-13T06:33:37.677Z" }, + { url = "https://files.pythonhosted.org/packages/82/be/6952fde329c37b8c2f64548df726bd86b8dfda12df740b244114adc7e60d/qh3-1.9.4-cp314-cp314t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3755d777ff35561bc719f571ebc4b91aaa8a945c210e50b2576bf2cc69cb511f", size = 2035596, upload-time = "2026-07-13T06:33:39.391Z" }, + { url = "https://files.pythonhosted.org/packages/98/5b/67676a616baba1265e5f06ef202779099a9b4926c81342c007528c68a3f8/qh3-1.9.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04b64bb18e10d8f482bea1ef43e5da3448e2d5ba78bb0c735da5b9e02d7dfb0e", size = 2033387, upload-time = "2026-07-13T06:33:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/94/03/6823de539154091b46234c667e49bff9b06bae44a82d14895d813f909fae/qh3-1.9.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b47597daa4ae9195f81a35822217a395d3277f01a393d9c2bb3796a6b458b22c", size = 2090092, upload-time = "2026-07-13T06:33:42.875Z" }, + { url = "https://files.pythonhosted.org/packages/1f/5c/8a3e3b005f66091baf0310a429003733986d9e001d13f3f7781ff3849873/qh3-1.9.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9bb9012bc02c44da6af1b931b6082588519685fad8a1f866c341f749394f53f", size = 2368083, upload-time = "2026-07-13T06:33:45.085Z" }, + { url = "https://files.pythonhosted.org/packages/02/3b/6feff7b78e7cf9eaf3b25af6d7239754c32d62881f9f392bf4d18d60a907/qh3-1.9.4-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:9c7f3781ec022dd958b5eb0e5cb2e2a54e207a3d8837fb7290bc239912648fd7", size = 2015665, upload-time = "2026-07-13T06:33:46.794Z" }, + { url = "https://files.pythonhosted.org/packages/54/24/07692a20232b6f8788a54c7f06b25ce702b4a94a5ad79bf680a9e32fae08/qh3-1.9.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:633a24ab2db76d64881876c9d0f1b226a47d892bd7d78e61ea2c788ab59570af", size = 2349383, upload-time = "2026-07-13T06:33:48.495Z" }, + { url = "https://files.pythonhosted.org/packages/38/15/f447e2ea4216757fbb5627ce96bb79e386133b0cd3e8f3c1b0142d4d1bdd/qh3-1.9.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:f2ff36b84a4ab9b68806af1f6985fa3b47e29695effe766a3e70c21efac8157c", size = 2133616, upload-time = "2026-07-13T06:33:50.079Z" }, + { url = "https://files.pythonhosted.org/packages/54/4a/dc4a346000aeb4d21aabb4b6daf47231556e47d534be3fa4a9ce95ee6d83/qh3-1.9.4-cp314-cp314t-musllinux_1_1_i686.whl", hash = "sha256:2100697d736dbdfd1d3f0266c9fcadea6230b66ed59e3e7f8c9a9d1a6eec0c65", size = 2231106, upload-time = "2026-07-13T06:33:51.678Z" }, + { url = "https://files.pythonhosted.org/packages/38/b8/6090e97b28c7486d045309ebf010f8bd79a94b5838edeb7b39080ef8b1ad/qh3-1.9.4-cp314-cp314t-musllinux_1_1_riscv64.whl", hash = "sha256:9ce179efaf97f927f01fe158619f5624d8d874c7161b7c497f06897cdde210d5", size = 2125844, upload-time = "2026-07-13T06:33:54.318Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b7/a8d8d0568758ac85e87003aa550b7557b502eb1b600309abbdbe6246294b/qh3-1.9.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:476ee4fcdbd3454eb0eb4dee5678294f06c08ca8e8b83160e2d0ae3c4ae34603", size = 2588008, upload-time = "2026-07-13T06:33:55.915Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ab/318fb071ed092edc1b6572066157b6a029c15c42369520d7d7353a126334/qh3-1.9.4-cp314-cp314t-win32.whl", hash = "sha256:fe9df8136bf358d45a2757287523abaeb208fcbe2c1e34664562eb2d8f64dbdb", size = 1855852, upload-time = "2026-07-13T06:33:57.59Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5f/2b617597c665739ee6d0b190aef22df378e759a810a86fe1de7acab31857/qh3-1.9.4-cp314-cp314t-win_amd64.whl", hash = "sha256:1fbdf53feb1732faf6c1cb34f2e1cbe4b543054db7b4d19d8752f1162e9370be", size = 2122462, upload-time = "2026-07-13T06:33:59.182Z" }, + { url = "https://files.pythonhosted.org/packages/85/3e/56694a36751508ec18f66ba115c83a4dad6d576ebbb50523c940eb89b3ca/qh3-1.9.4-cp314-cp314t-win_arm64.whl", hash = "sha256:e03ee1e2157a31c69415c3dbfbfe64e02469499ca46e968d13d8a7b289bf520a", size = 1971117, upload-time = "2026-07-13T06:34:00.709Z" }, + { url = "https://files.pythonhosted.org/packages/f2/48/646ee80a03ef7fac2b70cccea8a8f17c284fbb86fb854227f6ce70898053/qh3-1.9.4-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:26f7c8ed1a7ac0e25b654f030a4f472830c80890cc284dc40a31c17ecc21fbf0", size = 4376855, upload-time = "2026-07-13T06:34:02.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/86/d6777ff057a45debcfeb2a55b21692ce0c6f32a3c3a45531e83cf8ed4ef6/qh3-1.9.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57b3dfe5dd2da2084adea63dba0bf587a4096ebf9207339187c90139d1f460d", size = 2164636, upload-time = "2026-07-13T06:34:04.437Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7b/7ebfd6babc3aaac74fb40beb5f85c289e2dcc40116282a3e48db99558eb7/qh3-1.9.4-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7fa1a3debb5199da3c7f45b0ca6efa55174dfd6229370e774099073143ebe9e9", size = 1865942, upload-time = "2026-07-13T06:34:06.03Z" }, + { url = "https://files.pythonhosted.org/packages/62/8f/bbc0b834b1252356d49d8813156606d426eb7f90ed5bc6aa479bc8aef7c6/qh3-1.9.4-cp37-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dc9ff40d3a06f27b47d93417666f03ba6fd6b9585d9711ebbddf84fa4b7c50f", size = 2038977, upload-time = "2026-07-13T06:34:09.165Z" }, + { url = "https://files.pythonhosted.org/packages/c4/32/fe10f124f2062cb9004c18c1b506d29961e2e4d72d2c20ea1b3503ebe6fe/qh3-1.9.4-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:010b132c33719af3df4c1d6f569e52a2a20046c55a31c5532e741019c8c53868", size = 2042505, upload-time = "2026-07-13T06:34:10.932Z" }, + { url = "https://files.pythonhosted.org/packages/c8/eb/b7b93687c033d4291c8207f47b2b0c7fdb738db89ab5beceae08d866734f/qh3-1.9.4-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:124045b0c04c05afc038a239d7717019b4c4b9d0cb967040b4331f4d588eff7d", size = 2039083, upload-time = "2026-07-13T06:34:12.952Z" }, + { url = "https://files.pythonhosted.org/packages/1b/22/b064ec3fd14f121a8710f41bb42680ed96635b55bc78f2e85d475e8dbd65/qh3-1.9.4-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a94a1726bf46a451a0295d68fafde28794e83ab6fda7457cbd9309c38151de9", size = 2092767, upload-time = "2026-07-13T06:34:14.925Z" }, + { url = "https://files.pythonhosted.org/packages/29/92/54bac0ab3aad24b84f1e3c4868399e42cb8678054650f5f2e436acc2554c/qh3-1.9.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b361e627b6a529ecb231c1d5f3f2abd6d1952b0d7cc02c7718d9657d55dae37", size = 2372367, upload-time = "2026-07-13T06:34:16.589Z" }, + { url = "https://files.pythonhosted.org/packages/4c/78/99f27d776e54724050d41a14762e25e6b8d251b20943b59928f4fca366ad/qh3-1.9.4-cp37-abi3-manylinux_2_39_riscv64.whl", hash = "sha256:43b6cfb2240836e3e54bbe61ad1b8fa5716516ff5359131a2421c55676a24f26", size = 2017823, upload-time = "2026-07-13T06:34:18.114Z" }, + { url = "https://files.pythonhosted.org/packages/9f/f7/ac383f019b69e9c7021e300cde900de36953d44e408fbd323993575ce765/qh3-1.9.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e6db3def610e9c2d2f3602d3e9c8b2156273225f6eedf74a4749d3bc21af8167", size = 2353468, upload-time = "2026-07-13T06:34:19.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/db/d60b95e872368e51756877e98841b77691acb833e89997839a7adfe053d0/qh3-1.9.4-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:22d5879dc93d2c7ee14cc5933b12c7780d9c7705dff4553becea152fcb767d99", size = 2136024, upload-time = "2026-07-13T06:34:21.837Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ed/a84cbc776edb96b8502d9e9093432431927dd827ca2d064b196a43418bb7/qh3-1.9.4-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:2c2b2005fe6c605d61cac05ec2b3c019cc546184fa43444f3d1e3d22ed020ab2", size = 2235665, upload-time = "2026-07-13T06:34:23.425Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0b/ef538579ba7144fadc6865df30bc9ccb3b78ae1dd61899a5da115b0ea461/qh3-1.9.4-cp37-abi3-musllinux_1_1_riscv64.whl", hash = "sha256:e991b0f20b172fea466d0ffd499a85bf2f26decdeeda537275c72b5f7b26ccfe", size = 2128989, upload-time = "2026-07-13T06:34:25.636Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ad/fb9f922d0ea410c7f7f19a746f02be0fab2b5b5c061872ca0965fe767c52/qh3-1.9.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c13775ed27a88b297f6a1aa05ffb286879453f99e1e53e0af2b22e7ed8f92ebf", size = 2593826, upload-time = "2026-07-13T06:34:27.288Z" }, + { url = "https://files.pythonhosted.org/packages/75/a5/aeef82a0edae7003d42c0587dfbceae4ff0ce375f8c2c13398a4aa4a29f3/qh3-1.9.4-cp37-abi3-win32.whl", hash = "sha256:952abe1be90c0114993886792a8a5ec2211f8391df41227f5b7a5a02ff71bc26", size = 1868838, upload-time = "2026-07-13T06:34:28.944Z" }, + { url = "https://files.pythonhosted.org/packages/05/48/77f3055a3790a515fc59b37cdf1f23c374f8a235c6fa1b110153b78668e4/qh3-1.9.4-cp37-abi3-win_amd64.whl", hash = "sha256:5543cb40a48d401f3fa4ec89398e3a213634f195716a9d06d70417c05edbbb32", size = 2130155, upload-time = "2026-07-13T06:34:30.526Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bc/aec1f86da0d093a319f43d88b005b4b71efa94e8f675c2c9764098c2abb2/qh3-1.9.4-cp37-abi3-win_arm64.whl", hash = "sha256:d0bd73b3e73f4f80d2430d410b532222ecf57e9d4fe8600b7683c775cbff9479", size = 1980028, upload-time = "2026-07-13T06:34:32.376Z" }, + { url = "https://files.pythonhosted.org/packages/4e/5a/7c8a46d4ee36b7f18bfa444e63d9cbb6c15141508ae12232ae10bc86a45b/qh3-1.9.4-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2beae7faf80fa9c2c79c29d45e27686ff9d785b46f7da9848c88cce0d867569e", size = 4375023, upload-time = "2026-07-13T06:34:34.247Z" }, + { url = "https://files.pythonhosted.org/packages/61/2a/a096a6fc146806e6848ea1442c2d96f75c5cf6c98bf0136c5b18236bba76/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:817bad9bc133cf6fa393ab6f06ada20d27f8c6c8fce0d64147013f7d802b636c", size = 2162875, upload-time = "2026-07-13T06:34:35.888Z" }, + { url = "https://files.pythonhosted.org/packages/8d/67/8679f6ac6b11a607dad9b7d01ac9c14cdad0d40e372a00821c9185c04e3d/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3154bcc3070d7d674748c7a618ec2718255ba7e8976da4f71c9c6077efc49a85", size = 1866020, upload-time = "2026-07-13T06:34:37.478Z" }, + { url = "https://files.pythonhosted.org/packages/4e/47/e03dbfcbd149346a3e86b4e2051244bdc5bf8d7e7439c1bb392dc879a667/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47f08aa99fd79a3313c0426220541afa266bafe3b966b74f8b54ee3df1ce07bd", size = 2038665, upload-time = "2026-07-13T06:34:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/65/fb/ad57dff7d22640068a4952e36e9ac5d6821dd05130d0779d34daef2cd335/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0738a83037d2a832ca4902110e2a8d45896ea1a5ae102e9127a57fd96f5d3dfc", size = 2040646, upload-time = "2026-07-13T06:34:41.456Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/65f9e1b84b1e56854674beae605a1abe97ecd35a83146b4e8a9ce72badb7/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca757d585d4e235f4f376a43d293db539331cfe7f597b84b405754195f6c1d77", size = 2038019, upload-time = "2026-07-13T06:34:43.13Z" }, + { url = "https://files.pythonhosted.org/packages/12/bf/10c0567b4f3c31d8e8d4fb5dde3a830e2e6446fb47703a9e78f4a48e2f0e/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd763fcd3508d3366bb5c93eadc7f0064b5453c982edac7aed8b0e112f34ae30", size = 2091474, upload-time = "2026-07-13T06:34:44.79Z" }, + { url = "https://files.pythonhosted.org/packages/f5/0a/fdf9a93c8a8d95cc266593e325b8b08618445603f2b4e5308d50e36ee251/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42a8e2a3a313d7f32cd1ebc8c29ad42f967e797660a4d7c37f0c1354a48d7d5f", size = 2371394, upload-time = "2026-07-13T06:34:46.477Z" }, + { url = "https://files.pythonhosted.org/packages/28/34/27e2ddb5d3d901b3768c2bfed17f61136bdb71eb03d338d60af470428240/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a292ef84826309e12b530ef95dd37af28029d7976521eac3441812a0ae94761e", size = 2352089, upload-time = "2026-07-13T06:34:48.452Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9e/f1cca3bf6f155a78586985bb3583b0d538d89ca5d3b8e26160abb8020ed1/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a5df03681010f359e21cf9175cfbaf25e637abe970d0eb6448e78ec37abe9b48", size = 2136049, upload-time = "2026-07-13T06:34:50.108Z" }, + { url = "https://files.pythonhosted.org/packages/a8/82/3d57fd79ec778de3110f04f84c982e3c5bfae9c51ec9fd6e387989daec75/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:53cb4a2d96d1f908821589104fc3bcc5521d3fcd0ef7bd91e2301e581a33fdc6", size = 2235239, upload-time = "2026-07-13T06:34:51.813Z" }, + { url = "https://files.pythonhosted.org/packages/70/cd/37c353f0020fe26e24d6f5bc69a93e4c8d8c602e4cdc10bfaf087340b96d/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9010ed3f82b8f9c4de43da0ce8691f2a0ccb9fc2d64b080f7d99218a6303667d", size = 2592896, upload-time = "2026-07-13T06:34:53.479Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/b9dcfb39ad8d40271f422b8371536dc460de52ca2493c3f0f75b9adbe432/qh3-1.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ddf624234a3cd54672fa081fbac250371683e12595a0328c52e6af84dd01a847", size = 2128044, upload-time = "2026-07-13T06:34:55.142Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/8ca93971288d3dfd2492df8cacb8a346f7c977d446284c9ff3ce19654f55/qh3-1.9.4-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9356db6fe28a3368b27479b8a23409074c9a567b2250fc8ff99373929b4ac291", size = 4368393, upload-time = "2026-07-13T06:34:56.802Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ef/2acba8fbeeb77f1092a0ea29c7d055e37150ea2aecaec223266fa69b114c/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d46c0687461468411ced872d199f2c60a9f8163d96622ab9f4737c991e427de", size = 2160060, upload-time = "2026-07-13T06:34:58.64Z" }, + { url = "https://files.pythonhosted.org/packages/aa/6f/00bc375338f5022c06d60ff34e82b9522fd0c79f58392f855b9b8f1a28ea/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6da6de3744fc8db6915f667196bb8cd0bf291f91f3e9a880e2761950558c795f", size = 1862733, upload-time = "2026-07-13T06:35:00.446Z" }, + { url = "https://files.pythonhosted.org/packages/df/2c/7bc75c988d4a1df3dd16e54347dd2ead43609f8eab2071f72f2e681928da/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c111428fcbbb228284e0efab10df49ef3523cf3bf86ca81ed5e694909b7fb616", size = 2036108, upload-time = "2026-07-13T06:35:02.11Z" }, + { url = "https://files.pythonhosted.org/packages/24/ba/ef236210448a327738a1139dae0fca9ff24df2d5a82bd6a105e794c092c8/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7dca54c459d5ff0e5cf16e9bb8c274270377259e0f8ddad534933a42f840dd8d", size = 2037947, upload-time = "2026-07-13T06:35:03.823Z" }, + { url = "https://files.pythonhosted.org/packages/10/d7/2e3e752a94c87cb7634a060e50cf3ff85f1c8c5d9b1e10708f21d6f5c000/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d25e7be545cb549c59db2b14a09214991ccca804e395eab8fcaff258e2991d13", size = 2035584, upload-time = "2026-07-13T06:35:05.709Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3a/2f55f5a9cb3c99335c5950890c97e062bb2f57eb4f725448d72adfe7dc3c/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73028c91f3b5c177ee0b0de699dbc29eb322f9d6a6892d37d2add50c8ff7f0f4", size = 2086910, upload-time = "2026-07-13T06:35:07.638Z" }, + { url = "https://files.pythonhosted.org/packages/dc/91/fcb1337f27610b2f2ec66c7118a754c17053681b22e6d56b527704e6f6f0/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c107efab234839fce48f00b9215fb422ed1692176221518b5bf55b10df27ee3", size = 2368581, upload-time = "2026-07-13T06:35:09.476Z" }, + { url = "https://files.pythonhosted.org/packages/dc/74/262e4efe0fdc7f6331997f8bfe8b921754bdc94876f6f795e71d86dea32f/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:28b6caf25e1581328b20ab709eb93290ca73c583e9c2cf873e90f42fbceabb08", size = 2349548, upload-time = "2026-07-13T06:35:11.391Z" }, + { url = "https://files.pythonhosted.org/packages/c2/2b/c74620eff5bc451eeff4e15aa4ba02dc38bce0fa834d1dea5a13bca844b9/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:6a07b90d6519d2265c2fd8a579ef657b9431bc6c7cddc6861b192c89da869e2a", size = 2133554, upload-time = "2026-07-13T06:35:13.088Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9c/92b8fcaad91ab7e7d9c4710bbcf06a6d20289276e737843fc837063349a8/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_i686.whl", hash = "sha256:c53e4e452593c2049ddb6fd3065ccb217c3d2d3648c0611f07d214becc0ce360", size = 2232502, upload-time = "2026-07-13T06:35:14.771Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0e/dfe43082db1d44751eb4e5673aec13d8ff1075bd99aa2f44d6588fcb2b3c/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9e09e5c20d92b5fdc6c487c523f4e488bc39c2eec44b940efd196ecf11f37bf8", size = 2588725, upload-time = "2026-07-13T06:35:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/90/e2/58db66254ba98b605249478974be63026d88a72dacbaf67e3258d4e7238d/qh3-1.9.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e95808b3d37d06a1411cfa33ae7c414ea4d01b38921c300634c9109c4e45f1ba", size = 2125025, upload-time = "2026-07-13T06:35:18.399Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rstcheck" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "rstcheck-core" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/c1/ebec613b5e9225a9f5655bc17ce07b0e27e5a8fb302a1c088be5567d9f6f/rstcheck-6.2.1.tar.gz", hash = "sha256:e4d173950b023eb12c2b9d2348a8c62bef46612bbc7b29e1e57d37320ed0a891", size = 23609, upload-time = "2024-03-23T09:31:42.864Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/b3/5556afc6e83b162dc29f84e0c8a10f6a72af70845552698acd6bb3b927cd/rstcheck-6.2.1-py3-none-any.whl", hash = "sha256:b450943707d8ca053f5c6b9f103ee595f4926a064203e5e579172aefb3fe2c12", size = 8526, upload-time = "2024-03-23T09:31:40.716Z" }, +] + +[[package]] +name = "rstcheck-core" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/91/1d6dc8b0f4482b5c1d96aa35127963c55f5724e84390db6839e2c1973f3f/rstcheck_core-1.3.1.tar.gz", hash = "sha256:3fce3e9f9d250659db021b158e63a373298140dab9aa37ea917373c962bfa04e", size = 61224, upload-time = "2026-07-28T19:25:32.438Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/4c/b46fa11eb819094f0317622b07a560a097f751300d24c19959171a0f1398/rstcheck_core-1.3.1-py3-none-any.whl", hash = "sha256:21eb176c9cb1e6255bd4293d7d220710515cfe6f0862958fcddee8040aa30f88", size = 28703, upload-time = "2026-07-28T19:25:30.596Z" }, +] + +[[package]] +name = "seam" +version = "2.2.0" +source = { editable = "." } +dependencies = [ + { name = "dataclasses-json" }, + { name = "niquests" }, + { name = "svix" }, +] + +[package.dev-dependencies] +dev = [ + { name = "black" }, + { name = "mypy" }, + { name = "pylint" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-runner" }, + { name = "pytest-watch" }, + { name = "rstcheck" }, +] + +[package.metadata] +requires-dist = [ + { name = "dataclasses-json", specifier = ">=0.6.4,<0.7" }, + { name = "niquests", specifier = ">=3.6.4,<4" }, + { name = "svix", specifier = ">=1.24.0,<2" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "black", specifier = ">=24.3.0,<25" }, + { name = "mypy", specifier = ">=1.17.0,<2" }, + { name = "pylint", specifier = ">=3.1.0,<4" }, + { name = "pytest", specifier = ">=8.1.1,<9" }, + { name = "pytest-cov", specifier = ">=5.0.0,<6" }, + { name = "pytest-runner", specifier = ">=6.0.0,<7" }, + { name = "pytest-watch", specifier = ">=4.2.0,<5" }, + { name = "rstcheck", specifier = ">=6.1.2,<7" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +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 = "standardwebhooks" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/50/81/a26b812525dd9ad6cc0f6ba2f8eafd5bf595542e319d95123aa92b51bf22/standardwebhooks-1.1.0.tar.gz", hash = "sha256:e5cb66e21a6356ebb9375aeb57f1348583323015808d475a7c1baaa4b718068a", size = 3232, upload-time = "2026-07-21T15:49:51.45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/dc/0cd2bd9a536e61ad0ee9de3b724492a38faa88ce1828fbc15940824664c3/standardwebhooks-1.1.0-py3-none-any.whl", hash = "sha256:9a88d48a1f198be61517fc7ad328cf58ba02b73f0fbd8941d4213e9c0b6c2a61", size = 3538, upload-time = "2026-07-21T15:49:50.519Z" }, +] + +[[package]] +name = "svix" +version = "1.99.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "deprecated" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "standardwebhooks" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/f5/f12d2a1254c9c891b4cfdf0e94bdf706057b0328df5f4ba9aeb6b2be3b78/svix-1.99.1.tar.gz", hash = "sha256:70374aeb3cc19fcff384e9e22a35fce16c57bf1c95a325b940dc0b5b765c7047", size = 55766, upload-time = "2026-07-23T16:33:52.134Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/fc/0d33bc99ede3fe0a029e47c947a3c2ad1b9cba1d6fcbc432765f5ac282b0/svix-1.99.1-py3-none-any.whl", hash = "sha256:c5e4af1cbcf19ed93b5a02c91f036850d10fc3d22a83092ecfcb83f637f5bb61", size = 156158, upload-time = "2026-07-23T16:33:50.79Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, +] + +[[package]] +name = "typer" +version = "0.27.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/40/4a3db7990d1f62a53182aa96eaef57aeb2886a27f90a195bc66713565d31/typer-0.27.1.tar.gz", hash = "sha256:a79bef8469a79c45498e7b814ecf8d603cc7644e9acbd9e19cac0334240b18df", size = 203994, upload-time = "2026-08-03T14:41:03.438Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/89/9518bc0c3929bee36b3a4a8e3daddd6e03f92f9961c66d4983b837160543/typer-0.27.1-py3-none-any.whl", hash = "sha256:53150287edd11baeb4e4722c8e394fcdf8181c0ae89485cba8d25c778d5edd56", size = 122874, upload-time = "2026-08-03T14:41:04.391Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspect" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825, upload-time = "2023-05-24T20:25:47.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827, upload-time = "2023-05-24T20:25:45.287Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] + +[[package]] +name = "urllib3-future" +version = "2.24.900" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "jh2" }, + { name = "qh3", marker = "(python_full_version < '3.12' and platform_machine == 'AMD64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'ARM64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'arm64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'armv7l' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'i686' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'ppc64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'ppc64le' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'riscv64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'riscv64gc' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 's390x' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'x86' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'x86_64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'AMD64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'ARM64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'arm64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'armv7l' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'i686' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'ppc64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'ppc64le' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'riscv64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'riscv64gc' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 's390x' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'x86' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'x86_64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'AMD64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'ARM64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'arm64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'armv7l' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'i686' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'ppc64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'ppc64le' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'riscv64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'riscv64gc' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 's390x' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'x86' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'x86_64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'armv7l' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'i686' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'ppc64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'ppc64le' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'riscv64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'riscv64gc' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 's390x' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'x86' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'armv7l' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'i686' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'ppc64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'ppc64le' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'riscv64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'riscv64gc' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 's390x' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'x86' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'armv7l' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'i686' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ppc64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ppc64le' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'riscv64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'riscv64gc' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 's390x' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'x86' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/9f/590bfa6575e8872829dc65d02c887d0599b3897912479e5ca198660a8c1e/urllib3_future-2.24.900.tar.gz", hash = "sha256:05c2e9d09293ab29a154fed8f5b60644c7cdaffe4ae2587a07b1c7f00f23a7fb", size = 1370419, upload-time = "2026-07-27T06:16:52.572Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/b6/1a7cf7971c979e56ecff5eb76cf8d748d567ef5f7a4fa39fb87adac922e4/urllib3_future-2.24.900-py3-none-any.whl", hash = "sha256:a4fe06b59b0c3ce2fffa821c3de72eaf59b1e61619becc255dee18919ee25260", size = 809879, upload-time = "2026-07-27T06:16:50.77Z" }, +] + +[[package]] +name = "wassima" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/a1/714674b53d3a57013730187e027e291c652a25db053e02236798bec49d61/wassima-2.1.3.tar.gz", hash = "sha256:fcd6c38be0f909c393da35cb2a993101fcdcff673b8fa8d5da228f73b630d0d0", size = 140075, upload-time = "2026-07-24T18:36:21.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/a2/4c8cd0dddfe40a779739afab2f09e84286bea37cdb73c7b1c12b4990ff12/wassima-2.1.3-py3-none-any.whl", hash = "sha256:f8251c23720dd092117a554b3012756caee3feed6d0122c2458a2aca5d7d92c5", size = 131416, upload-time = "2026-07-24T18:36:19.36Z" }, +] + +[[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/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, + { 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/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, + { 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 = "wrapt" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/b0/c1f5a970721f06b85c0cd5142e0ff8fe067708abd779b0c4f4be7d61d09f/wrapt-2.3.0.tar.gz", hash = "sha256:681a2d0eefd721998f90642762b8e75c2159ec531b20ad5e437245ea7b06a107", size = 131509, upload-time = "2026-07-28T06:06:14.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/31/5822ce37ca8820c2ed35a498c67c8b37960b9cee2ba437fd32849d0a234c/wrapt-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0bb2797048db0956348cb3058c33bc4184614f13231389cfbccc16a5d32780a7", size = 81191, upload-time = "2026-07-28T06:04:04.858Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5a/3c6117938be98754578ab83f5a40d7d0ea2cd2c487dc5cd6027ee7228229/wrapt-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce9f398f868d2b3b27aa2ea4de79645ef9077aeeac8dfc2814b0d542c6a2b87f", size = 82255, upload-time = "2026-07-28T06:04:07.151Z" }, + { url = "https://files.pythonhosted.org/packages/a5/0f/94ae724c5087eb6054c0d63febd7094947dcf302fe058e2e0488102a872b/wrapt-2.3.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad71df7a04dd3497e9302e81f4a7c91bd401ea0e15a9df9029527900f94bee43", size = 155228, upload-time = "2026-07-28T06:04:08.272Z" }, + { url = "https://files.pythonhosted.org/packages/6c/21/1f780bba935dcf697c0c59de9be3a559bbb8e31a53ca3f25422023738432/wrapt-2.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc82c2ccc8e234c844f5303d9f2984b346dcdd53e94823ce8420d2c75b4b9023", size = 157073, upload-time = "2026-07-28T06:04:09.459Z" }, + { url = "https://files.pythonhosted.org/packages/73/31/6c7799d7b6431fcd7e1b83245fb45258a2d2c3a2187fbaecb83572a72d7a/wrapt-2.3.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6e19531ae33c508cea7d84a7edfda01fa86e51b8d1a93a77712c55e6e469152", size = 151594, upload-time = "2026-07-28T06:04:10.784Z" }, + { url = "https://files.pythonhosted.org/packages/ce/17/42d670dbfafd49076c6eb2b7d67633d7e1c968e39bfb11a135acb6fac67b/wrapt-2.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:df4ce31150bcd5d9f36f816aac3010ab4f4bf8672ac1d3b0ac7d539ec61c7c02", size = 156069, upload-time = "2026-07-28T06:04:12.316Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d6/c66b4ba4eda49257c84d5c2df26118280f09ca7905aee20d0064db778d13/wrapt-2.3.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e2e692bc0d63f881cf7006730a56bd4e0c2fab5dc318466942805d692b166276", size = 150930, upload-time = "2026-07-28T06:04:13.482Z" }, + { url = "https://files.pythonhosted.org/packages/c0/f2/1a3b949c0322fb27396eafd1044328c1cb0400e0b32105d75a3cd03096e7/wrapt-2.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c8388ba7faf5dbf9ee106bb70d66f257629b1bd98091123e19e8a4553a319199", size = 154525, upload-time = "2026-07-28T06:04:14.698Z" }, + { url = "https://files.pythonhosted.org/packages/12/65/147563a3dfa6e830c857b93b530ebd8c0cd9d540e5914aec8f9b12880c02/wrapt-2.3.0-cp310-cp310-win32.whl", hash = "sha256:e045ff75d7d94900fc32896ed93c45ce2d2cac28c9dead582ff9a5a49d446e35", size = 77879, upload-time = "2026-07-28T06:04:16.102Z" }, + { url = "https://files.pythonhosted.org/packages/c4/eb/921405b4dc55d4f8be4c700ef120539fdd75d5fdb50d83bd257171ee18e0/wrapt-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4fc96b159af0a3e0faa72475a69d66292bea72a5bed1e1aca1bffbddc3cb2b0", size = 80733, upload-time = "2026-07-28T06:04:17.43Z" }, + { url = "https://files.pythonhosted.org/packages/b6/13/75947450c5bb57795fa86384721cd52c5c4deb0879022f309501a8a85d44/wrapt-2.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:1236fa25173ca964c97422470482e9011b9e3c7ed0d75798b40b3da3b0e0e760", size = 80199, upload-time = "2026-07-28T06:04:18.761Z" }, + { url = "https://files.pythonhosted.org/packages/00/b8/9182e4c618a847be0baccb68e4602b070d0fa22c782cf058f4bc66b32709/wrapt-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ab559e1b2551d23d54db2a0001c6d73bad022a254639561c5f6c382a9d6c2fe", size = 81427, upload-time = "2026-07-28T06:04:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/84/ca/613cefd9c5977366b1587e61c0b428176d382e6d75b454084c5e58503042/wrapt-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bff9a671bc00709cab5a7f745c592b5671873449db0ee2a569af994f16b29a4d", size = 82360, upload-time = "2026-07-28T06:04:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/71/71/4cd2151a236f44a6e2dd4ed8011838d7ba0be3d656c8bafdfc65a2ed1917/wrapt-2.3.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fc648a335d7e01adb3640b25f02fd0ea05886cf04d0af7f4ee902bc7b5e466e8", size = 161700, upload-time = "2026-07-28T06:04:22.723Z" }, + { url = "https://files.pythonhosted.org/packages/49/2c/bc508fee75eb2919ed69769800b09968e4aab16897f909a23f39c81e323f/wrapt-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0077f3d65541925fa83002f967b22ad6550d24813ac64cb905f717194128d9c", size = 162922, upload-time = "2026-07-28T06:04:24.177Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e5/04f34d38e66d857dfc2fc4088d60e70c0e422467822defa49b2b4a26e17b/wrapt-2.3.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9790ea25190a4e0fe4cdf4eeb868e9d75f8a024a70a5b6bf9c348a3a2b72e731", size = 156125, upload-time = "2026-07-28T06:04:25.58Z" }, + { url = "https://files.pythonhosted.org/packages/23/41/c35940ea1c423f129ebe4361db853bc80d4def6326242e1206fa15bf94f4/wrapt-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:816877aa749253149f9ecfd2635d4d948ecfa338e1a0311d187b1acb1bb8a3eb", size = 162039, upload-time = "2026-07-28T06:04:27.154Z" }, + { url = "https://files.pythonhosted.org/packages/0e/60/9bda34c3d7d182aa703fe35339ae0ed4c4dad5e5c587f93890143e1f87fb/wrapt-2.3.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d1c2c1b808600d2ea808e6360910a60ed5f409a4011655e10f9164ba0a414a6", size = 155110, upload-time = "2026-07-28T06:04:28.497Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ba/60bfd9b1a751f4fcb2d603668fc272d651ccdd339a56acf8c40ad21a0293/wrapt-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5ba1e5e08ddc46130e9682b2c249f2d1dd39bda9106ed4bd401b7519f18f41bd", size = 161089, upload-time = "2026-07-28T06:04:29.959Z" }, + { url = "https://files.pythonhosted.org/packages/0f/32/2bd358c6f4f1305c813479d1e9ba746bebdd794f4a20107ab2b3ee0cbd45/wrapt-2.3.0-cp311-cp311-win32.whl", hash = "sha256:45c9279b373d15649dfa2c2077cb3408ea1a6d3125afbdab9d6b809a66f68e14", size = 78030, upload-time = "2026-07-28T06:04:31.241Z" }, + { url = "https://files.pythonhosted.org/packages/4a/62/ecc969b13b141fef89b888c9760821cb01a86ac8fc953911592c8e1e1522/wrapt-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:195b1842b4122fb54e3cd3dd5b2b4aa49302a5a61da901df0481f5c97aedde84", size = 80944, upload-time = "2026-07-28T06:04:32.655Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3d/9278ada8a2b3f24372b630361e84e9a7de7abc3784634860c26d1c37785a/wrapt-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:6db604ef0c67bdb2042ecdfd7b7f037cf09733557ca42360d1018285634f7b98", size = 80074, upload-time = "2026-07-28T06:04:33.811Z" }, + { url = "https://files.pythonhosted.org/packages/5b/4a/d17a0fad1bf1c5f2c887ff71fef75654141b0880bff71d157d955b5bec3a/wrapt-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a45ffae742ce91a16e11cb6c7cd71e7f9994f3cbd283b962ab093f5c6dcf525", size = 82139, upload-time = "2026-07-28T06:04:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/51b92daaf6defb57f4dc56bdcce985400f75c6984a03ca5e78ccac717028/wrapt-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:69e477046f2237ef0bc6547544ee73008dc764ca26eff44f09e976d221b34d5d", size = 82723, upload-time = "2026-07-28T06:04:36.502Z" }, + { url = "https://files.pythonhosted.org/packages/28/7f/cfd9bc4b1f5e424eeea83d0493e43f3b1b02707ce8e50c47945873982bd5/wrapt-2.3.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d221a6e6ddd302b8397433184e96b59f259f50024b854db1c411a881586b6b8", size = 172381, upload-time = "2026-07-28T06:04:37.674Z" }, + { url = "https://files.pythonhosted.org/packages/cb/89/ff7814f6eb6856b479946117d1138a2fbb46cdb6b1f379db359056c69743/wrapt-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:392158c9a7f2ab1b8699418bfc0fe6f83548788c418b27d7bf2019ad3405cebb", size = 174120, upload-time = "2026-07-28T06:04:38.987Z" }, + { url = "https://files.pythonhosted.org/packages/12/1e/8eded8615d39e3ce81f626937a3a87b280a2a86239a2bf14a4b4bb345034/wrapt-2.3.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e5301c35cf75655eb33498f2bd6ae8703ca19940e3167dc9cdf740c712a39c60", size = 163035, upload-time = "2026-07-28T06:04:40.361Z" }, + { url = "https://files.pythonhosted.org/packages/35/ea/a0af2d9da62897af2a055484920de05dade30d2ba2c0d65cbdea875d3d8b/wrapt-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:418f54bb09d1762db02c7009b4051149893af3153a87f92d70356703c11eea02", size = 171887, upload-time = "2026-07-28T06:04:41.614Z" }, + { url = "https://files.pythonhosted.org/packages/7e/dd/63cd4c864c65ef4906df64bd2d378f4a62b54f28063f282dfb3bf93caead/wrapt-2.3.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1598becd30f8f2777d18564064eb4f4dbe1ab0e05a8f09786d0ef505ac782bf3", size = 161113, upload-time = "2026-07-28T06:04:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ee/82f1fc9e431b5c2c5a6d201aa865dbeae3984c311c6d11a185f0c8367cf6/wrapt-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3da470536bf9645143323dd41b32db55c6f4304ad382094c1a1da8a92061e10d", size = 170530, upload-time = "2026-07-28T06:04:44.212Z" }, + { url = "https://files.pythonhosted.org/packages/37/a5/5dc590e863a419930d988f8b7ca3e75a6befcfb10b6003b3a152f3d5f732/wrapt-2.3.0-cp312-cp312-win32.whl", hash = "sha256:fb8e2e6704a1e0b1b989546c69e2688371ef4a07fa5f61bde3eb6211186f5ac1", size = 78323, upload-time = "2026-07-28T06:04:45.484Z" }, + { url = "https://files.pythonhosted.org/packages/51/f9/4a6925a07951df56394f7e6ebe14f69f1c5ef9d87aa63e0839acf15aa63a/wrapt-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:cdc021cb0b62471d6aac7f2bd92f3b4658073775f9ee7fcd325c511129e7bcc8", size = 81180, upload-time = "2026-07-28T06:04:47.021Z" }, + { url = "https://files.pythonhosted.org/packages/a8/4f/8b5de0395b2a72216751d41c9861df6facaeb611b619d8810ed2b3b23eb2/wrapt-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:67bfe2485f50368c3fcd2275fc1fd100e350d601e0058921a7c82678a465aeab", size = 80155, upload-time = "2026-07-28T06:04:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6e/0f88a072483e76b881e3fdcd6b6ffb4a5791002514fe541e72b1b73c859a/wrapt-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d3fb71e65b001adfc42684522eeccd9c21d8ba679945abc993439567b66e59f", size = 81960, upload-time = "2026-07-28T06:04:49.622Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ff/b7e2776e7c294075eb712cc9ef573d1b818f393006d09787262b8fc871c4/wrapt-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51a7a4181c1295774812271fbcd7c909df372bc25579d4ed9eb875caaf0ae86f", size = 82435, upload-time = "2026-07-28T06:04:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/d8/90/343bb5d0f1f9669bc252a6073f085b4abf862511bd5c9c9eaec754341f1d/wrapt-2.3.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9045917809c63fdf7abe3a2ceaed3d670b8ee4500ddd9291192d30aeb34467c5", size = 170350, upload-time = "2026-07-28T06:04:52.187Z" }, + { url = "https://files.pythonhosted.org/packages/59/f8/13b79a392930bd0dd6b86cbfbfe1c40944110456e1dc6d809e5c46ece904/wrapt-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:54ca1d5573f69b5fe1d74f1f65799c68015e82f685efec9fd8cfa40a094c44d0", size = 170022, upload-time = "2026-07-28T06:04:53.599Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fc/4f1b6918f5290db959d6e0c07f77385d87cede29c39c9cf8f145e9c82954/wrapt-2.3.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:242b60c21e30866e6a2fa606c612b47c553fa60c0eaeeeb7797fb842ac0ce609", size = 161043, upload-time = "2026-07-28T06:04:54.936Z" }, + { url = "https://files.pythonhosted.org/packages/01/e1/45d3cf74414780bdff6d0380467e003f6eb0f028b6c9403db868dbc7209c/wrapt-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3f3d7ec0a51fbfe00d3aef047641ff2c58b25565b4717fc1f90e050be01cba8", size = 168576, upload-time = "2026-07-28T06:04:56.261Z" }, + { url = "https://files.pythonhosted.org/packages/f3/73/2fa58dd97f191c997755e2c6d569a68f0c433db4e4b36099bdd7227b6cac/wrapt-2.3.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:261f53870cd4fb2bf38f9f972c56c728fd224cb7c65721307de59d9e7e6741ae", size = 159140, upload-time = "2026-07-28T06:04:57.754Z" }, + { url = "https://files.pythonhosted.org/packages/29/a8/08a56e2000a8816d449dcbad8c8b081697acbbd490821ceca0f9d8e8d20c/wrapt-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8159ec0b0cb7608175eb150de94c19e34f4d47ac655f5ca9baf45df6b688ffd3", size = 169263, upload-time = "2026-07-28T06:04:59.161Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d4/354e1725e35a73b2af4fa70a3e024c7a5d1bf1802dfb862dcb668aae0253/wrapt-2.3.0-cp313-cp313-win32.whl", hash = "sha256:10461884b3014fbfc8eb7d09a93c5f246363e6711d9d881f95eb8c27fdef049f", size = 78241, upload-time = "2026-07-28T06:05:00.507Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7e/34c87fa2174848dfee820322aaa318bab08913998ccecc8d2f57b4ad4639/wrapt-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac870cc97b73bb00ac353329e9559a4bebc47c4c86792ed9b23b58c15b6ad838", size = 81113, upload-time = "2026-07-28T06:05:01.839Z" }, + { url = "https://files.pythonhosted.org/packages/11/86/fcc9a530579e008c9478bb565a6cdfbfd33536660f069c8b91a6607c5050/wrapt-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:a65e8db2b4e90c2e7ade931086351c98ef420bf7a94ee08c95ac8a3cbbc43579", size = 80182, upload-time = "2026-07-28T06:05:03.152Z" }, + { url = "https://files.pythonhosted.org/packages/96/50/3864848b95b28ef73e17551fc8dccbff2628a834f52cf26a57f9c419fb83/wrapt-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:fd1f2f557dd3491fe75905e578f4db967393d40d1a8f468edc4d40ac7f2d5944", size = 83921, upload-time = "2026-07-28T06:05:04.476Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4c/3d1921a60c3e8c71c540ff136e6a47a1fbccf7f671e818394889f7871d9c/wrapt-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9f5d2aec29dfc76c37e23897dee92766a3fd4f3bff3ae7fc9c6b4bf37d8c1360", size = 84412, upload-time = "2026-07-28T06:05:05.921Z" }, + { url = "https://files.pythonhosted.org/packages/fa/1a/4a796ff7adb26ada6d4b758c94d47a38320b085e7099afc088efbbcdb006/wrapt-2.3.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:646d20d413ffcd1b0a2f700076e2d0252d872dcb7754860a73e45a59ea883614", size = 207168, upload-time = "2026-07-28T06:05:07.256Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3e/d7777776806c579b761bac2f91721dda9f04c7a1b380213c5935cc750ae6/wrapt-2.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:379f670f45b7bb8993edd9f6fc36c6cc65edb81cffa0b504be34acb0303fff0a", size = 214351, upload-time = "2026-07-28T06:05:08.945Z" }, + { url = "https://files.pythonhosted.org/packages/63/27/2d64d394df7bf181955b3bb562bf33c4492fb4be113f53071106d43ad8b5/wrapt-2.3.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6208f302f110295d64b22a7ac96500c791bf492dce4366e622e4912b077c9687", size = 199020, upload-time = "2026-07-28T06:05:10.418Z" }, + { url = "https://files.pythonhosted.org/packages/3e/3d/fb31d3db7d9834d265fb1a27a2adf0ddf51557c67458c97b22439ad6ae3d/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ed635a9ca4f3a5a2b900c10c69e823373bc00ebc114b459383596d3487da3570", size = 209969, upload-time = "2026-07-28T06:05:11.983Z" }, + { url = "https://files.pythonhosted.org/packages/1f/d1/8724b5da582e62070dc9bf4d8bf1972f317297eefd7ba1f2b5c6393ccf6c/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e3b9eaa742ae7a0aaaaad4ca4b69469d757af2d6e6663ef1dadc47adec0aeb41", size = 196324, upload-time = "2026-07-28T06:05:13.557Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5c/3d9ef411149543016ee6bcf3af707f787cebd946527452b94bf122e9b7b4/wrapt-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0f7284f88f4833705132d06d3b425a43095c2cbd07c58166aac3ab646ba12a4", size = 202610, upload-time = "2026-07-28T06:05:15.048Z" }, + { url = "https://files.pythonhosted.org/packages/13/9b/4fc042ceb757866dd4a5fc057b3b736f2b360d3703ce9f830d83dc9226e0/wrapt-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:7ebb274aba688b043429eb1500ff8a76ce0cb8ac0812ca3e301f06247b8722b3", size = 79178, upload-time = "2026-07-28T06:05:16.469Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ff/b94878f8eed809ca042685276bcea9f24e8c2ca7c9653bb80bbb920a68a5/wrapt-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c4bded758ad6f03b965830944a2f0bc5b2eb3767fe5a7310134315d1a6610e98", size = 82634, upload-time = "2026-07-28T06:05:18.026Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/663e1de5332a71685a729754312d327d4cada767c36e1c5a2db4c8de49e6/wrapt-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:d2cc64539da63e39ffb9c7ede849b6e8ddaaf7b3876b5cfb04efd85a5f3f4eb6", size = 81387, upload-time = "2026-07-28T06:05:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/58/10/b073beaea89bc0d3670a75ff51139430a54b6af7ba7796507730634536dd/wrapt-2.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ea52a0d0f08c584943d5764be0e84efa912c8da23c23e1e285ff2f5641c18fcc", size = 81978, upload-time = "2026-07-28T06:05:21.133Z" }, + { url = "https://files.pythonhosted.org/packages/b3/31/0916d9cebf848ed3f1a0c1888faee421747df77331e4db2bc527a9a85988/wrapt-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd85b0aa88efdb189d6ae2f35f4526943a8f091c38599c9c31478241c819e6a1", size = 82518, upload-time = "2026-07-28T06:05:22.562Z" }, + { url = "https://files.pythonhosted.org/packages/f5/73/31c1bf0f3384062751c2094dadb314916d70aa9b6bfd26d994b4a7b393fa/wrapt-2.3.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:141ed6211286a9660d8d6702de598b43f0934b4f0eda16393f100a80f501d945", size = 170187, upload-time = "2026-07-28T06:05:23.904Z" }, + { url = "https://files.pythonhosted.org/packages/ed/25/fce087d54b79b8905f3c3c9dd5f454bbd8d8acb80b960c4a6aee5b4659b3/wrapt-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e49885a62ec4ee854d1b9e6371fda6afd219917225752abf729a3f36d4df9a5", size = 169288, upload-time = "2026-07-28T06:05:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/c7/30/0d09e6dddc6b7a7230ac77f50254b5980ab4fcd22976f72f8cc8a0404458/wrapt-2.3.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d6159c9b2fefec02314e1332dbbbfaf960e369dfd26bcf7f8b258b5732065b3", size = 160932, upload-time = "2026-07-28T06:05:27.022Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ca/0913af0d2ec0c43865d32d615f518fea66c13c5c930e489e9b0de248e9a8/wrapt-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24da48596326ef8e448cfa837b454f638713d3531262375f00e5a9681682fc07", size = 169017, upload-time = "2026-07-28T06:05:28.501Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f2/3d1e47ea81b822210f5df1bf942fd90780a75c055243d569b664529dea88/wrapt-2.3.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cd3a2edf0427013736b8127955cec62608c56e53ea47e82812ea32059cda407f", size = 159065, upload-time = "2026-07-28T06:05:30.01Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/ef2066ced8e5fca204e2b361e9708e36555b40949c583d997ea3b590817d/wrapt-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0df3bff4e7ce45759f33fd39335fe2f60477bb9ecf7b8aa41e7d07ee36a23", size = 168821, upload-time = "2026-07-28T06:05:31.649Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e1/016104650d4e572fa91506eb396b3dd8efbccc9284fdc1c9479c3d21db28/wrapt-2.3.0-cp314-cp314-win32.whl", hash = "sha256:2935d5454b3f179a29b12cf390ee47246740ba2c3a7545b1b46ba31a5f2a4a0b", size = 78700, upload-time = "2026-07-28T06:05:33.391Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/6fdc20a9f2ca304748b3f0819cbf377d55260562777bf0b615431bc3c181/wrapt-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:cc2cea812e5cb179a796b766747e7d3b21088760d8deb95676d482b8c8e6fa7d", size = 81422, upload-time = "2026-07-28T06:05:34.774Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a4/9cbd53bf05746bea2c392af39cb052427a8ec95cbd494d930733d8f44681/wrapt-2.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:22cc5c0a717bd4da87018ae0bffd4c19c6fb679d3ff357216ba566ab26c76cab", size = 80639, upload-time = "2026-07-28T06:05:36.228Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/6c5e4a0f66ea0d2b2dd267e8dd05a0014eea56840b3c8595d40b0a5d1f91/wrapt-2.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a6b5984cd65dd639546f0eb4b8eacf1c31cb2fe9fb5c27bffe240987cdb2cf84", size = 84030, upload-time = "2026-07-28T06:05:37.714Z" }, + { url = "https://files.pythonhosted.org/packages/6a/eb/a1aedf03283bc9cbf8a1783995ddc54e3c5a86878f19002d2c428494f4c5/wrapt-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c88abcf53daef80e01a75c7530e727fa6e2c1888fe83e3dcdba4c96216a1f5c7", size = 84419, upload-time = "2026-07-28T06:05:39.131Z" }, + { url = "https://files.pythonhosted.org/packages/63/61/50d511c0dc5105563849e86daa3e16ac7feef699f79fb05af45ea70107d5/wrapt-2.3.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:85de890ff968196e92dd1ae73a9fb8970495e7650a457b1c9ef0ac3dd550bce2", size = 207171, upload-time = "2026-07-28T06:05:40.69Z" }, + { url = "https://files.pythonhosted.org/packages/3f/59/9b538cf7795217e810699d16bc88b96a830d9b5c403eb2ec2db6b5f2ae81/wrapt-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50f416b74d092bb9f41b424e90dd457f365f7ba4b11de62a23679769a21bd85c", size = 214329, upload-time = "2026-07-28T06:05:42.287Z" }, + { url = "https://files.pythonhosted.org/packages/b3/28/9935d62b1499e5c8b3d191e99ba4eb31ca237a0b699142011a837e9dc7ea/wrapt-2.3.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39febbee6d77301d31da6996b152ce52452da7c7ef72aba10c2fa976dff9c295", size = 199079, upload-time = "2026-07-28T06:05:43.958Z" }, + { url = "https://files.pythonhosted.org/packages/2b/01/4446b80fa2ffa47a3449b250d004ba1c1937f07f64a179608fec735df866/wrapt-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:93513bec052c6cd987f9f580c3df068c8bc4ebae6543736be3ca7ec5959cafcd", size = 209992, upload-time = "2026-07-28T06:05:45.677Z" }, + { url = "https://files.pythonhosted.org/packages/d4/07/56f26c9f9979586a021e8148747004aba4498f49458c90b0502969b904e1/wrapt-2.3.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:729126e667da34d251b8ebf8a45ef0c5ddadc21542b3d6e1abf4259ece6508df", size = 196334, upload-time = "2026-07-28T06:05:47.608Z" }, + { url = "https://files.pythonhosted.org/packages/8b/41/6d7bcc895b0f28b2250e10908f060687b9165429dcd7f22ddb3d4c031b74/wrapt-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:626b69db2021aa01671ec7bbc9740e558522bd44c18cf2ce69bf3d666a014109", size = 202644, upload-time = "2026-07-28T06:05:49.183Z" }, + { url = "https://files.pythonhosted.org/packages/cd/25/7860927edba06b758b8852a6f02e832be715563c67a6795d94350bc81099/wrapt-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:629d73378082c00a8173031f9fb30a3ac6abbc894a5bfdfae71fabc60642d501", size = 79685, upload-time = "2026-07-28T06:05:50.976Z" }, + { url = "https://files.pythonhosted.org/packages/c4/0f/270bafe92fde3b069a39bc01e39ee79340895b335640df861d43d2a51885/wrapt-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:42869085687f0aefd57c0f636c3f9354f8ffb321a8ba9cb52d19beb796e561c5", size = 83104, upload-time = "2026-07-28T06:05:52.405Z" }, + { url = "https://files.pythonhosted.org/packages/55/b3/af176d79a8515a8a720eccdad9a96f6e31a30abf2865430c8c42adf2fd13/wrapt-2.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b1e5aa486e269b00ed35e64771c7d0ab8096cfd2643405ca8cd60ebedc099a51", size = 81774, upload-time = "2026-07-28T06:05:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/00/39/3daf9f47be208606586de4568ba6713db53ebc8fd7a575aea1fe57983b69/wrapt-2.3.0-py3-none-any.whl", hash = "sha256:d8c7ed08477429752b8c44991f40ad7838b18332a160698740a6bfbc10d998a2", size = 61866, upload-time = "2026-07-28T06:06:12.9Z" }, +] From 5d0a961e3e72a7261542f2af9834873d31026928 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 03:24:35 +0000 Subject: [PATCH 02/39] 3.0.0b1 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ea2e1462..25300032 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seam" -version = "2.2.0" +version = "3.0.0b1" description = "SDK for the Seam API written in Python." authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" diff --git a/uv.lock b/uv.lock index 9453d94a..c93e5236 100644 --- a/uv.lock +++ b/uv.lock @@ -1246,7 +1246,7 @@ wheels = [ [[package]] name = "seam" -version = "2.2.0" +version = "3.0.0b1" source = { editable = "." } dependencies = [ { name = "dataclasses-json" }, From 35b243b120bfad2a517b5d54786751ba7dbbaa59 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 20:33:17 -0700 Subject: [PATCH 03/39] docs: name the page cursor property as Python spells it (#607) The pagination heading called it nextPageCursor, which is how the JavaScript SDK spells it. In this SDK it is pagination.next_page_cursor, as the example under that very heading already shows. Claude-Session: https://claude.ai/code/session_01PpqwDhZmyikGbjmCPqFW2A Co-authored-by: Claude --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 4dbba7bb..8b1c5891 100644 --- a/README.rst +++ b/README.rst @@ -49,7 +49,7 @@ Contents * `Pagination`_ - * `Manually fetch pages with the nextPageCursor`_ + * `Manually fetch pages with the next_page_cursor`_ * `Resume pagination`_ @@ -282,8 +282,8 @@ Pagination Some Seam API endpoints that return lists of resources support pagination. Use the ``SeamPaginator`` class to fetch and process resources across multiple pages. -Manually fetch pages with the nextPageCursor -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Manually fetch pages with the next_page_cursor +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python From 17deca098c005d5840f79c0bde3d0cba15b8ce45 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 21:04:17 -0700 Subject: [PATCH 04/39] Type check the whole project instead of seam/resources with error codes disabled (#608) * fix: Export type information from the package root seam/__init__.py carried a blanket "# type: ignore", which hid the module from type checkers entirely. Since the package also ships py.typed, every downstream project that type checks its own code saw the documented import fail: app.py:1: error: Module "seam" has no attribute "Seam" [attr-defined] The lint step only pointed mypy at seam/resources, so nothing in this repository could catch it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * fix: Type the decoded response body on the HTTP client SeamHttpClient overrides request to return the decoded body rather than the Response that niquests.Session promises, but the verb helpers kept the inherited Response return type. Indexing what post and get actually return did not type check, which accounted for every "Value of type Response is not indexable" error in the generated routes. Both helpers now name the arguments they forward: Session.request takes params in the position Session.post gives data, so collecting them into *args would have rerouted positional calls. Handling of a response without a status code is no longer a TypeError. niquests types status_code as optional because a Response exists before it has one, so the comparison against it is guarded and the narrowed value is passed to the error path. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * fix: Correct optional handling across the hand-written modules Type checking these files for the first time turned up a latent crash: SeamPaginator.flatten_to_list read pagination.has_next_page without the None guard its sibling flatten already had, so a page whose response carried no pagination raised AttributeError instead of ending the loop. The rest are annotations that did not match their callers. request_id is read from a response header that may be absent, so the HTTP exceptions take Optional[str]. SeamActionAttemptTimeoutError documented its timeout as str while every caller passes a float. poll_until_ready declared its timeout and polling interval Optional though neither is ever None, which made arithmetic on them unsound. SeamPaginator.params used an implicit Optional default, and its response hook is handed either side of the exchange, so the request side is turned away before the pagination is read. A failed action attempt is assumed to carry an error; reading through it unguarded would raise AttributeError over the actual failure. Two invariants are beyond what the checker can follow and are ignored in place: the auth option guards return True only once the values they check are set, and Seam borrows Routes.__init__ to attach the route namespaces even though the two are siblings under AbstractRoutes rather than parent and child. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * feat: Declare resource fields the API may omit as Optional Generated dataclasses declared every field as present and non-null, but from_dict reads each one with dict.get, so a field the payload omits arrives as None behind a type that rules None out. That mismatch was the whole of the ~1720 arg-type errors the lint step disabled the error code to hide. Fields the blueprint marks optional or nullable are now generated as Optional. So are the ones a merged shape cannot guarantee: a property only some variants of a discriminated union carry is absent whenever the dataclass holds a variant that omits it. Nested objects are Optional regardless of what the schema says, because the schema is not a reliable guide to when they arrive. An action attempt documents both error and result as required, yet a pending one carries neither, so from_dict keeps its None fallback and the field admits it. No field gains a default, so the constructors are unchanged. from_dict now takes Any. The payload is decoded JSON and every value read out of it is untyped, so keeping that at the boundary avoids casting each read while the fields carry the real types. Route methods annotate json_payload as Dict[str, Any]. Left bare, its type was inferred from whichever parameter was written first, and every later parameter of a different type was reported as an incompatible assignment. BREAKING CHANGE: Resource fields that the API may omit or send as null are now typed Optional. Code that reads them under a type checker may need a None check. Runtime behavior is unchanged: those fields could already be None. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM * build: Type check the whole project The mypy step checked seam/resources with two error codes disabled. That covered 34 of the 94 files in the package and left every hand-written module unchecked, including the package root whose blanket ignore broke type checking for every downstream user. It now checks seam and test with nothing disabled. The import-not-found disable is dropped outright: it suppressed nothing, since pointing mypy at the whole package resolves the imports it was added for. The paginator tests narrow the pagination they assert on, the way test_paginator_first_page already did, and the test that passes None as a cursor on purpose says so. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01V8duZRXUbcxu7y5WeoACqM --------- Co-authored-by: Claude --- .../layouts/partials/resource-dataclass.hbs | 5 +- codegen/layouts/partials/route-method.hbs | 2 +- codegen/lib/layouts/resources.ts | 32 +- codegen/lib/python-type.ts | 16 + justfile | 2 +- seam/__init__.py | 1 - seam/auth.py | 7 +- seam/client.py | 30 +- seam/exceptions.py | 48 +- seam/modules/action_attempts.py | 4 +- seam/paginator.py | 15 +- seam/resources/access_code.py | 139 +- seam/resources/access_grant.py | 94 +- seam/resources/access_method.py | 66 +- seam/resources/acs_access_group.py | 69 +- seam/resources/acs_credential.py | 93 +- seam/resources/acs_encoder.py | 10 +- seam/resources/acs_entrance.py | 216 +-- seam/resources/acs_system.py | 55 +- seam/resources/acs_user.py | 133 +- seam/resources/action_attempt.py | 329 ++-- seam/resources/batch.py | 55 +- seam/resources/client_session.py | 11 +- seam/resources/connect_webview.py | 17 +- seam/resources/connected_account.py | 100 +- seam/resources/customer_portal.py | 5 +- seam/resources/device.py | 1339 ++++++++++------- seam/resources/device_provider.py | 45 +- seam/resources/instant_key.py | 20 +- seam/resources/noise_threshold.py | 7 +- seam/resources/pagination.py | 9 +- seam/resources/phone.py | 57 +- seam/resources/seam_event.py | 268 ++-- seam/resources/space.py | 33 +- seam/resources/thermostat_daily_program.py | 12 +- seam/resources/thermostat_schedule.py | 16 +- seam/resources/unmanaged_access_code.py | 96 +- seam/resources/unmanaged_access_grant.py | 88 +- seam/resources/unmanaged_access_method.py | 60 +- seam/resources/unmanaged_device.py | 135 +- seam/resources/unmanaged_user_identity.py | 21 +- seam/resources/user_identity.py | 23 +- seam/resources/webhook.py | 9 +- seam/resources/workspace.py | 30 +- seam/routes/access_codes.py | 20 +- seam/routes/access_codes_simulate.py | 2 +- seam/routes/access_codes_unmanaged.py | 10 +- seam/routes/access_grants.py | 14 +- seam/routes/access_grants_unmanaged.py | 6 +- seam/routes/access_methods.py | 14 +- seam/routes/access_methods_unmanaged.py | 4 +- seam/routes/acs_access_groups.py | 14 +- seam/routes/acs_credentials.py | 16 +- seam/routes/acs_encoders.py | 10 +- seam/routes/acs_encoders_simulate.py | 8 +- seam/routes/acs_entrances.py | 10 +- seam/routes/acs_systems.py | 8 +- seam/routes/acs_users.py | 22 +- seam/routes/action_attempts.py | 4 +- seam/routes/client_sessions.py | 14 +- seam/routes/connect_webviews.py | 8 +- seam/routes/connected_accounts.py | 10 +- seam/routes/connected_accounts_simulate.py | 2 +- seam/routes/customers.py | 6 +- seam/routes/devices.py | 10 +- seam/routes/devices_simulate.py | 12 +- seam/routes/devices_unmanaged.py | 6 +- seam/routes/events.py | 4 +- seam/routes/instant_keys.py | 6 +- seam/routes/locks.py | 10 +- seam/routes/locks_simulate.py | 4 +- seam/routes/noise_sensors.py | 2 +- seam/routes/noise_sensors_noise_thresholds.py | 10 +- seam/routes/noise_sensors_simulate.py | 2 +- seam/routes/phones.py | 6 +- seam/routes/phones_simulate.py | 2 +- seam/routes/spaces.py | 24 +- seam/routes/thermostats.py | 28 +- seam/routes/thermostats_daily_programs.py | 6 +- seam/routes/thermostats_schedules.py | 10 +- seam/routes/thermostats_simulate.py | 4 +- seam/routes/user_identities.py | 28 +- seam/routes/user_identities_unmanaged.py | 6 +- seam/routes/webhooks.py | 10 +- seam/routes/workspaces.py | 10 +- seam/seam.py | 5 +- test/paginator_test.py | 8 +- 87 files changed, 2540 insertions(+), 1697 deletions(-) diff --git a/codegen/layouts/partials/resource-dataclass.hbs b/codegen/layouts/partials/resource-dataclass.hbs index 8a923753..c0297ba0 100644 --- a/codegen/layouts/partials/resource-dataclass.hbs +++ b/codegen/layouts/partials/resource-dataclass.hbs @@ -15,8 +15,11 @@ {{../memberIndent}}{{pythonIdentifier name}}: {{type}} {{/each}} +{{memberIndent}}# The payload is decoded JSON, so every value read out of it is untyped. +{{memberIndent}}# Typing d as Any keeps that at this boundary instead of casting each +{{memberIndent}}# read, and the dataclass fields carry the real types. {{memberIndent}}@classmethod -{{memberIndent}}def from_dict(cls, d: Dict[str, Any]): +{{memberIndent}}def from_dict(cls, d: Any): {{#unless properties}} {{memberIndent}} # This shape documents no properties, so there is nothing to read. {{memberIndent}} # pylint: disable=unused-argument diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index c009161a..d5549441 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,6 +1,6 @@ def {{> method-signature}}: """{{> method-docstring}}""" - json_payload = {} + json_payload: Dict[str, Any] = {} {{#each params}} if {{name}} is not None: diff --git a/codegen/lib/layouts/resources.ts b/codegen/lib/layouts/resources.ts index 0463f8de..fc4d6397 100644 --- a/codegen/lib/layouts/resources.ts +++ b/codegen/lib/layouts/resources.ts @@ -6,7 +6,10 @@ import type { Blueprint, Property } from '@seamapi/blueprint' import { pascalCase, snakeCase } from 'change-case' import { convertCustomResourceName } from '../custom-resource-name-conversions.js' -import { mapPropertyToPythonType } from '../python-type.js' +import { + mapPropertyToPythonType, + mapRequiredPropertyToPythonType, +} from '../python-type.js' export interface ResourceLayoutContext extends ResourceClassLayoutContext { moduleName: string @@ -144,6 +147,9 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { return { ...first, ...docs } } +const withOptionality = (property: Property, isOptional: boolean): Property => + isOptional ? { ...property, isOptional: true } : property + const mergePropertyLists = ( propertyLists: Property[][], path = '', @@ -161,7 +167,13 @@ const mergePropertyLists = ( } return [...occurrences.entries()].map(([name, group]) => - mergeOccurrences(group, path === '' ? name : `${path}.${name}`), + // A property only some variants carry is absent whenever the merged + // dataclass holds one of the variants that omits it, so it is optional on + // the merged shape no matter how each variant declares it. + withOptionality( + mergeOccurrences(group, path === '' ? name : `${path}.${name}`), + group.length < propertyLists.length, + ), ) } @@ -249,7 +261,17 @@ const buildClass = ( ) } - const type = mapPropertyToPythonType(property, nestedClassName) + const isObject = nestedClassName != null && property.format === 'object' + // A nested object is read as None whenever the payload omits it, and the + // schema is not a reliable guide to when that happens: an action attempt + // documents both error and result as required, yet a pending one carries + // neither. Constructing them unconditionally would fail on those payloads, + // so from_dict keeps its None fallback and the field stays Optional. + const type = mapPropertyToPythonType(property, nestedClassName, isObject) + const requiredType = mapRequiredPropertyToPythonType( + property, + nestedClassName, + ) return { name: property.name, description: property.description, @@ -259,8 +281,8 @@ const buildClass = ( // Nested classes are attributes of the class that owns them, so // from_dict reaches them through cls rather than a qualified path. nestedClassName: nestedClassName ?? '', - isDictParam: type.startsWith('Dict'), - isObject: nestedClassName != null && property.format === 'object', + isDictParam: requiredType.startsWith('Dict'), + isObject, isObjectList: nestedClassName != null && property.format === 'list', } }) diff --git a/codegen/lib/python-type.ts b/codegen/lib/python-type.ts index feed09b5..fadcd5d7 100644 --- a/codegen/lib/python-type.ts +++ b/codegen/lib/python-type.ts @@ -21,9 +21,25 @@ export const mapParameterToPythonType = (parameter: Parameter): string => { return mapScalarFormatToPythonType(parameter.format) } +// from_dict reads every property with dict.get, so a property the API may omit +// or send as null arrives as None. Declaring those fields Optional keeps the +// dataclass honest about what a caller can actually find on it. export const mapPropertyToPythonType = ( property: Property, nestedClassName?: string, + isOptional = false, +): string => { + const type = mapRequiredPropertyToPythonType(property, nestedClassName) + return isOptional || property.isOptional || property.isNullable + ? `Optional[${type}]` + : type +} + +// The type a property has before optionality is taken into account. Callers +// that match on the shape of the type, rather than render it, want this one. +export const mapRequiredPropertyToPythonType = ( + property: Property, + nestedClassName?: string, ): string => { if (property.format === 'list') { return `List[${ diff --git a/justfile b/justfile index a34cc4d5..ed397262 100644 --- a/justfile +++ b/justfile @@ -11,7 +11,7 @@ default: build uv run pylint ./seam ./test uv run black --check . uv run rstcheck README.rst - uv run mypy seam/resources --disable-error-code=arg-type --disable-error-code=import-not-found + uv run mypy seam test @test: uv run pytest --cov=./seam diff --git a/seam/__init__.py b/seam/__init__.py index 6e62d98d..de402cd8 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -1,5 +1,4 @@ # flake8: noqa -# type: ignore from .seam import Seam from .seam_without_workspace import SeamWithoutWorkspace diff --git a/seam/auth.py b/seam/auth.py index 3b30c071..6b8c09af 100644 --- a/seam/auth.py +++ b/seam/auth.py @@ -43,15 +43,18 @@ def get_auth_headers( api_key=api_key, personal_access_token=personal_access_token, ): - return get_auth_headers_for_api_key(api_key) + # The guard returns True only for a non-None api_key, which is not + # something the type checker can see through the call. + return get_auth_headers_for_api_key(api_key) # type: ignore[arg-type] if is_seam_options_with_personal_access_token( personal_access_token=personal_access_token, api_key=api_key, workspace_id=workspace_id, ): + # Likewise, the guard raises unless both of these are set. return get_auth_headers_for_personal_access_token( - personal_access_token, workspace_id + personal_access_token, workspace_id # type: ignore[arg-type] ) raise SeamInvalidOptionsError( diff --git a/seam/client.py b/seam/client.py index 9dc3ea44..e637020b 100644 --- a/seam/client.py +++ b/seam/client.py @@ -40,7 +40,7 @@ def _handle_response(self, response: requests.Response): raise NotImplementedError @abc.abstractmethod - def _handle_error_response(self, response: requests.Response): + def _handle_error_response(self, response: requests.Response, status_code: int): raise NotImplementedError @@ -74,7 +74,19 @@ def __init__( headers = {**auth_headers, **custom_headers, **SDK_HEADERS} self.headers.update(headers) - def request(self, method, url, *args, **kwargs): + # request returns the decoded body rather than the Response that + # niquests.Session promises, so the verb helpers routed through it have to + # say so too. Without these overrides callers see the inherited Response + # type and indexing the returned payload does not type check. + def get(self, url, **kwargs) -> Any: + return self.request("GET", url, **kwargs) + + # data and json are named rather than collected into *args because + # Session.request takes params in the position Session.post gives data. + def post(self, url, data=None, json=None, **kwargs) -> Any: + return self.request("POST", url, data=data, json=json, **kwargs) + + def request(self, method, url, *args, **kwargs) -> Any: url = urljoin(self.base_url, url) if kwargs.get("timeout", NIQUESTS_TIMEOUT_DEFAULT) == NIQUESTS_TIMEOUT_DEFAULT: @@ -85,16 +97,22 @@ def request(self, method, url, *args, **kwargs): return self._handle_response(response) def _handle_response(self, response: requests.Response): - if not 200 <= response.status_code < 300: - self._handle_error_response(response) + # niquests types status_code as optional because a Response exists + # before it has one. Anything reaching here has been received, so a + # missing status is an error the SDK cannot classify itself. + status_code = response.status_code + + if status_code is None: + response.raise_for_status() + elif not 200 <= status_code < 300: + self._handle_error_response(response, status_code) if "application/json" in response.headers.get("content-type", ""): return response.json() return response.text - def _handle_error_response(self, response: requests.Response): - status_code = response.status_code + def _handle_error_response(self, response: requests.Response, status_code: int): request_id = response.headers.get("seam-request-id") if status_code == 401: diff --git a/seam/exceptions.py b/seam/exceptions.py index 73fc3210..9a8367b7 100644 --- a/seam/exceptions.py +++ b/seam/exceptions.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any, Dict, Optional from .resources import ActionAttempt @@ -15,20 +15,24 @@ class SeamHttpApiError(Exception): :vartype code: str :ivar status_code: The HTTP status code of the error response :vartype status_code: int - :ivar request_id: The unique identifier for the API request - :vartype request_id: str + :ivar request_id: The unique identifier for the API request, when the + response carried one + :vartype request_id: Optional[str] :ivar data: Additional error data, if provided by the API :vartype data: Dict[str, Any] """ - def __init__(self, error: Dict[str, Any], status_code: int, request_id: str): + def __init__( + self, error: Dict[str, Any], status_code: int, request_id: Optional[str] + ): """ :param error: Dictionary containing error details from the API response :type error: Dict[str, Any] :param status_code: HTTP status code of the error response :type status_code: int - :param request_id: Unique identifier for the API request - :type request_id: str + :param request_id: Unique identifier for the API request, when the + response carried one + :type request_id: Optional[str] """ super().__init__(error.get("message")) @@ -45,10 +49,11 @@ class SeamHttpUnauthorizedError(SeamHttpApiError): This exception is a specific type of SeamHttpApiError for 401 Unauthorized errors. """ - def __init__(self, request_id: str): + def __init__(self, request_id: Optional[str]): """ - :param request_id: Unique identifier for the API request - :type request_id: str + :param request_id: Unique identifier for the API request, when the + response carried one + :type request_id: Optional[str] """ super().__init__( @@ -66,14 +71,17 @@ class SeamHttpInvalidInputError(SeamHttpApiError): :vartype code: str """ - def __init__(self, error: Dict[str, Any], status_code: int, request_id: str): + def __init__( + self, error: Dict[str, Any], status_code: int, request_id: Optional[str] + ): """ :param error: Dictionary containing error details from the API response :type error: Dict[str, Any] :param status_code: HTTP status code of the error response :type status_code: int - :param request_id: Unique identifier for the API request - :type request_id: str + :param request_id: Unique identifier for the API request, when the + response carried one + :type request_id: Optional[str] """ super().__init__(error, status_code, request_id) @@ -120,9 +128,17 @@ def __init__(self, action_attempt: ActionAttempt): :type action_attempt: ActionAttempt """ - super().__init__(action_attempt.error.message, action_attempt) + # A failed action attempt carries an error, but reading through it + # unguarded would raise AttributeError over the actual failure if one + # ever arrives without it. + error = action_attempt.error + + super().__init__( + error.message if error is not None else "Action attempt failed", + action_attempt, + ) self.name = self.__class__.__name__ - self.code = action_attempt.error.type + self.code = error.type if error is not None else "unknown_error" class SeamActionAttemptTimeoutError(SeamActionAttemptError): @@ -136,12 +152,12 @@ class SeamActionAttemptTimeoutError(SeamActionAttemptError): :vartype name: str """ - def __init__(self, action_attempt: ActionAttempt, timeout: str): + def __init__(self, action_attempt: ActionAttempt, timeout: float): """ :param action_attempt: The ActionAttempt object associated with this error :type action_attempt: ActionAttempt :param timeout: The timeout duration in seconds - :type timeout: str + :type timeout: float """ message = f"Timed out waiting for action attempt after {timeout}s" diff --git a/seam/modules/action_attempts.py b/seam/modules/action_attempts.py index 7d48243c..d764d3cb 100644 --- a/seam/modules/action_attempts.py +++ b/seam/modules/action_attempts.py @@ -21,8 +21,8 @@ def poll_until_ready( client: SeamHttpClient, *, action_attempt_id: str, - timeout: Optional[float] = TIMEOUT, - polling_interval: Optional[float] = POLLING_INTERVAL, + timeout: float = TIMEOUT, + polling_interval: float = POLLING_INTERVAL, ) -> ActionAttempt: time_waiting = 0.0 diff --git a/seam/paginator.py b/seam/paginator.py index fedb86d9..119173ca 100644 --- a/seam/paginator.py +++ b/seam/paginator.py @@ -1,6 +1,6 @@ -from typing import Callable, Dict, Any, Tuple, Generator, List +from typing import Callable, Dict, Any, Optional, Tuple, Generator, List, Union from .client import SeamHttpClient -from niquests import Response, JSONDecodeError +from niquests import PreparedRequest, Response, JSONDecodeError from .pagination import Pagination @@ -17,7 +17,7 @@ def __init__( self, client: SeamHttpClient, request: Callable, - params: Dict[str, Any] = None, + params: Optional[Dict[str, Any]] = None, ): """ Initializes the Paginator. @@ -74,7 +74,7 @@ def flatten_to_list(self) -> List[Any]: if current_items: all_items.extend(current_items) - while pagination.has_next_page: + while pagination and pagination.has_next_page and pagination.next_page_cursor: current_items, pagination = self.next_page(pagination.next_page_cursor) if current_items: all_items.extend(current_items) @@ -92,8 +92,13 @@ def flatten(self) -> Generator[Any, None, None]: if current_items: yield from current_items - def _cache_pagination(self, response: Response, page_key: str) -> None: + def _cache_pagination( + self, response: Union[PreparedRequest, Response], page_key: str + ) -> None: """Extracts pagination dict from response, creates Pagination object, and caches it.""" + if not isinstance(response, Response): + return + try: response_json = response.json() pagination = response_json.get("pagination", {}) diff --git a/seam/resources/access_code.py b/seam/resources/access_code.py index 00fc66a5..7cb5baba 100644 --- a/seam/resources/access_code.py +++ b/seam/resources/access_code.py @@ -86,17 +86,20 @@ class DormakabaOracodeMetadata(ResourceMapping): :ivar user_level_name: Dormakaba Oracode user level name associated with this access code. """ - is_cancellable: bool - is_early_checkin_able: bool - is_extendable: bool - is_overridable: bool - site_name: str - stay_id: float - user_level_id: str - user_level_name: str - + is_cancellable: Optional[bool] + is_early_checkin_able: Optional[bool] + is_extendable: Optional[bool] + is_overridable: Optional[bool] + site_name: Optional[str] + stay_id: Optional[float] + user_level_id: Optional[str] + user_level_name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( is_cancellable=d.get("is_cancellable", None), is_early_checkin_able=d.get("is_early_checkin_able", None), @@ -146,31 +149,37 @@ class ModifiedFields(ResourceMapping): :ivar to: The new value of the field.""" field: str - from_: str - to: str + from_: Optional[str] + to: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( field=d.get("field", None), from_=d.get("from", None), to=d.get("to", None), ) - created_at: str + created_at: Optional[str] error_code: str - is_access_code_error: bool + is_access_code_error: Optional[bool] message: str - managed_access_code_id: str - unmanaged_access_code_id: str - change_type: str - modified_fields: List[ModifiedFields] - is_connected_account_error: bool - is_device_error: bool - is_bridge_error: bool - + managed_access_code_id: Optional[str] + unmanaged_access_code_id: Optional[str] + change_type: Optional[str] + modified_fields: Optional[List[ModifiedFields]] + is_connected_account_error: Optional[bool] + is_device_error: Optional[bool] + is_bridge_error: Optional[bool] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -216,13 +225,16 @@ class From(ResourceMapping): :ivar starts_at: Previous start time for the access code.""" - code: str - name: str - ends_at: str - starts_at: str + code: Optional[str] + name: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( code=d.get("code", None), name=d.get("name", None), @@ -242,13 +254,16 @@ class To(ResourceMapping): :ivar starts_at: New start time for the access code.""" - code: str - name: str - ends_at: str - starts_at: str + code: Optional[str] + name: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( code=d.get("code", None), name=d.get("name", None), @@ -259,12 +274,15 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str message: str mutation_code: str - scheduled_at: str - from_: From - to: To + scheduled_at: Optional[str] + from_: Optional[From] + to: Optional[To] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -304,25 +322,31 @@ class ModifiedFields(ResourceMapping): :ivar to: The new value of the field.""" field: str - from_: str - to: str + from_: Optional[str] + to: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( field=d.get("field", None), from_=d.get("from", None), to=d.get("to", None), ) - created_at: str + created_at: Optional[str] message: str warning_code: str - change_type: str - modified_fields: List[ModifiedFields] + change_type: Optional[str] + modified_fields: Optional[List[ModifiedFields]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -335,32 +359,35 @@ def from_dict(cls, d: Dict[str, Any]): ) access_code_id: str - code: str - common_code_key: str + code: Optional[str] + common_code_key: Optional[str] created_at: str device_id: str - dormakaba_oracode_metadata: DormakabaOracodeMetadata - ends_at: str + dormakaba_oracode_metadata: Optional[DormakabaOracodeMetadata] + ends_at: Optional[str] errors: List[Errors] - is_backup: bool + is_backup: Optional[bool] is_backup_access_code_available: bool is_external_modification_allowed: bool is_managed: bool is_offline_access_code: bool is_one_time_use: bool - is_scheduled_on_device: bool - is_waiting_for_code_assignment: bool - name: str + is_scheduled_on_device: Optional[bool] + is_waiting_for_code_assignment: Optional[bool] + name: Optional[str] pending_mutations: List[PendingMutations] - pulled_backup_access_code_id: str - starts_at: str + pulled_backup_access_code_id: Optional[str] + starts_at: Optional[str] status: str type: str warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_code_id=d.get("access_code_id", None), code=d.get("code", None), diff --git a/seam/resources/access_grant.py b/seam/resources/access_grant.py index 77550335..4eac1e84 100644 --- a/seam/resources/access_grant.py +++ b/seam/resources/access_grant.py @@ -64,10 +64,13 @@ class Errors(ResourceMapping): created_at: str error_code: str message: str - missing_device_ids: List[str] + missing_device_ids: Optional[List[str]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -101,12 +104,15 @@ class From(ResourceMapping): :ivar starts_at: Previous start time for access.""" - device_ids: List[str] - ends_at: str - starts_at: str + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), @@ -125,13 +131,16 @@ class To(ResourceMapping): :ivar starts_at: New start time for access.""" - common_code_key: str - device_ids: List[str] - ends_at: str - starts_at: str + common_code_key: Optional[str] + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( common_code_key=d.get("common_code_key", None), device_ids=d.get("device_ids", None), @@ -140,14 +149,17 @@ def from_dict(cls, d: Dict[str, Any]): ) created_at: str - from_: From + from_: Optional[From] message: str mutation_code: str - to: To - access_method_ids: List[str] + to: Optional[To] + access_method_ids: Optional[List[str]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), from_=( @@ -178,15 +190,18 @@ class RequestedAccessMethods(ResourceMapping): :ivar mode: Access method mode. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. """ - code: str + code: Optional[str] created_access_method_ids: List[str] created_at: str display_name: str - instant_key_max_use_count: int + instant_key_max_use_count: Optional[int] mode: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( code=d.get("code", None), created_access_method_ids=d.get("created_access_method_ids", None), @@ -233,8 +248,11 @@ class FailedDevices(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), error_code=d.get("error_code", None), @@ -244,15 +262,18 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str message: str warning_code: str - failed_devices: List[FailedDevices] - access_method_ids: List[str] - device_id: str - new_code: str - original_code: str - reason: str - + failed_devices: Optional[List[FailedDevices]] + access_method_ids: Optional[List[str]] + device_id: Optional[str] + new_code: Optional[str] + original_code: Optional[str] + reason: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -269,28 +290,31 @@ def from_dict(cls, d: Dict[str, Any]): ) access_grant_id: str - access_grant_key: str + access_grant_key: Optional[str] access_method_ids: List[str] - client_session_token: str + client_session_token: Optional[str] created_at: str - customization_profile_id: str + customization_profile_id: Optional[str] display_name: str - ends_at: str + ends_at: Optional[str] errors: List[Errors] - instant_key_url: str + instant_key_url: Optional[str] location_ids: List[str] - name: str + name: Optional[str] pending_mutations: List[PendingMutations] requested_access_methods: List[RequestedAccessMethods] - reservation_key: str + reservation_key: Optional[str] space_ids: List[str] starts_at: str user_identity_id: str warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_grant_id=d.get("access_grant_id", None), access_grant_key=d.get("access_grant_key", None), diff --git a/seam/resources/access_method.py b/seam/resources/access_method.py index f8d11823..595d6611 100644 --- a/seam/resources/access_method.py +++ b/seam/resources/access_method.py @@ -59,8 +59,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -91,12 +94,15 @@ class From(ResourceMapping): :ivar starts_at: Previous start time for access.""" - device_ids: List[str] - ends_at: str - starts_at: str + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), @@ -113,12 +119,15 @@ class To(ResourceMapping): :ivar starts_at: New start time for access.""" - device_ids: List[str] - ends_at: str - starts_at: str + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), @@ -126,13 +135,16 @@ def from_dict(cls, d: Dict[str, Any]): ) created_at: str - from_: From + from_: Optional[From] message: str mutation_code: str - to: To + to: Optional[To] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), from_=( @@ -161,10 +173,13 @@ class Warnings(ResourceMapping): created_at: str message: str warning_code: str - original_access_method_id: str + original_access_method_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -173,26 +188,29 @@ def from_dict(cls, d: Dict[str, Any]): ) access_method_id: str - client_session_token: str - code: str + client_session_token: Optional[str] + code: Optional[str] created_at: str - customization_profile_id: str + customization_profile_id: Optional[str] display_name: str errors: List[Errors] - instant_key_url: str - is_assignment_required: bool - is_encoding_required: bool + instant_key_url: Optional[str] + is_assignment_required: Optional[bool] + is_encoding_required: Optional[bool] is_issued: bool - is_ready_for_assignment: bool - is_ready_for_encoding: bool - issued_at: str + is_ready_for_assignment: Optional[bool] + is_ready_for_encoding: Optional[bool] + issued_at: Optional[str] mode: str pending_mutations: List[PendingMutations] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_method_id=d.get("access_method_id", None), client_session_token=d.get("client_session_token", None), diff --git a/seam/resources/acs_access_group.py b/seam/resources/acs_access_group.py index 24692f63..ca19719f 100644 --- a/seam/resources/acs_access_group.py +++ b/seam/resources/acs_access_group.py @@ -53,11 +53,14 @@ class AccessSchedule(ResourceMapping): :ivar starts_at: Date and time at which the user's access starts, in `ISO 8601 `_ format. """ - ends_at: str + ends_at: Optional[str] starts_at: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), @@ -78,8 +81,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -119,14 +125,17 @@ class From(ResourceMapping): :ivar acs_entrance_id: Old entrance ID.""" - name: str - ends_at: str - starts_at: str - acs_user_id: str - acs_entrance_id: str + name: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + acs_user_id: Optional[str] + acs_entrance_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( name=d.get("name", None), ends_at=d.get("ends_at", None), @@ -149,14 +158,17 @@ class To(ResourceMapping): :ivar acs_entrance_id: New entrance ID.""" - name: str - ends_at: str - starts_at: str - acs_user_id: str - acs_entrance_id: str + name: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + acs_user_id: Optional[str] + acs_entrance_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( name=d.get("name", None), ends_at=d.get("ends_at", None), @@ -168,13 +180,16 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str message: str mutation_code: str - from_: From - to: To - acs_user_id: str - variant: str - + from_: Optional[From] + to: Optional[To] + acs_user_id: Optional[str] + variant: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -204,8 +219,11 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -214,7 +232,7 @@ def from_dict(cls, d: Dict[str, Any]): access_group_type: str access_group_type_display_name: str - access_schedule: AccessSchedule + access_schedule: Optional[AccessSchedule] acs_access_group_id: str acs_system_id: str connected_account_id: str @@ -229,8 +247,11 @@ def from_dict(cls, d: Dict[str, Any]): warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_group_type=d.get("access_group_type", None), access_group_type_display_name=d.get( diff --git a/seam/resources/acs_credential.py b/seam/resources/acs_credential.py index 561bded7..a33ca7b1 100644 --- a/seam/resources/acs_credential.py +++ b/seam/resources/acs_credential.py @@ -88,15 +88,18 @@ class AssaAbloyVostioMetadata(ResourceMapping): :ivar override_guest_acs_entrance_ids: IDs of the guest entrances to override in the Vostio access system. """ - auto_join: bool - door_names: List[str] - endpoint_id: str - key_id: str - key_issuing_request_id: str - override_guest_acs_entrance_ids: List[str] - + auto_join: Optional[bool] + door_names: Optional[List[str]] + endpoint_id: Optional[str] + key_id: Optional[str] + key_issuing_request_id: Optional[str] + override_guest_acs_entrance_ids: Optional[List[str]] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( auto_join=d.get("auto_join", None), door_names=d.get("door_names", None), @@ -122,8 +125,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -151,17 +157,20 @@ class VisionlineMetadata(ResourceMapping): :ivar joiner_acs_credential_ids: IDs of the credentials to which you want to join. """ - auto_join: bool - card_function_type: str - card_id: str - common_acs_entrance_ids: List[str] - credential_id: str - guest_acs_entrance_ids: List[str] - is_valid: bool - joiner_acs_credential_ids: List[str] - + auto_join: Optional[bool] + card_function_type: Optional[str] + card_id: Optional[str] + common_acs_entrance_ids: Optional[List[str]] + credential_id: Optional[str] + guest_acs_entrance_ids: Optional[List[str]] + is_valid: Optional[bool] + joiner_acs_credential_ids: Optional[List[str]] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( auto_join=d.get("auto_join", None), card_function_type=d.get("card_function_type", None), @@ -188,8 +197,11 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -198,35 +210,38 @@ def from_dict(cls, d: Dict[str, Any]): access_method: str acs_credential_id: str - acs_credential_pool_id: str + acs_credential_pool_id: Optional[str] acs_system_id: str - acs_user_id: str - assa_abloy_vostio_metadata: AssaAbloyVostioMetadata - card_number: str - code: str + acs_user_id: Optional[str] + assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] + card_number: Optional[str] + code: Optional[str] connected_account_id: str created_at: str display_name: str - ends_at: str + ends_at: Optional[str] errors: List[Errors] - external_type: str - external_type_display_name: str - is_issued: bool - is_latest_desired_state_synced_with_provider: bool + external_type: Optional[str] + external_type_display_name: Optional[str] + is_issued: Optional[bool] + is_latest_desired_state_synced_with_provider: Optional[bool] is_managed: bool - is_multi_phone_sync_credential: bool - is_one_time_use: bool - issued_at: str - latest_desired_state_synced_with_provider_at: str - parent_acs_credential_id: str - starts_at: str - user_identity_id: str - visionline_metadata: VisionlineMetadata + is_multi_phone_sync_credential: Optional[bool] + is_one_time_use: Optional[bool] + issued_at: Optional[str] + latest_desired_state_synced_with_provider_at: Optional[str] + parent_acs_credential_id: Optional[str] + starts_at: Optional[str] + user_identity_id: Optional[str] + visionline_metadata: Optional[VisionlineMetadata] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_method=d.get("access_method", None), acs_credential_id=d.get("acs_credential_id", None), diff --git a/seam/resources/acs_encoder.py b/seam/resources/acs_encoder.py index 88bc5a75..b048ad95 100644 --- a/seam/resources/acs_encoder.py +++ b/seam/resources/acs_encoder.py @@ -51,8 +51,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -67,8 +70,11 @@ def from_dict(cls, d: Dict[str, Any]): errors: List[Errors] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_encoder_id=d.get("acs_encoder_id", None), acs_system_id=d.get("acs_system_id", None), diff --git a/seam/resources/acs_entrance.py b/seam/resources/acs_entrance.py index d3c40fee..6c5ccb71 100644 --- a/seam/resources/acs_entrance.py +++ b/seam/resources/acs_entrance.py @@ -81,23 +81,29 @@ class Actions(ResourceMapping): :ivar name: Name of the gadget action.""" - id: str - name: str + id: Optional[str] + name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( id=d.get("id", None), name=d.get("name", None), ) - actions: List[Actions] - gadget_id: str - site_id: str - site_name: str + actions: Optional[List[Actions]] + gadget_id: Optional[str] + site_id: Optional[str] + site_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( actions=[cls.Actions.from_dict(i) for i in d.get("actions") or []], gadget_id=d.get("gadget_id", None), @@ -120,14 +126,17 @@ class AssaAbloyVostioMetadata(ResourceMapping): :ivar stand_open: Indicates whether keys are allowed to set the door in stand open mode in the Vostio access system. """ - door_name: str - door_number: float - door_type: str - pms_id: str - stand_open: bool + door_name: Optional[str] + door_number: Optional[float] + door_type: Optional[str] + pms_id: Optional[str] + stand_open: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( door_name=d.get("door_name", None), door_number=d.get("door_number", None), @@ -154,16 +163,19 @@ class AvigilonAltaMetadata(ResourceMapping): :ivar zone_name: Zone name for an Avigilon Alta system.""" - entry_name: str - entry_relays_total_count: float - org_name: str - site_id: float - site_name: str - zone_id: float - zone_name: str - + entry_name: Optional[str] + entry_relays_total_count: Optional[float] + org_name: Optional[str] + site_id: Optional[float] + site_name: Optional[str] + zone_id: Optional[float] + zone_name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( entry_name=d.get("entry_name", None), entry_relays_total_count=d.get("entry_relays_total_count", None), @@ -184,12 +196,15 @@ class BrivoMetadata(ResourceMapping): :ivar site_name: Name of the site that the access point belongs to.""" - access_point_id: str - site_id: float - site_name: str + access_point_id: Optional[str] + site_id: Optional[float] + site_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_point_id=d.get("access_point_id", None), site_id=d.get("site_id", None), @@ -203,10 +218,13 @@ class DormakabaAmbianceMetadata(ResourceMapping): :ivar access_point_name: Name of the access point in the dormakaba Ambiance access system. """ - access_point_name: str + access_point_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_point_name=d.get("access_point_name", None), ) @@ -218,10 +236,13 @@ class DormakabaCommunityMetadata(ResourceMapping): :ivar access_point_profile: Type of access point profile in the dormakaba Community access system. """ - access_point_profile: str + access_point_profile: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_point_profile=d.get("access_point_profile", None), ) @@ -241,8 +262,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -259,12 +283,15 @@ class HotekMetadata(ResourceMapping): :ivar room_number: Room number of the entrance.""" - common_area_name: str - common_area_number: str - room_number: str + common_area_name: Optional[str] + common_area_number: Optional[str] + room_number: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( common_area_name=d.get("common_area_name", None), common_area_number=d.get("common_area_number", None), @@ -283,13 +310,16 @@ class LatchMetadata(ResourceMapping): :ivar is_connected: Indicates whether the entrance is connected.""" - accessibility_type: str - door_name: str - door_type: str - is_connected: bool + accessibility_type: Optional[str] + door_name: Optional[str] + door_type: Optional[str] + is_connected: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accessibility_type=d.get("accessibility_type", None), door_name=d.get("door_name", None), @@ -317,17 +347,20 @@ class SaltoKsMetadata(ResourceMapping): :ivar privacy_mode: Indicates whether privacy mode is enabled for the lock.""" - battery_level: str - door_name: str - intrusion_alarm: bool - left_open_alarm: bool - lock_type: str - locked_state: str - online: bool - privacy_mode: bool - + battery_level: Optional[str] + door_name: Optional[str] + intrusion_alarm: Optional[bool] + left_open_alarm: Optional[bool] + lock_type: Optional[str] + locked_state: Optional[str] + online: Optional[bool] + privacy_mode: Optional[bool] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( battery_level=d.get("battery_level", None), door_name=d.get("door_name", None), @@ -355,15 +388,18 @@ class SaltoSpaceMetadata(ResourceMapping): :ivar room_name: Name of the room in the Salto Space access system.""" - audit_on_keys: bool - door_description: str - door_id: str - door_name: str - room_description: str - room_name: str + audit_on_keys: Optional[bool] + door_description: Optional[str] + door_id: Optional[str] + door_name: Optional[str] + room_description: Optional[str] + room_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( audit_on_keys=d.get("audit_on_keys", None), door_description=d.get("door_description", None), @@ -392,11 +428,14 @@ class Profiles(ResourceMapping): :ivar visionline_door_profile_type: Door profile type in the Visionline access system. """ - visionline_door_profile_id: str - visionline_door_profile_type: str + visionline_door_profile_id: Optional[str] + visionline_door_profile_type: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( visionline_door_profile_id=d.get( "visionline_door_profile_id", None @@ -406,12 +445,15 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - door_category: str - door_name: str - profiles: List[Profiles] + door_category: Optional[str] + door_name: Optional[str] + profiles: Optional[List[Profiles]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( door_category=d.get("door_category", None), door_name=d.get("door_name", None), @@ -433,8 +475,11 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -443,32 +488,35 @@ def from_dict(cls, d: Dict[str, Any]): acs_entrance_id: str acs_system_id: str - akiles_metadata: AkilesMetadata - assa_abloy_vostio_metadata: AssaAbloyVostioMetadata - avigilon_alta_metadata: AvigilonAltaMetadata - brivo_metadata: BrivoMetadata - can_belong_to_reservation: bool - can_unlock_with_card: bool - can_unlock_with_cloud_key: bool - can_unlock_with_code: bool - can_unlock_with_mobile_key: bool + akiles_metadata: Optional[AkilesMetadata] + assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] + avigilon_alta_metadata: Optional[AvigilonAltaMetadata] + brivo_metadata: Optional[BrivoMetadata] + can_belong_to_reservation: Optional[bool] + can_unlock_with_card: Optional[bool] + can_unlock_with_cloud_key: Optional[bool] + can_unlock_with_code: Optional[bool] + can_unlock_with_mobile_key: Optional[bool] connected_account_id: str created_at: str display_name: str - dormakaba_ambiance_metadata: DormakabaAmbianceMetadata - dormakaba_community_metadata: DormakabaCommunityMetadata + dormakaba_ambiance_metadata: Optional[DormakabaAmbianceMetadata] + dormakaba_community_metadata: Optional[DormakabaCommunityMetadata] errors: List[Errors] - hotek_metadata: HotekMetadata - is_locked: bool - latch_metadata: LatchMetadata - salto_ks_metadata: SaltoKsMetadata - salto_space_metadata: SaltoSpaceMetadata + hotek_metadata: Optional[HotekMetadata] + is_locked: Optional[bool] + latch_metadata: Optional[LatchMetadata] + salto_ks_metadata: Optional[SaltoKsMetadata] + salto_space_metadata: Optional[SaltoSpaceMetadata] space_ids: List[str] - visionline_metadata: VisionlineMetadata + visionline_metadata: Optional[VisionlineMetadata] warnings: List[Warnings] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_entrance_id=d.get("acs_entrance_id", None), acs_system_id=d.get("acs_system_id", None), diff --git a/seam/resources/acs_system.py b/seam/resources/acs_system.py index 933eaecd..3c1ebf54 100644 --- a/seam/resources/acs_system.py +++ b/seam/resources/acs_system.py @@ -69,10 +69,13 @@ class Errors(ResourceMapping): created_at: str error_code: str message: str - is_bridge_error: bool + is_bridge_error: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -87,10 +90,13 @@ class Location(ResourceMapping): :ivar time_zone: Time zone in which the `access control system `_ is located. """ - time_zone: str + time_zone: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( time_zone=d.get("time_zone", None), ) @@ -106,12 +112,15 @@ class VisionlineMetadata(ResourceMapping): :ivar system_id: Unique ID assigned by the ASSA ABLOY licensing team that identifies each hotel in your credential manager. """ - lan_address: str - mobile_access_uuid: str - system_id: str + lan_address: Optional[str] + mobile_access_uuid: Optional[str] + system_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( lan_address=d.get("lan_address", None), mobile_access_uuid=d.get("mobile_access_uuid", None), @@ -133,10 +142,13 @@ class Warnings(ResourceMapping): created_at: str message: str warning_code: str - misconfigured_acs_entrance_ids: List[str] + misconfigured_acs_entrance_ids: Optional[List[str]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -146,29 +158,32 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - acs_access_group_count: float + acs_access_group_count: Optional[float] acs_system_id: str - acs_user_count: float + acs_user_count: Optional[float] connected_account_id: str connected_account_ids: List[str] created_at: str - default_credential_manager_acs_system_id: str + default_credential_manager_acs_system_id: Optional[str] errors: List[Errors] - external_type: str - external_type_display_name: str + external_type: Optional[str] + external_type_display_name: Optional[str] image_alt_text: str image_url: str is_credential_manager: bool - location: Location + location: Optional[Location] name: str - system_type: str - system_type_display_name: str - visionline_metadata: VisionlineMetadata + system_type: Optional[str] + system_type_display_name: Optional[str] + visionline_metadata: Optional[VisionlineMetadata] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_access_group_count=d.get("acs_access_group_count", None), acs_system_id=d.get("acs_system_id", None), diff --git a/seam/resources/acs_user.py b/seam/resources/acs_user.py index faef5196..18ce9aa2 100644 --- a/seam/resources/acs_user.py +++ b/seam/resources/acs_user.py @@ -72,11 +72,14 @@ class AccessSchedule(ResourceMapping): :ivar starts_at: Date and time at which the user's access starts, in `ISO 8601 `_ format. """ - ends_at: str + ends_at: Optional[str] starts_at: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), @@ -97,8 +100,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -146,17 +152,20 @@ class From(ResourceMapping): :ivar acs_credential_id: Previous credential ID.""" - email_address: str - full_name: str - phone_number: str - ends_at: str - starts_at: str - is_suspended: bool - acs_access_group_id: str - acs_credential_id: str - + email_address: Optional[str] + full_name: Optional[str] + phone_number: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + is_suspended: Optional[bool] + acs_access_group_id: Optional[str] + acs_credential_id: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( email_address=d.get("email_address", None), full_name=d.get("full_name", None), @@ -188,17 +197,20 @@ class To(ResourceMapping): :ivar acs_credential_id: New credential ID.""" - email_address: str - full_name: str - phone_number: str - ends_at: str - starts_at: str - is_suspended: bool - acs_access_group_id: str - acs_credential_id: str - + email_address: Optional[str] + full_name: Optional[str] + phone_number: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + is_suspended: Optional[bool] + acs_access_group_id: Optional[str] + acs_credential_id: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( email_address=d.get("email_address", None), full_name=d.get("full_name", None), @@ -213,14 +225,17 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str message: str mutation_code: str - scheduled_at: str - from_: From - to: To - acs_access_group_id: str - variant: str - + scheduled_at: Optional[str] + from_: Optional[From] + to: Optional[To] + acs_access_group_id: Optional[str] + variant: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -243,10 +258,13 @@ class SaltoKsMetadata(ResourceMapping): :ivar is_subscribed: Indicates whether the user holds an active subscription slot on the Salto KS site. Only subscribed users can unlock doors and count against the site's user-subscription limit. A user may not be subscribed because their access schedule has not started or has ended, the site has reached its subscription limit, or they were manually unsubscribed. This is distinct from ``is_suspended``, which reflects whether the user has been explicitly blocked. """ - is_subscribed: bool + is_subscribed: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( is_subscribed=d.get("is_subscribed", None), ) @@ -259,11 +277,14 @@ class SaltoSpaceMetadata(ResourceMapping): :ivar user_id: User ID in the Salto Space access system.""" - audit_openings: bool - user_id: str + audit_openings: Optional[bool] + user_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( audit_openings=d.get("audit_openings", None), user_id=d.get("user_id", None), @@ -283,42 +304,48 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), warning_code=d.get("warning_code", None), ) - access_schedule: AccessSchedule + access_schedule: Optional[AccessSchedule] acs_system_id: str acs_user_id: str connected_account_id: str created_at: str display_name: str - email: str - email_address: str + email: Optional[str] + email_address: Optional[str] errors: List[Errors] - external_type: str - external_type_display_name: str - full_name: str - hid_acs_system_id: str + external_type: Optional[str] + external_type_display_name: Optional[str] + full_name: Optional[str] + hid_acs_system_id: Optional[str] is_managed: bool - is_suspended: bool - pending_mutations: List[PendingMutations] - phone_number: str - salto_ks_metadata: SaltoKsMetadata - salto_space_metadata: SaltoSpaceMetadata - user_identity_email_address: str - user_identity_full_name: str - user_identity_id: str - user_identity_phone_number: str + is_suspended: Optional[bool] + pending_mutations: Optional[List[PendingMutations]] + phone_number: Optional[str] + salto_ks_metadata: Optional[SaltoKsMetadata] + salto_space_metadata: Optional[SaltoSpaceMetadata] + user_identity_email_address: Optional[str] + user_identity_full_name: Optional[str] + user_identity_id: Optional[str] + user_identity_phone_number: Optional[str] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_schedule=( cls.AccessSchedule.from_dict(d.get("access_schedule")) diff --git a/seam/resources/action_attempt.py b/seam/resources/action_attempt.py index 21e4a97a..a9bb54b3 100644 --- a/seam/resources/action_attempt.py +++ b/seam/resources/action_attempt.py @@ -29,8 +29,11 @@ class Error(ResourceMapping): message: str type: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( message=d.get("message", None), type=d.get("type", None), @@ -169,21 +172,24 @@ class VisionlineMetadata(ResourceMapping): :ivar pending_auto_update: Indicates whether the card associated with the `credential `_ is pending auto-update. """ - cancelled: bool - card_format: str - card_holder: str - card_id: str - common_acs_entrance_ids: List[str] - discarded: bool - expired: bool - guest_acs_entrance_ids: List[str] - number_of_issued_cards: float - overridden: bool - overwritten: bool - pending_auto_update: bool - + cancelled: Optional[bool] + card_format: Optional[str] + card_holder: Optional[str] + card_id: Optional[str] + common_acs_entrance_ids: Optional[List[str]] + discarded: Optional[bool] + expired: Optional[bool] + guest_acs_entrance_ids: Optional[List[str]] + number_of_issued_cards: Optional[float] + overridden: Optional[bool] + overwritten: Optional[bool] + pending_auto_update: Optional[bool] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( cancelled=d.get("cancelled", None), card_format=d.get("card_format", None), @@ -199,15 +205,18 @@ def from_dict(cls, d: Dict[str, Any]): pending_auto_update=d.get("pending_auto_update", None), ) - card_number: str - created_at: str - ends_at: str - is_issued: bool - starts_at: str - visionline_metadata: VisionlineMetadata + card_number: Optional[str] + created_at: Optional[str] + ends_at: Optional[str] + is_issued: Optional[bool] + starts_at: Optional[str] + visionline_metadata: Optional[VisionlineMetadata] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( card_number=d.get("card_number", None), created_at=d.get("created_at", None), @@ -299,15 +308,18 @@ class AssaAbloyVostioMetadata(ResourceMapping): :ivar override_guest_acs_entrance_ids: IDs of the guest entrances to override in the Vostio access system. """ - auto_join: bool - door_names: List[str] - endpoint_id: str - key_id: str - key_issuing_request_id: str - override_guest_acs_entrance_ids: List[str] + auto_join: Optional[bool] + door_names: Optional[List[str]] + endpoint_id: Optional[str] + key_id: Optional[str] + key_issuing_request_id: Optional[str] + override_guest_acs_entrance_ids: Optional[List[str]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( auto_join=d.get("auto_join", None), door_names=d.get("door_names", None), @@ -333,8 +345,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -362,17 +377,20 @@ class VisionlineMetadata(ResourceMapping): :ivar joiner_acs_credential_ids: IDs of the credentials to which you want to join. """ - auto_join: bool - card_function_type: str - card_id: str - common_acs_entrance_ids: List[str] - credential_id: str - guest_acs_entrance_ids: List[str] - is_valid: bool - joiner_acs_credential_ids: List[str] - + auto_join: Optional[bool] + card_function_type: Optional[str] + card_id: Optional[str] + common_acs_entrance_ids: Optional[List[str]] + credential_id: Optional[str] + guest_acs_entrance_ids: Optional[List[str]] + is_valid: Optional[bool] + joiner_acs_credential_ids: Optional[List[str]] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( auto_join=d.get("auto_join", None), card_function_type=d.get("card_function_type", None), @@ -401,8 +419,11 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -411,35 +432,38 @@ def from_dict(cls, d: Dict[str, Any]): access_method: str acs_credential_id: str - acs_credential_pool_id: str + acs_credential_pool_id: Optional[str] acs_system_id: str - acs_user_id: str - assa_abloy_vostio_metadata: AssaAbloyVostioMetadata - card_number: str - code: str + acs_user_id: Optional[str] + assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] + card_number: Optional[str] + code: Optional[str] connected_account_id: str created_at: str display_name: str - ends_at: str + ends_at: Optional[str] errors: List[Errors] - external_type: str - external_type_display_name: str - is_issued: bool - is_latest_desired_state_synced_with_provider: bool + external_type: Optional[str] + external_type_display_name: Optional[str] + is_issued: Optional[bool] + is_latest_desired_state_synced_with_provider: Optional[bool] is_managed: bool - is_multi_phone_sync_credential: bool - is_one_time_use: bool - issued_at: str - latest_desired_state_synced_with_provider_at: str - parent_acs_credential_id: str - starts_at: str - user_identity_id: str - visionline_metadata: VisionlineMetadata + is_multi_phone_sync_credential: Optional[bool] + is_one_time_use: Optional[bool] + issued_at: Optional[str] + latest_desired_state_synced_with_provider_at: Optional[str] + parent_acs_credential_id: Optional[str] + starts_at: Optional[str] + user_identity_id: Optional[str] + visionline_metadata: Optional[VisionlineMetadata] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_method=d.get("access_method", None), acs_credential_id=d.get("acs_credential_id", None), @@ -507,13 +531,16 @@ class Warnings(ResourceMapping): """ warning_code: str - warning_message: str - created_at: str - message: str - original_access_method_id: str - + warning_message: Optional[str] + created_at: Optional[str] + message: Optional[str] + original_access_method_id: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( warning_code=d.get("warning_code", None), warning_message=d.get("warning_message", None), @@ -539,15 +566,18 @@ class AssaAbloyVostioMetadata(ResourceMapping): :ivar override_guest_acs_entrance_ids: IDs of the guest entrances to override in the Vostio access system. """ - auto_join: bool - door_names: List[str] - endpoint_id: str - key_id: str - key_issuing_request_id: str - override_guest_acs_entrance_ids: List[str] + auto_join: Optional[bool] + door_names: Optional[List[str]] + endpoint_id: Optional[str] + key_id: Optional[str] + key_issuing_request_id: Optional[str] + override_guest_acs_entrance_ids: Optional[List[str]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( auto_join=d.get("auto_join", None), door_names=d.get("door_names", None), @@ -574,8 +604,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -603,17 +636,20 @@ class VisionlineMetadata(ResourceMapping): :ivar joiner_acs_credential_ids: IDs of the credentials to which you want to join. """ - auto_join: bool - card_function_type: str - card_id: str - common_acs_entrance_ids: List[str] - credential_id: str - guest_acs_entrance_ids: List[str] - is_valid: bool - joiner_acs_credential_ids: List[str] - + auto_join: Optional[bool] + card_function_type: Optional[str] + card_id: Optional[str] + common_acs_entrance_ids: Optional[List[str]] + credential_id: Optional[str] + guest_acs_entrance_ids: Optional[List[str]] + is_valid: Optional[bool] + joiner_acs_credential_ids: Optional[List[str]] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( auto_join=d.get("auto_join", None), card_function_type=d.get("card_function_type", None), @@ -647,11 +683,14 @@ class From(ResourceMapping): :ivar starts_at: Previous start time for access.""" - ends_at: str - starts_at: str + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), @@ -665,24 +704,30 @@ class To(ResourceMapping): :ivar starts_at: New start time for access.""" - ends_at: str - starts_at: str + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) created_at: str - from_: From + from_: Optional[From] message: str mutation_code: str - to: To + to: Optional[To] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), from_=( @@ -699,50 +744,53 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - was_confirmed_by_device: bool - acs_credential_on_encoder: AcsCredentialOnEncoder - acs_credential_on_seam: AcsCredentialOnSeam - warnings: List[Warnings] - access_method: str - acs_credential_id: str - acs_credential_pool_id: str - acs_system_id: str - acs_user_id: str - assa_abloy_vostio_metadata: AssaAbloyVostioMetadata - card_number: str - code: str - connected_account_id: str - created_at: str - display_name: str - ends_at: str - errors: List[Errors] - external_type: str - external_type_display_name: str - is_issued: bool - is_latest_desired_state_synced_with_provider: bool - is_managed: bool - is_multi_phone_sync_credential: bool - is_one_time_use: bool - issued_at: str - latest_desired_state_synced_with_provider_at: str - parent_acs_credential_id: str - starts_at: str - user_identity_id: str - visionline_metadata: VisionlineMetadata - workspace_id: str - access_method_id: str - client_session_token: str - customization_profile_id: str - instant_key_url: str - is_assignment_required: bool - is_encoding_required: bool - is_ready_for_assignment: bool - is_ready_for_encoding: bool - mode: str - pending_mutations: List[PendingMutations] - + was_confirmed_by_device: Optional[bool] + acs_credential_on_encoder: Optional[AcsCredentialOnEncoder] + acs_credential_on_seam: Optional[AcsCredentialOnSeam] + warnings: Optional[List[Warnings]] + access_method: Optional[str] + acs_credential_id: Optional[str] + acs_credential_pool_id: Optional[str] + acs_system_id: Optional[str] + acs_user_id: Optional[str] + assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] + card_number: Optional[str] + code: Optional[str] + connected_account_id: Optional[str] + created_at: Optional[str] + display_name: Optional[str] + ends_at: Optional[str] + errors: Optional[List[Errors]] + external_type: Optional[str] + external_type_display_name: Optional[str] + is_issued: Optional[bool] + is_latest_desired_state_synced_with_provider: Optional[bool] + is_managed: Optional[bool] + is_multi_phone_sync_credential: Optional[bool] + is_one_time_use: Optional[bool] + issued_at: Optional[str] + latest_desired_state_synced_with_provider_at: Optional[str] + parent_acs_credential_id: Optional[str] + starts_at: Optional[str] + user_identity_id: Optional[str] + visionline_metadata: Optional[VisionlineMetadata] + workspace_id: Optional[str] + access_method_id: Optional[str] + client_session_token: Optional[str] + customization_profile_id: Optional[str] + instant_key_url: Optional[str] + is_assignment_required: Optional[bool] + is_encoding_required: Optional[bool] + is_ready_for_assignment: Optional[bool] + is_ready_for_encoding: Optional[bool] + mode: Optional[str] + pending_mutations: Optional[List[PendingMutations]] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( was_confirmed_by_device=d.get("was_confirmed_by_device", None), acs_credential_on_encoder=( @@ -818,12 +866,15 @@ def from_dict(cls, d: Dict[str, Any]): action_attempt_id: str action_type: str - error: Error - result: Result + error: Optional[Error] + result: Optional[Result] status: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), diff --git a/seam/resources/batch.py b/seam/resources/batch.py index 1313eb3e..6e4c2e2b 100644 --- a/seam/resources/batch.py +++ b/seam/resources/batch.py @@ -134,33 +134,36 @@ class Batch: :ivar workspaces: Represents a Seam `workspace `_. A workspace is a top-level entity that encompasses all other resources below it, such as devices, connected accounts, and Connect Webviews. Seam provides two types of workspaces. A `sandbox workspace `_ is a special type of workspace designed for testing code. Sandbox workspaces offer test device accounts and virtual devices that you can connect and control. This ability to work with virtual devices is quite handy because it removes the need to own physical devices from multiple brands. To connect real devices and systems to Seam, use a `production workspace `_. """ - access_codes: List[Dict[str, Any]] - access_grants: List[Dict[str, Any]] - access_methods: List[Dict[str, Any]] - acs_access_groups: List[Dict[str, Any]] - acs_credentials: List[Dict[str, Any]] - acs_encoders: List[Dict[str, Any]] - acs_entrances: List[Dict[str, Any]] - acs_systems: List[Dict[str, Any]] - acs_users: List[Dict[str, Any]] - action_attempts: List[Dict[str, Any]] - client_sessions: List[Dict[str, Any]] - connect_webviews: List[Dict[str, Any]] - connected_accounts: List[Dict[str, Any]] - devices: List[Dict[str, Any]] - events: List[Dict[str, Any]] - instant_keys: List[Dict[str, Any]] - noise_thresholds: List[Dict[str, Any]] - spaces: List[Dict[str, Any]] - thermostat_daily_programs: List[Dict[str, Any]] - thermostat_schedules: List[Dict[str, Any]] - unmanaged_access_codes: List[Dict[str, Any]] - unmanaged_devices: List[Dict[str, Any]] - user_identities: List[Dict[str, Any]] - workspaces: List[Dict[str, Any]] - + access_codes: Optional[List[Dict[str, Any]]] + access_grants: Optional[List[Dict[str, Any]]] + access_methods: Optional[List[Dict[str, Any]]] + acs_access_groups: Optional[List[Dict[str, Any]]] + acs_credentials: Optional[List[Dict[str, Any]]] + acs_encoders: Optional[List[Dict[str, Any]]] + acs_entrances: Optional[List[Dict[str, Any]]] + acs_systems: Optional[List[Dict[str, Any]]] + acs_users: Optional[List[Dict[str, Any]]] + action_attempts: Optional[List[Dict[str, Any]]] + client_sessions: Optional[List[Dict[str, Any]]] + connect_webviews: Optional[List[Dict[str, Any]]] + connected_accounts: Optional[List[Dict[str, Any]]] + devices: Optional[List[Dict[str, Any]]] + events: Optional[List[Dict[str, Any]]] + instant_keys: Optional[List[Dict[str, Any]]] + noise_thresholds: Optional[List[Dict[str, Any]]] + spaces: Optional[List[Dict[str, Any]]] + thermostat_daily_programs: Optional[List[Dict[str, Any]]] + thermostat_schedules: Optional[List[Dict[str, Any]]] + unmanaged_access_codes: Optional[List[Dict[str, Any]]] + unmanaged_devices: Optional[List[Dict[str, Any]]] + user_identities: Optional[List[Dict[str, Any]]] + workspaces: Optional[List[Dict[str, Any]]] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_codes=d.get("access_codes", None), access_grants=d.get("access_grants", None), diff --git a/seam/resources/client_session.py b/seam/resources/client_session.py index fb289c16..38456094 100644 --- a/seam/resources/client_session.py +++ b/seam/resources/client_session.py @@ -44,17 +44,20 @@ class ClientSession: connect_webview_ids: List[str] connected_account_ids: List[str] created_at: str - customer_key: str + customer_key: Optional[str] device_count: float expires_at: str token: str - user_identifier_key: str - user_identity_id: str + user_identifier_key: Optional[str] + user_identity_id: Optional[str] user_identity_ids: List[str] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( client_session_id=d.get("client_session_id", None), connect_webview_ids=d.get("connect_webview_ids", None), diff --git a/seam/resources/connect_webview.py b/seam/resources/connect_webview.py index fc948390..77d06b1d 100644 --- a/seam/resources/connect_webview.py +++ b/seam/resources/connect_webview.py @@ -59,25 +59,28 @@ class ConnectWebview: accepted_capabilities: List[str] accepted_providers: List[str] any_provider_allowed: bool - authorized_at: str + authorized_at: Optional[str] automatically_manage_new_devices: bool connect_webview_id: str - connected_account_id: str + connected_account_id: Optional[str] created_at: str custom_metadata: Dict[str, Any] - custom_redirect_failure_url: str - custom_redirect_url: str - customer_key: str + custom_redirect_failure_url: Optional[str] + custom_redirect_url: Optional[str] + customer_key: Optional[str] device_selection_mode: str login_successful: bool - selected_provider: str + selected_provider: Optional[str] status: str url: str wait_for_device_creation: bool workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accepted_capabilities=d.get("accepted_capabilities", None), accepted_providers=d.get("accepted_providers", None), diff --git a/seam/resources/connected_account.py b/seam/resources/connected_account.py index 9cceb75d..a7277447 100644 --- a/seam/resources/connected_account.py +++ b/seam/resources/connected_account.py @@ -81,13 +81,16 @@ class Sites(ResourceMapping): :ivar subscribed_site_user_count: Count of subscribed site users for a Salto site associated with the connected account that has an error. """ - site_id: str - site_name: str - site_user_subscription_limit: int - subscribed_site_user_count: int - + site_id: Optional[str] + site_name: Optional[str] + site_user_subscription_limit: Optional[int] + subscribed_site_user_count: Optional[int] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( site_id=d.get("site_id", None), site_name=d.get("site_name", None), @@ -99,23 +102,29 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - sites: List[Sites] + sites: Optional[List[Sites]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( sites=[cls.Sites.from_dict(i) for i in d.get("sites") or []], ) created_at: str error_code: str - is_bridge_error: bool - is_connected_account_error: bool + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] message: str - salto_ks_metadata: SaltoKsMetadata + salto_ks_metadata: Optional[SaltoKsMetadata] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -144,14 +153,17 @@ class UserIdentifier(ResourceMapping): :ivar username: Username of the user identifier associated with the connected account. """ - api_url: str - email: str - exclusive: bool - phone: str - username: str + api_url: Optional[str] + email: Optional[str] + exclusive: Optional[bool] + phone: Optional[str] + username: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( api_url=d.get("api_url", None), email=d.get("email", None), @@ -193,13 +205,16 @@ class Sites(ResourceMapping): :ivar subscribed_site_user_count: Count of subscribed site users for a Salto site associated with the connected account that has a warning. """ - site_id: str - site_name: str - site_user_subscription_limit: int - subscribed_site_user_count: int + site_id: Optional[str] + site_name: Optional[str] + site_user_subscription_limit: Optional[int] + subscribed_site_user_count: Optional[int] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( site_id=d.get("site_id", None), site_name=d.get("site_name", None), @@ -211,10 +226,13 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - sites: List[Sites] + sites: Optional[List[Sites]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( sites=[cls.Sites.from_dict(i) for i in d.get("sites") or []], ) @@ -222,10 +240,13 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str message: str warning_code: str - salto_ks_metadata: SaltoKsMetadata + salto_ks_metadata: Optional[SaltoKsMetadata] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -238,26 +259,29 @@ def from_dict(cls, d: Dict[str, Any]): ) accepted_capabilities: List[str] - account_type: str + account_type: Optional[str] account_type_display_name: str automatically_manage_new_devices: bool connected_account_id: str - created_at: str + created_at: Optional[str] custom_metadata: Dict[str, Any] - customer_key: str - default_checkin_time: str - default_checkout_time: str + customer_key: Optional[str] + default_checkin_time: Optional[str] + default_checkout_time: Optional[str] display_name: str errors: List[Errors] - ical_feed_origin: str - ical_url: str - image_url: str - time_zone: str - user_identifier: UserIdentifier + ical_feed_origin: Optional[str] + ical_url: Optional[str] + image_url: Optional[str] + time_zone: Optional[str] + user_identifier: Optional[UserIdentifier] warnings: List[Warnings] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accepted_capabilities=d.get("accepted_capabilities", None), account_type=d.get("account_type", None), diff --git a/seam/resources/customer_portal.py b/seam/resources/customer_portal.py index 120cbc7a..fa9a7f82 100644 --- a/seam/resources/customer_portal.py +++ b/seam/resources/customer_portal.py @@ -28,8 +28,11 @@ class CustomerPortal: url: str workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), customer_key=d.get("customer_key", None), diff --git a/seam/resources/device.py b/seam/resources/device.py index 8d86fe69..65c2a775 100644 --- a/seam/resources/device.py +++ b/seam/resources/device.py @@ -95,11 +95,14 @@ class DeviceManufacturer(ResourceMapping): """ display_name: str - image_url: str + image_url: Optional[str] manufacturer: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( display_name=d.get("display_name", None), image_url=d.get("image_url", None), @@ -121,11 +124,14 @@ class DeviceProvider(ResourceMapping): device_provider_name: str display_name: str - image_url: str + image_url: Optional[str] provider_category: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_provider_name=d.get("device_provider_name", None), display_name=d.get("display_name", None), @@ -152,13 +158,16 @@ class Errors(ResourceMapping): created_at: str error_code: str - is_connected_account_error: bool - is_device_error: bool + is_connected_account_error: Optional[bool] + is_device_error: Optional[bool] message: str - is_bridge_error: bool + is_bridge_error: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -179,12 +188,15 @@ class Location(ResourceMapping): :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. """ - location_name: str - time_zone: str - timezone: str + location_name: Optional[str] + time_zone: Optional[str] + timezone: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( location_name=d.get("location_name", None), time_zone=d.get("time_zone", None), @@ -421,17 +433,23 @@ class Battery(ResourceMapping): level: float + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( level=d.get("level", None), ) - battery: Battery + battery: Optional[Battery] is_connected: bool + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( battery=( cls.Battery.from_dict(d.get("battery")) @@ -450,8 +468,11 @@ class Appearance(ResourceMapping): name: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( name=d.get("name", None), ) @@ -468,8 +489,11 @@ class Battery(ResourceMapping): level: float status: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( level=d.get("level", None), status=d.get("status", None), @@ -494,16 +518,19 @@ class Model(ResourceMapping): :ivar online_access_codes_supported: Deprecated: use device.can_program_online_access_codes. """ - accessory_keypad_supported: bool - can_connect_accessory_keypad: bool + accessory_keypad_supported: Optional[bool] + can_connect_accessory_keypad: Optional[bool] display_name: str - has_built_in_keypad: bool + has_built_in_keypad: Optional[bool] manufacturer_display_name: str - offline_access_codes_supported: bool - online_access_codes_supported: bool + offline_access_codes_supported: Optional[bool] + online_access_codes_supported: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accessory_keypad_supported=d.get( "accessory_keypad_supported", None @@ -539,21 +566,27 @@ class Endpoints(ResourceMapping): :ivar is_active: Indicated whether the endpoint is active.""" - endpoint_id: str - is_active: bool + endpoint_id: Optional[str] + is_active: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( endpoint_id=d.get("endpoint_id", None), is_active=d.get("is_active", None), ) - endpoints: List[Endpoints] - has_active_endpoint: bool + endpoints: Optional[List[Endpoints]] + has_active_endpoint: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( endpoints=[ cls.Endpoints.from_dict(i) for i in d.get("endpoints") or [] @@ -568,10 +601,13 @@ class SaltoSpaceCredentialServiceMetadata(ResourceMapping): :ivar has_active_phone: Indicates whether the credential service has an active associated phone. """ - has_active_phone: bool + has_active_phone: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( has_active_phone=d.get("has_active_phone", None), ) @@ -588,13 +624,16 @@ class AkilesMetadata(ResourceMapping): :ivar product_name: Product name for an Akiles device.""" - _member_group_id: str - gadget_id: str - gadget_name: str - product_name: str + _member_group_id: Optional[str] + gadget_id: Optional[str] + gadget_name: Optional[str] + product_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( _member_group_id=d.get("_member_group_id", None), gadget_id=d.get("gadget_id", None), @@ -622,17 +661,20 @@ class AqaraMetadata(ResourceMapping): :ivar time_zone: Time zone reported for an Aqara device (e.g. GMT-07:00).""" - device_name: str - did: str - firmware_version: str - model: str - model_type: float - parent_did: str - position_id: str - time_zone: str - + device_name: Optional[str] + did: Optional[str] + firmware_version: Optional[str] + model: Optional[str] + model_type: Optional[float] + parent_did: Optional[str] + position_id: Optional[str] + time_zone: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_name=d.get("device_name", None), did=d.get("did", None), @@ -650,10 +692,13 @@ class AssaAbloyVostioMetadata(ResourceMapping): :ivar encoder_name: Encoder name for an ASSA ABLOY Vostio system.""" - encoder_name: str + encoder_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( encoder_name=d.get("encoder_name", None), ) @@ -676,16 +721,19 @@ class AugustMetadata(ResourceMapping): :ivar model: Model for an August device.""" - has_keypad: bool - house_id: str - house_name: str - keypad_battery_level: str - lock_id: str - lock_name: str - model: str - + has_keypad: Optional[bool] + house_id: Optional[str] + house_name: Optional[str] + keypad_battery_level: Optional[str] + lock_id: Optional[str] + lock_name: Optional[str] + model: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( has_keypad=d.get("has_keypad", None), house_id=d.get("house_id", None), @@ -714,16 +762,19 @@ class AvigilonAltaMetadata(ResourceMapping): :ivar zone_name: Zone name for an Avigilon Alta system.""" - entry_name: str - entry_relays_total_count: float - org_name: str - site_id: float - site_name: str - zone_id: float - zone_name: str - + entry_name: Optional[str] + entry_relays_total_count: Optional[float] + org_name: Optional[str] + site_id: Optional[float] + site_name: Optional[str] + zone_id: Optional[float] + zone_name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( entry_name=d.get("entry_name", None), entry_relays_total_count=d.get("entry_relays_total_count", None), @@ -742,11 +793,14 @@ class BrivoMetadata(ResourceMapping): :ivar device_name: Device name for a Brivo device.""" - activation_enabled: bool - device_name: str + activation_enabled: Optional[bool] + device_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( activation_enabled=d.get("activation_enabled", None), device_name=d.get("device_name", None), @@ -762,12 +816,15 @@ class ControlbywebMetadata(ResourceMapping): :ivar relay_name: Relay name for a ControlByWeb device.""" - device_id: str - device_name: str - relay_name: str + device_id: Optional[str] + device_name: Optional[str] + relay_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -798,8 +855,11 @@ class DormakabaOracodeMetadata(ResourceMapping): class DeviceId(ResourceMapping): """Device ID for a dormakaba Oracode device.""" + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): # This shape documents no properties, so there is nothing to read. # pylint: disable=unused-argument return cls() @@ -828,19 +888,22 @@ class PredefinedTimeSlots(ResourceMapping): :ivar prefix: Prefix for a time slot for a dormakaba Oracode device.""" - check_in_time: str - check_out_time: str - dormakaba_oracode_user_level_id: str - dormakaba_oracode_user_level_prefix: float - is_24_hour: bool - is_biweekly_mode: bool - is_master: bool - is_one_shot: bool - name: str - prefix: float - + check_in_time: Optional[str] + check_out_time: Optional[str] + dormakaba_oracode_user_level_id: Optional[str] + dormakaba_oracode_user_level_prefix: Optional[float] + is_24_hour: Optional[bool] + is_biweekly_mode: Optional[bool] + is_master: Optional[bool] + is_one_shot: Optional[bool] + name: Optional[str] + prefix: Optional[float] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( check_in_time=d.get("check_in_time", None), check_out_time=d.get("check_out_time", None), @@ -858,17 +921,20 @@ def from_dict(cls, d: Dict[str, Any]): prefix=d.get("prefix", None), ) - device_id: DeviceId - door_id: float - door_is_wireless: bool - door_name: str - iana_timezone: str - predefined_time_slots: List[PredefinedTimeSlots] - site_id: float - site_name: str - + device_id: Optional[DeviceId] + door_id: Optional[float] + door_is_wireless: Optional[bool] + door_name: Optional[str] + iana_timezone: Optional[str] + predefined_time_slots: Optional[List[PredefinedTimeSlots]] + site_id: Optional[float] + site_name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=( cls.DeviceId.from_dict(d.get("device_id")) @@ -895,11 +961,14 @@ class EcobeeMetadata(ResourceMapping): :ivar ecobee_device_id: Device ID for an ecobee device.""" - device_name: str - ecobee_device_id: str + device_name: Optional[str] + ecobee_device_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_name=d.get("device_name", None), ecobee_device_id=d.get("ecobee_device_id", None), @@ -916,12 +985,15 @@ class FourSuitesMetadata(ResourceMapping): :ivar reclose_delay_in_seconds: Reclose delay, in seconds, for a 4SUITES device. """ - device_id: float - device_name: str - reclose_delay_in_seconds: float + device_id: Optional[float] + device_name: Optional[str] + reclose_delay_in_seconds: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -936,11 +1008,14 @@ class GenieMetadata(ResourceMapping): :ivar door_name: Door name for a Genie device.""" - device_name: str - door_name: str + device_name: Optional[str] + door_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_name=d.get("device_name", None), door_name=d.get("door_name", None), @@ -955,11 +1030,14 @@ class HoneywellResideoMetadata(ResourceMapping): :ivar honeywell_resideo_device_id: Device ID for a Honeywell Resideo device. """ - device_name: str - honeywell_resideo_device_id: str + device_name: Optional[str] + honeywell_resideo_device_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_name=d.get("device_name", None), honeywell_resideo_device_id=d.get( @@ -977,12 +1055,15 @@ class IglooMetadata(ResourceMapping): :ivar model: Model for an igloo device.""" - bridge_id: str - device_id: str - model: str + bridge_id: Optional[str] + device_id: Optional[str] + model: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( bridge_id=d.get("bridge_id", None), device_id=d.get("device_id", None), @@ -1005,15 +1086,18 @@ class IgloohomeMetadata(ResourceMapping): :ivar keypad_id: Keypad ID for an igloohome device.""" - bridge_id: str - bridge_name: str - device_id: str - device_name: str - is_accessory_keypad_linked_to_bridge: bool - keypad_id: str + bridge_id: Optional[str] + bridge_name: Optional[str] + device_id: Optional[str] + device_name: Optional[str] + is_accessory_keypad_linked_to_bridge: Optional[bool] + keypad_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( bridge_id=d.get("bridge_id", None), bridge_name=d.get("bridge_name", None), @@ -1071,30 +1155,33 @@ class KeynestMetadata(ResourceMapping): :ivar subscription_plan: Subscription plan for a KeyNest device.""" - address: str - current_or_last_store_id: float - current_status: str - current_user_company: str - current_user_email: str - current_user_name: str - current_user_phone_number: str - default_office_id: float - device_name: str - fob_id: float - handover_method: str - has_photo: bool - is_quadient_locker: bool - key_id: str - key_notes: str - keynest_app_user: str - last_movement: str - property_id: str - property_postcode: str - status_type: str - subscription_plan: str - + address: Optional[str] + current_or_last_store_id: Optional[float] + current_status: Optional[str] + current_user_company: Optional[str] + current_user_email: Optional[str] + current_user_name: Optional[str] + current_user_phone_number: Optional[str] + default_office_id: Optional[float] + device_name: Optional[str] + fob_id: Optional[float] + handover_method: Optional[str] + has_photo: Optional[bool] + is_quadient_locker: Optional[bool] + key_id: Optional[str] + key_notes: Optional[str] + keynest_app_user: Optional[str] + last_movement: Optional[str] + property_id: Optional[str] + property_postcode: Optional[str] + status_type: Optional[str] + subscription_plan: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( address=d.get("address", None), current_or_last_store_id=d.get("current_or_last_store_id", None), @@ -1131,13 +1218,16 @@ class KisiMetadata(ResourceMapping): :ivar place_name: Place name for a Kisi device.""" - description: str - lock_id: float - lock_name: str - place_name: str + description: Optional[str] + lock_id: Optional[float] + lock_name: Optional[str] + place_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( description=d.get("description", None), lock_id=d.get("lock_id", None), @@ -1164,16 +1254,19 @@ class KorelockMetadata(ResourceMapping): :ivar wifi_signal_strength: WiFi signal strength (0-1) for a Korelock device. """ - device_id: str - device_name: str - firmware_version: str - location_id: str - model_code: str - serial_number: str - wifi_signal_strength: float - + device_id: Optional[str] + device_name: Optional[str] + firmware_version: Optional[str] + location_id: Optional[str] + model_code: Optional[str] + serial_number: Optional[str] + wifi_signal_strength: Optional[float] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1194,12 +1287,15 @@ class KwiksetMetadata(ResourceMapping): :ivar model_number: Model number for a Kwikset device.""" - device_id: str - device_name: str - model_number: str + device_id: Optional[str] + device_name: Optional[str] + model_number: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1216,12 +1312,15 @@ class LocklyMetadata(ResourceMapping): :ivar model: Model for a Lockly device.""" - device_id: str - device_name: str - model: str + device_id: Optional[str] + device_name: Optional[str] + model: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1261,11 +1360,14 @@ class AccelerometerZ(ResourceMapping): :ivar value: Value of latest accelerometer Z-axis reading for a Minut device. """ - time: str - value: float + time: Optional[str] + value: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( time=d.get("time", None), value=d.get("value", None), @@ -1279,11 +1381,14 @@ class Humidity(ResourceMapping): :ivar value: Value of latest humidity reading for a Minut device.""" - time: str - value: float + time: Optional[str] + value: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( time=d.get("time", None), value=d.get("value", None), @@ -1297,11 +1402,14 @@ class Pressure(ResourceMapping): :ivar value: Value of latest pressure reading for a Minut device.""" - time: str - value: float + time: Optional[str] + value: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( time=d.get("time", None), value=d.get("value", None), @@ -1315,11 +1423,14 @@ class Sound(ResourceMapping): :ivar value: Value of latest sound reading for a Minut device.""" - time: str - value: float + time: Optional[str] + value: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( time=d.get("time", None), value=d.get("value", None), @@ -1334,24 +1445,30 @@ class Temperature(ResourceMapping): :ivar value: Value of latest temperature reading for a Minut device. """ - time: str - value: float + time: Optional[str] + value: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( time=d.get("time", None), value=d.get("value", None), ) - accelerometer_z: AccelerometerZ - humidity: Humidity - pressure: Pressure - sound: Sound - temperature: Temperature + accelerometer_z: Optional[AccelerometerZ] + humidity: Optional[Humidity] + pressure: Optional[Pressure] + sound: Optional[Sound] + temperature: Optional[Temperature] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accelerometer_z=( cls.AccelerometerZ.from_dict(d.get("accelerometer_z")) @@ -1380,12 +1497,15 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - device_id: str - device_name: str - latest_sensor_values: LatestSensorValues + device_id: Optional[str] + device_name: Optional[str] + latest_sensor_values: Optional[LatestSensorValues] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1408,13 +1528,16 @@ class NestMetadata(ResourceMapping): :ivar nest_device_id: Device ID for a Google Nest device.""" - device_custom_name: str - device_name: str - display_name: str - nest_device_id: str + device_custom_name: Optional[str] + device_name: Optional[str] + display_name: Optional[str] + nest_device_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_custom_name=d.get("device_custom_name", None), device_name=d.get("device_name", None), @@ -1437,14 +1560,17 @@ class NoiseawareMetadata(ResourceMapping): :ivar noise_level_nrs: Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. """ - device_id: str - device_model: str - device_name: str - noise_level_decibel: float - noise_level_nrs: float + device_id: Optional[str] + device_model: Optional[str] + device_name: Optional[str] + noise_level_decibel: Optional[float] + noise_level_nrs: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_model=d.get("device_model", None), @@ -1468,14 +1594,17 @@ class NukiMetadata(ResourceMapping): :ivar keypad_paired: Indicates whether the keypad is paired for a Nuki device. """ - device_id: str - device_name: str - keypad_2_paired: bool - keypad_battery_critical: bool - keypad_paired: bool + device_id: Optional[str] + device_name: Optional[str] + keypad_2_paired: Optional[bool] + keypad_battery_critical: Optional[bool] + keypad_paired: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1503,16 +1632,19 @@ class OmnitecMetadata(ResourceMapping): :ivar timezone_raw_offset_ms: Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. """ - has_gateway: bool - lock_alias: str - lock_id: float - lock_mac: str - lock_name: str - time_zone: str - timezone_raw_offset_ms: float - + has_gateway: Optional[bool] + lock_alias: Optional[str] + lock_id: Optional[float] + lock_mac: Optional[str] + lock_name: Optional[str] + time_zone: Optional[str] + timezone_raw_offset_ms: Optional[float] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( has_gateway=d.get("has_gateway", None), lock_alias=d.get("lock_alias", None), @@ -1531,11 +1663,14 @@ class RingMetadata(ResourceMapping): :ivar device_name: Device name for a Ring device.""" - device_id: str - device_name: str + device_id: Optional[str] + device_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1564,18 +1699,21 @@ class SaltoKsMetadata(ResourceMapping): :ivar site_name: Site name for the Salto KS site to which the device belongs. """ - battery_level: str - customer_reference: str - has_custom_pin_subscription: bool - lock_id: str - lock_type: str - locked_state: str - model: str - site_id: str - site_name: str - + battery_level: Optional[str] + customer_reference: Optional[str] + has_custom_pin_subscription: Optional[bool] + lock_id: Optional[str] + lock_type: Optional[str] + locked_state: Optional[str] + model: Optional[str] + site_id: Optional[str] + site_name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( battery_level=d.get("battery_level", None), customer_reference=d.get("customer_reference", None), @@ -1611,17 +1749,20 @@ class SaltoMetadata(ResourceMapping): :ivar site_name: Site name for the Salto KS site to which the device belongs. """ - battery_level: str - customer_reference: str - lock_id: str - lock_type: str - locked_state: str - model: str - site_id: str - site_name: str - + battery_level: Optional[str] + customer_reference: Optional[str] + lock_id: Optional[str] + lock_type: Optional[str] + locked_state: Optional[str] + model: Optional[str] + site_id: Optional[str] + site_name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( battery_level=d.get("battery_level", None), customer_reference=d.get("customer_reference", None), @@ -1643,12 +1784,15 @@ class SchlageMetadata(ResourceMapping): :ivar model: Model for a Schlage device.""" - device_id: str - device_name: str - model: str + device_id: Optional[str] + device_name: Optional[str] + model: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1665,12 +1809,15 @@ class SeamBridgeMetadata(ResourceMapping): :ivar unlock_method: Unlock method for Seam Bridge.""" - device_num: float - name: str - unlock_method: str + device_num: Optional[float] + name: Optional[str] + unlock_method: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_num=d.get("device_num", None), name=d.get("name", None), @@ -1689,13 +1836,16 @@ class SensiMetadata(ResourceMapping): :ivar product_type: Product type for a Sensi device.""" - device_id: str - device_name: str - dual_setpoints_not_supported: bool - product_type: str + device_id: Optional[str] + device_name: Optional[str] + dual_setpoints_not_supported: Optional[bool] + product_type: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1717,13 +1867,16 @@ class SmartthingsMetadata(ResourceMapping): :ivar model: Model for a SmartThings device.""" - device_id: str - device_name: str - location_id: str - model: str + device_id: Optional[str] + device_name: Optional[str] + location_id: Optional[str] + model: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1739,11 +1892,14 @@ class TadoMetadata(ResourceMapping): :ivar serial_no: Serial number for a tado° device.""" - device_type: str - serial_no: str + device_type: Optional[str] + serial_no: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_type=d.get("device_type", None), serial_no=d.get("serial_no", None), @@ -1767,16 +1923,19 @@ class TedeeMetadata(ResourceMapping): :ivar serial_number: Serial number for a Tedee device.""" - bridge_id: float - bridge_name: str - device_id: float - device_model: str - device_name: str - keypad_id: float - serial_number: str - + bridge_id: Optional[float] + bridge_name: Optional[str] + device_id: Optional[float] + device_model: Optional[str] + device_name: Optional[str] + keypad_id: Optional[float] + serial_number: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( bridge_id=d.get("bridge_id", None), bridge_name=d.get("bridge_name", None), @@ -1823,16 +1982,19 @@ class Features(ResourceMapping): :ivar wifi: Indicates whether a TTLock device supports Wi-Fi.""" - auto_lock_time_config: bool - incomplete_keyboard_passcode: bool - lock_command: bool - passcode: bool - passcode_management: bool - unlock_via_gateway: bool - wifi: bool - + auto_lock_time_config: Optional[bool] + incomplete_keyboard_passcode: Optional[bool] + lock_command: Optional[bool] + passcode: Optional[bool] + passcode_management: Optional[bool] + unlock_via_gateway: Optional[bool] + wifi: Optional[bool] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( auto_lock_time_config=d.get("auto_lock_time_config", None), incomplete_keyboard_passcode=d.get( @@ -1854,26 +2016,32 @@ class WirelessKeypads(ResourceMapping): :ivar wireless_keypad_name: Name for a wireless keypad for a TTLock device. """ - wireless_keypad_id: float - wireless_keypad_name: str + wireless_keypad_id: Optional[float] + wireless_keypad_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( wireless_keypad_id=d.get("wireless_keypad_id", None), wireless_keypad_name=d.get("wireless_keypad_name", None), ) - feature_value: str - features: Features - has_gateway: bool - lock_alias: str - lock_id: float - timezone_raw_offset_ms: float - wireless_keypads: List[WirelessKeypads] - + feature_value: Optional[str] + features: Optional[Features] + has_gateway: Optional[bool] + lock_alias: Optional[str] + lock_id: Optional[float] + timezone_raw_offset_ms: Optional[float] + wireless_keypads: Optional[List[WirelessKeypads]] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( feature_value=d.get("feature_value", None), features=( @@ -1899,11 +2067,14 @@ class TwoNMetadata(ResourceMapping): :ivar device_name: Device name for a 2N device.""" - device_id: float - device_name: str + device_id: Optional[float] + device_name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1921,13 +2092,16 @@ class UltraloqMetadata(ResourceMapping): :ivar time_zone: IANA timezone for the Ultraloq device.""" - device_id: str - device_name: str - device_type: str - time_zone: str + device_id: Optional[str] + device_name: Optional[str] + device_type: Optional[str] + time_zone: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_name=d.get("device_name", None), @@ -1941,10 +2115,13 @@ class VisionlineMetadata(ResourceMapping): :ivar encoder_id: Encoder ID for an ASSA ABLOY Visionline system.""" - encoder_id: str + encoder_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( encoder_id=d.get("encoder_id", None), ) @@ -1969,17 +2146,20 @@ class WyzeMetadata(ResourceMapping): :ivar product_type: Product type for a Wyze device.""" - device_id: str - device_info_model: str - device_name: str - keypad_uuid: str - locker_status_hardlock: float - product_model: str - product_name: str - product_type: str - + device_id: Optional[str] + device_info_model: Optional[str] + device_name: Optional[str] + keypad_uuid: Optional[str] + locker_status_hardlock: Optional[float] + product_model: Optional[str] + product_name: Optional[str] + product_type: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), device_info_model=d.get("device_info_model", None), @@ -2002,11 +2182,14 @@ class CodeConstraints(ResourceMapping): :ivar min_length: Minimum name length constraint for access codes.""" constraint_type: str - max_length: float - min_length: float + max_length: Optional[float] + min_length: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( constraint_type=d.get("constraint_type", None), max_length=d.get("max_length", None), @@ -2021,8 +2204,11 @@ class KeypadBattery(ResourceMapping): level: float + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( level=d.get("level", None), ) @@ -2063,8 +2249,11 @@ class TimePairs(ResourceMapping): end_time: str start_time: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( display_name=d.get("display_name", None), end_time=d.get("end_time", None), @@ -2072,16 +2261,19 @@ def from_dict(cls, d: Dict[str, Any]): ) display_name: str - end_date_recurrence_rule: str - matching_start_end_time: bool - max_duration: str - min_duration: str - start_date_recurrence_rule: str - time_pairs: List[TimePairs] - time_zone: str - + end_date_recurrence_rule: Optional[str] + matching_start_end_time: Optional[bool] + max_duration: Optional[str] + min_duration: Optional[str] + start_date_recurrence_rule: Optional[str] + time_pairs: Optional[List[TimePairs]] + time_zone: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( display_name=d.get("display_name", None), end_date_recurrence_rule=d.get("end_date_recurrence_rule", None), @@ -2133,8 +2325,11 @@ class TimePairs(ResourceMapping): end_time: str start_time: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( display_name=d.get("display_name", None), end_time=d.get("end_time", None), @@ -2142,16 +2337,19 @@ def from_dict(cls, d: Dict[str, Any]): ) display_name: str - end_date_recurrence_rule: str - matching_start_end_time: bool - max_duration: str - min_duration: str - start_date_recurrence_rule: str - time_pairs: List[TimePairs] - time_zone: str - + end_date_recurrence_rule: Optional[str] + matching_start_end_time: Optional[bool] + max_duration: Optional[str] + min_duration: Optional[str] + start_date_recurrence_rule: Optional[str] + time_pairs: Optional[List[TimePairs]] + time_zone: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( display_name=d.get("display_name", None), end_date_recurrence_rule=d.get("end_date_recurrence_rule", None), @@ -2209,8 +2407,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -2222,15 +2423,18 @@ def from_dict(cls, d: Dict[str, Any]): device_id: str ends_at: str errors: List[Errors] - is_override_allowed: bool - max_override_period_minutes: int - name: str + is_override_allowed: Optional[bool] + max_override_period_minutes: Optional[int] + name: Optional[str] starts_at: str thermostat_schedule_id: str workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( climate_preset_key=d.get("climate_preset_key", None), created_at=d.get("created_at", None), @@ -2293,12 +2497,15 @@ class EcobeeMetadata(ResourceMapping): :ivar owner: Indicates whether the climate preset is owned by the user or the system. """ - climate_ref: str - is_optimized: bool - owner: str + climate_ref: Optional[str] + is_optimized: Optional[bool] + owner: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( climate_ref=d.get("climate_ref", None), is_optimized=d.get("is_optimized", None), @@ -2309,20 +2516,23 @@ def from_dict(cls, d: Dict[str, Any]): can_edit: bool can_use_with_thermostat_daily_programs: bool climate_preset_key: str - climate_preset_mode: str - cooling_set_point_celsius: float - cooling_set_point_fahrenheit: float + climate_preset_mode: Optional[str] + cooling_set_point_celsius: Optional[float] + cooling_set_point_fahrenheit: Optional[float] display_name: str - ecobee_metadata: EcobeeMetadata - fan_mode_setting: str - heating_set_point_celsius: float - heating_set_point_fahrenheit: float - hvac_mode_setting: str + ecobee_metadata: Optional[EcobeeMetadata] + fan_mode_setting: Optional[str] + heating_set_point_celsius: Optional[float] + heating_set_point_fahrenheit: Optional[float] + hvac_mode_setting: Optional[str] manual_override_allowed: bool - name: str + name: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( can_delete=d.get("can_delete", None), can_edit=d.get("can_edit", None), @@ -2397,36 +2607,42 @@ class EcobeeMetadata(ResourceMapping): :ivar owner: Indicates whether the climate preset is owned by the user or the system. """ - climate_ref: str - is_optimized: bool - owner: str + climate_ref: Optional[str] + is_optimized: Optional[bool] + owner: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( climate_ref=d.get("climate_ref", None), is_optimized=d.get("is_optimized", None), owner=d.get("owner", None), ) - can_delete: bool - can_edit: bool - can_use_with_thermostat_daily_programs: bool - climate_preset_key: str - climate_preset_mode: str - cooling_set_point_celsius: float - cooling_set_point_fahrenheit: float - display_name: str - ecobee_metadata: EcobeeMetadata - fan_mode_setting: str - heating_set_point_celsius: float - heating_set_point_fahrenheit: float - hvac_mode_setting: str - manual_override_allowed: bool - name: str - + can_delete: Optional[bool] + can_edit: Optional[bool] + can_use_with_thermostat_daily_programs: Optional[bool] + climate_preset_key: Optional[str] + climate_preset_mode: Optional[str] + cooling_set_point_celsius: Optional[float] + cooling_set_point_fahrenheit: Optional[float] + display_name: Optional[str] + ecobee_metadata: Optional[EcobeeMetadata] + fan_mode_setting: Optional[str] + heating_set_point_celsius: Optional[float] + heating_set_point_fahrenheit: Optional[float] + hvac_mode_setting: Optional[str] + manual_override_allowed: Optional[bool] + name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( can_delete=d.get("can_delete", None), can_edit=d.get("can_edit", None), @@ -2501,36 +2717,42 @@ class EcobeeMetadata(ResourceMapping): :ivar owner: Indicates whether the climate preset is owned by the user or the system. """ - climate_ref: str - is_optimized: bool - owner: str + climate_ref: Optional[str] + is_optimized: Optional[bool] + owner: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( climate_ref=d.get("climate_ref", None), is_optimized=d.get("is_optimized", None), owner=d.get("owner", None), ) - can_delete: bool - can_edit: bool - can_use_with_thermostat_daily_programs: bool - climate_preset_key: str - climate_preset_mode: str - cooling_set_point_celsius: float - cooling_set_point_fahrenheit: float - display_name: str - ecobee_metadata: EcobeeMetadata - fan_mode_setting: str - heating_set_point_celsius: float - heating_set_point_fahrenheit: float - hvac_mode_setting: str - manual_override_allowed: bool - name: str - + can_delete: Optional[bool] + can_edit: Optional[bool] + can_use_with_thermostat_daily_programs: Optional[bool] + climate_preset_key: Optional[str] + climate_preset_mode: Optional[str] + cooling_set_point_celsius: Optional[float] + cooling_set_point_fahrenheit: Optional[float] + display_name: Optional[str] + ecobee_metadata: Optional[EcobeeMetadata] + fan_mode_setting: Optional[str] + heating_set_point_celsius: Optional[float] + heating_set_point_fahrenheit: Optional[float] + hvac_mode_setting: Optional[str] + manual_override_allowed: Optional[bool] + name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( can_delete=d.get("can_delete", None), can_edit=d.get("can_edit", None), @@ -2572,13 +2794,16 @@ class TemperatureThreshold(ResourceMapping): :ivar upper_limit_fahrenheit: Upper limit in °F within the current `temperature threshold `_ set for the thermostat. """ - lower_limit_celsius: float - lower_limit_fahrenheit: float - upper_limit_celsius: float - upper_limit_fahrenheit: float + lower_limit_celsius: Optional[float] + lower_limit_fahrenheit: Optional[float] + upper_limit_celsius: Optional[float] + upper_limit_fahrenheit: Optional[float] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( lower_limit_celsius=d.get("lower_limit_celsius", None), lower_limit_fahrenheit=d.get("lower_limit_fahrenheit", None), @@ -2615,8 +2840,11 @@ class Periods(ResourceMapping): climate_preset_key: str starts_at_time: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( climate_preset_key=d.get("climate_preset_key", None), starts_at_time=d.get("starts_at_time", None), @@ -2624,13 +2852,16 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str device_id: str - name: str + name: Optional[str] periods: List[Periods] thermostat_daily_program_id: str workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), device_id=d.get("device_id", None), @@ -2664,16 +2895,19 @@ class ThermostatWeeklyProgram(ResourceMapping): """ created_at: str - friday_program_id: str - monday_program_id: str - saturday_program_id: str - sunday_program_id: str - thursday_program_id: str - tuesday_program_id: str - wednesday_program_id: str - + friday_program_id: Optional[str] + monday_program_id: Optional[str] + saturday_program_id: Optional[str] + sunday_program_id: Optional[str] + thursday_program_id: Optional[str] + tuesday_program_id: Optional[str] + wednesday_program_id: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), friday_program_id=d.get("friday_program_id", None), @@ -2685,113 +2919,120 @@ def from_dict(cls, d: Dict[str, Any]): wednesday_program_id=d.get("wednesday_program_id", None), ) - accessory_keypad: AccessoryKeypad - appearance: Appearance - battery: Battery - battery_level: float - currently_triggering_noise_threshold_ids: List[str] - has_direct_power: bool - image_alt_text: str - image_url: str - manufacturer: str - model: Model + accessory_keypad: Optional[AccessoryKeypad] + appearance: Optional[Appearance] + battery: Optional[Battery] + battery_level: Optional[float] + currently_triggering_noise_threshold_ids: Optional[List[str]] + has_direct_power: Optional[bool] + image_alt_text: Optional[str] + image_url: Optional[str] + manufacturer: Optional[str] + model: Optional[Model] name: str - noise_level_decibels: float - offline_access_codes_enabled: bool + noise_level_decibels: Optional[float] + offline_access_codes_enabled: Optional[bool] online: bool - online_access_codes_enabled: bool - serial_number: str - supports_accessory_keypad: bool - supports_offline_access_codes: bool - assa_abloy_credential_service_metadata: AssaAbloyCredentialServiceMetadata - salto_space_credential_service_metadata: SaltoSpaceCredentialServiceMetadata - akiles_metadata: AkilesMetadata - aqara_metadata: AqaraMetadata - assa_abloy_vostio_metadata: AssaAbloyVostioMetadata - august_metadata: AugustMetadata - avigilon_alta_metadata: AvigilonAltaMetadata - brivo_metadata: BrivoMetadata - controlbyweb_metadata: ControlbywebMetadata - dormakaba_oracode_metadata: DormakabaOracodeMetadata - ecobee_metadata: EcobeeMetadata - four_suites_metadata: FourSuitesMetadata - genie_metadata: GenieMetadata - honeywell_resideo_metadata: HoneywellResideoMetadata - igloo_metadata: IglooMetadata - igloohome_metadata: IgloohomeMetadata - keynest_metadata: KeynestMetadata - kisi_metadata: KisiMetadata - korelock_metadata: KorelockMetadata - kwikset_metadata: KwiksetMetadata - lockly_metadata: LocklyMetadata - minut_metadata: MinutMetadata - nest_metadata: NestMetadata - noiseaware_metadata: NoiseawareMetadata - nuki_metadata: NukiMetadata - omnitec_metadata: OmnitecMetadata - ring_metadata: RingMetadata - salto_ks_metadata: SaltoKsMetadata - salto_metadata: SaltoMetadata - schlage_metadata: SchlageMetadata - seam_bridge_metadata: SeamBridgeMetadata - sensi_metadata: SensiMetadata - smartthings_metadata: SmartthingsMetadata - tado_metadata: TadoMetadata - tedee_metadata: TedeeMetadata - ttlock_metadata: TtlockMetadata - two_n_metadata: TwoNMetadata - ultraloq_metadata: UltraloqMetadata - visionline_metadata: VisionlineMetadata - wyze_metadata: WyzeMetadata - auto_lock_delay_seconds: float - auto_lock_enabled: bool - backup_access_code_pool_enabled: bool - code_constraints: List[CodeConstraints] - door_open: bool - has_native_entry_events: bool - keypad_battery: KeypadBattery - locked: bool - max_active_codes_supported: float - offline_time_frame_options: List[OfflineTimeFrameOptions] - online_time_frame_options: List[OnlineTimeFrameOptions] - supported_code_lengths: List[float] - supports_backup_access_code_pool: bool - active_thermostat_schedule: ActiveThermostatSchedule - active_thermostat_schedule_id: str - available_climate_preset_modes: List[str] - available_climate_presets: List[AvailableClimatePresets] - available_fan_mode_settings: List[str] - available_hvac_mode_settings: List[str] - current_climate_setting: CurrentClimateSetting - default_climate_setting: DefaultClimateSetting - fallback_climate_preset_key: str - fan_mode_setting: str - is_cooling: bool - is_fan_running: bool - is_heating: bool - is_temporary_manual_override_active: bool - max_cooling_set_point_celsius: float - max_cooling_set_point_fahrenheit: float - max_heating_set_point_celsius: float - max_heating_set_point_fahrenheit: float - max_thermostat_daily_program_periods_per_day: float - max_unique_climate_presets_per_thermostat_weekly_program: float - min_cooling_set_point_celsius: float - min_cooling_set_point_fahrenheit: float - min_heating_cooling_delta_celsius: float - min_heating_cooling_delta_fahrenheit: float - min_heating_set_point_celsius: float - min_heating_set_point_fahrenheit: float - relative_humidity: float - temperature_celsius: float - temperature_fahrenheit: float - temperature_threshold: TemperatureThreshold - thermostat_daily_program_period_precision_minutes: float - thermostat_daily_programs: List[ThermostatDailyPrograms] - thermostat_weekly_program: ThermostatWeeklyProgram - + online_access_codes_enabled: Optional[bool] + serial_number: Optional[str] + supports_accessory_keypad: Optional[bool] + supports_offline_access_codes: Optional[bool] + assa_abloy_credential_service_metadata: Optional[ + AssaAbloyCredentialServiceMetadata + ] + salto_space_credential_service_metadata: Optional[ + SaltoSpaceCredentialServiceMetadata + ] + akiles_metadata: Optional[AkilesMetadata] + aqara_metadata: Optional[AqaraMetadata] + assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] + august_metadata: Optional[AugustMetadata] + avigilon_alta_metadata: Optional[AvigilonAltaMetadata] + brivo_metadata: Optional[BrivoMetadata] + controlbyweb_metadata: Optional[ControlbywebMetadata] + dormakaba_oracode_metadata: Optional[DormakabaOracodeMetadata] + ecobee_metadata: Optional[EcobeeMetadata] + four_suites_metadata: Optional[FourSuitesMetadata] + genie_metadata: Optional[GenieMetadata] + honeywell_resideo_metadata: Optional[HoneywellResideoMetadata] + igloo_metadata: Optional[IglooMetadata] + igloohome_metadata: Optional[IgloohomeMetadata] + keynest_metadata: Optional[KeynestMetadata] + kisi_metadata: Optional[KisiMetadata] + korelock_metadata: Optional[KorelockMetadata] + kwikset_metadata: Optional[KwiksetMetadata] + lockly_metadata: Optional[LocklyMetadata] + minut_metadata: Optional[MinutMetadata] + nest_metadata: Optional[NestMetadata] + noiseaware_metadata: Optional[NoiseawareMetadata] + nuki_metadata: Optional[NukiMetadata] + omnitec_metadata: Optional[OmnitecMetadata] + ring_metadata: Optional[RingMetadata] + salto_ks_metadata: Optional[SaltoKsMetadata] + salto_metadata: Optional[SaltoMetadata] + schlage_metadata: Optional[SchlageMetadata] + seam_bridge_metadata: Optional[SeamBridgeMetadata] + sensi_metadata: Optional[SensiMetadata] + smartthings_metadata: Optional[SmartthingsMetadata] + tado_metadata: Optional[TadoMetadata] + tedee_metadata: Optional[TedeeMetadata] + ttlock_metadata: Optional[TtlockMetadata] + two_n_metadata: Optional[TwoNMetadata] + ultraloq_metadata: Optional[UltraloqMetadata] + visionline_metadata: Optional[VisionlineMetadata] + wyze_metadata: Optional[WyzeMetadata] + auto_lock_delay_seconds: Optional[float] + auto_lock_enabled: Optional[bool] + backup_access_code_pool_enabled: Optional[bool] + code_constraints: Optional[List[CodeConstraints]] + door_open: Optional[bool] + has_native_entry_events: Optional[bool] + keypad_battery: Optional[KeypadBattery] + locked: Optional[bool] + max_active_codes_supported: Optional[float] + offline_time_frame_options: Optional[List[OfflineTimeFrameOptions]] + online_time_frame_options: Optional[List[OnlineTimeFrameOptions]] + supported_code_lengths: Optional[List[float]] + supports_backup_access_code_pool: Optional[bool] + active_thermostat_schedule: Optional[ActiveThermostatSchedule] + active_thermostat_schedule_id: Optional[str] + available_climate_preset_modes: Optional[List[str]] + available_climate_presets: Optional[List[AvailableClimatePresets]] + available_fan_mode_settings: Optional[List[str]] + available_hvac_mode_settings: Optional[List[str]] + current_climate_setting: Optional[CurrentClimateSetting] + default_climate_setting: Optional[DefaultClimateSetting] + fallback_climate_preset_key: Optional[str] + fan_mode_setting: Optional[str] + is_cooling: Optional[bool] + is_fan_running: Optional[bool] + is_heating: Optional[bool] + is_temporary_manual_override_active: Optional[bool] + max_cooling_set_point_celsius: Optional[float] + max_cooling_set_point_fahrenheit: Optional[float] + max_heating_set_point_celsius: Optional[float] + max_heating_set_point_fahrenheit: Optional[float] + max_thermostat_daily_program_periods_per_day: Optional[float] + max_unique_climate_presets_per_thermostat_weekly_program: Optional[float] + min_cooling_set_point_celsius: Optional[float] + min_cooling_set_point_fahrenheit: Optional[float] + min_heating_cooling_delta_celsius: Optional[float] + min_heating_cooling_delta_fahrenheit: Optional[float] + min_heating_set_point_celsius: Optional[float] + min_heating_set_point_fahrenheit: Optional[float] + relative_humidity: Optional[float] + temperature_celsius: Optional[float] + temperature_fahrenheit: Optional[float] + temperature_threshold: Optional[TemperatureThreshold] + thermostat_daily_program_period_precision_minutes: Optional[float] + thermostat_daily_programs: Optional[List[ThermostatDailyPrograms]] + thermostat_weekly_program: Optional[ThermostatWeeklyProgram] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accessory_keypad=( cls.AccessoryKeypad.from_dict(d.get("accessory_keypad")) @@ -3194,11 +3435,14 @@ class Warnings(ResourceMapping): created_at: str message: str warning_code: str - active_access_code_count: int - max_active_access_code_count: int + active_access_code_count: Optional[int] + max_active_access_code_count: Optional[int] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -3209,46 +3453,49 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - can_configure_auto_lock: bool - can_hvac_cool: bool - can_hvac_heat: bool - can_hvac_heat_cool: bool - can_program_offline_access_codes: bool - can_program_online_access_codes: bool - can_program_thermostat_programs_as_different_each_day: bool - can_program_thermostat_programs_as_same_each_day: bool - can_program_thermostat_programs_as_weekday_weekend: bool - can_remotely_lock: bool - can_remotely_unlock: bool - can_run_thermostat_programs: bool - can_simulate_connection: bool - can_simulate_disconnection: bool - can_simulate_hub_connection: bool - can_simulate_hub_disconnection: bool - can_simulate_paid_subscription: bool - can_simulate_removal: bool - can_turn_off_hvac: bool - can_unlock_with_code: bool + can_configure_auto_lock: Optional[bool] + can_hvac_cool: Optional[bool] + can_hvac_heat: Optional[bool] + can_hvac_heat_cool: Optional[bool] + can_program_offline_access_codes: Optional[bool] + can_program_online_access_codes: Optional[bool] + can_program_thermostat_programs_as_different_each_day: Optional[bool] + can_program_thermostat_programs_as_same_each_day: Optional[bool] + can_program_thermostat_programs_as_weekday_weekend: Optional[bool] + can_remotely_lock: Optional[bool] + can_remotely_unlock: Optional[bool] + can_run_thermostat_programs: Optional[bool] + can_simulate_connection: Optional[bool] + can_simulate_disconnection: Optional[bool] + can_simulate_hub_connection: Optional[bool] + can_simulate_hub_disconnection: Optional[bool] + can_simulate_paid_subscription: Optional[bool] + can_simulate_removal: Optional[bool] + can_turn_off_hvac: Optional[bool] + can_unlock_with_code: Optional[bool] capabilities_supported: List[str] connected_account_id: str created_at: str custom_metadata: Dict[str, Any] device_id: str - device_manufacturer: DeviceManufacturer - device_provider: DeviceProvider + device_manufacturer: Optional[DeviceManufacturer] + device_provider: Optional[DeviceProvider] device_type: str display_name: str errors: List[Errors] is_managed: bool - location: Location - nickname: str - properties: Properties + location: Optional[Location] + nickname: Optional[str] + properties: Optional[Properties] space_ids: List[str] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( can_configure_auto_lock=d.get("can_configure_auto_lock", None), can_hvac_cool=d.get("can_hvac_cool", None), diff --git a/seam/resources/device_provider.py b/seam/resources/device_provider.py index 2b498134..6bd6a2e7 100644 --- a/seam/resources/device_provider.py +++ b/seam/resources/device_provider.py @@ -57,33 +57,36 @@ class DeviceProvider: :ivar provider_categories: List of provider categories to which the device provider belongs, such as ``stable``, ``consumer_smartlocks``, ``thermostats``, and so on. """ - can_configure_auto_lock: bool - can_hvac_cool: bool - can_hvac_heat: bool - can_hvac_heat_cool: bool - can_program_offline_access_codes: bool - can_program_online_access_codes: bool - can_program_thermostat_programs_as_different_each_day: bool - can_program_thermostat_programs_as_same_each_day: bool - can_program_thermostat_programs_as_weekday_weekend: bool - can_remotely_lock: bool - can_remotely_unlock: bool - can_run_thermostat_programs: bool - can_simulate_connection: bool - can_simulate_disconnection: bool - can_simulate_hub_connection: bool - can_simulate_hub_disconnection: bool - can_simulate_paid_subscription: bool - can_simulate_removal: bool - can_turn_off_hvac: bool - can_unlock_with_code: bool + can_configure_auto_lock: Optional[bool] + can_hvac_cool: Optional[bool] + can_hvac_heat: Optional[bool] + can_hvac_heat_cool: Optional[bool] + can_program_offline_access_codes: Optional[bool] + can_program_online_access_codes: Optional[bool] + can_program_thermostat_programs_as_different_each_day: Optional[bool] + can_program_thermostat_programs_as_same_each_day: Optional[bool] + can_program_thermostat_programs_as_weekday_weekend: Optional[bool] + can_remotely_lock: Optional[bool] + can_remotely_unlock: Optional[bool] + can_run_thermostat_programs: Optional[bool] + can_simulate_connection: Optional[bool] + can_simulate_disconnection: Optional[bool] + can_simulate_hub_connection: Optional[bool] + can_simulate_hub_disconnection: Optional[bool] + can_simulate_paid_subscription: Optional[bool] + can_simulate_removal: Optional[bool] + can_turn_off_hvac: Optional[bool] + can_unlock_with_code: Optional[bool] device_provider_name: str display_name: str image_url: str provider_categories: List[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( can_configure_auto_lock=d.get("can_configure_auto_lock", None), can_hvac_cool=d.get("can_hvac_cool", None), diff --git a/seam/resources/instant_key.py b/seam/resources/instant_key.py index 39a98211..b7c32d1c 100644 --- a/seam/resources/instant_key.py +++ b/seam/resources/instant_key.py @@ -38,12 +38,15 @@ class Customization(ResourceMapping): :ivar secondary_color: Secondary color used in the Instant Key UI.""" - logo_url: str - primary_color: str - secondary_color: str + logo_url: Optional[str] + primary_color: Optional[str] + secondary_color: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( logo_url=d.get("logo_url", None), primary_color=d.get("primary_color", None), @@ -52,16 +55,19 @@ def from_dict(cls, d: Dict[str, Any]): client_session_id: str created_at: str - customization: Customization - customization_profile_id: str + customization: Optional[Customization] + customization_profile_id: Optional[str] expires_at: str instant_key_id: str instant_key_url: str user_identity_id: str workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( client_session_id=d.get("client_session_id", None), created_at=d.get("created_at", None), diff --git a/seam/resources/noise_threshold.py b/seam/resources/noise_threshold.py index e0eee750..d8179713 100644 --- a/seam/resources/noise_threshold.py +++ b/seam/resources/noise_threshold.py @@ -28,11 +28,14 @@ class NoiseThreshold: name: str noise_threshold_decibels: float noise_threshold_id: str - noise_threshold_nrs: float + noise_threshold_nrs: Optional[float] starts_daily_at: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), ends_daily_at=d.get("ends_daily_at", None), diff --git a/seam/resources/pagination.py b/seam/resources/pagination.py index a2180092..53949210 100644 --- a/seam/resources/pagination.py +++ b/seam/resources/pagination.py @@ -15,11 +15,14 @@ class Pagination: :ivar next_page_url: URL to get the next page of results.""" has_next_page: bool - next_page_cursor: str - next_page_url: str + next_page_cursor: Optional[str] + next_page_url: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( has_next_page=d.get("has_next_page", None), next_page_cursor=d.get("next_page_cursor", None), diff --git a/seam/resources/phone.py b/seam/resources/phone.py index c7ed93c2..2c69f137 100644 --- a/seam/resources/phone.py +++ b/seam/resources/phone.py @@ -42,8 +42,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -76,21 +79,27 @@ class Endpoints(ResourceMapping): :ivar is_active: Indicated whether the endpoint is active.""" - endpoint_id: str - is_active: bool + endpoint_id: Optional[str] + is_active: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( endpoint_id=d.get("endpoint_id", None), is_active=d.get("is_active", None), ) - endpoints: List[Endpoints] - has_active_endpoint: bool + endpoints: Optional[List[Endpoints]] + has_active_endpoint: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( endpoints=[ cls.Endpoints.from_dict(i) for i in d.get("endpoints") or [] @@ -105,19 +114,29 @@ class SaltoSpaceCredentialServiceMetadata(ResourceMapping): :ivar has_active_phone: Indicates whether the credential service has an active associated phone. """ - has_active_phone: bool + has_active_phone: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( has_active_phone=d.get("has_active_phone", None), ) - assa_abloy_credential_service_metadata: AssaAbloyCredentialServiceMetadata - salto_space_credential_service_metadata: SaltoSpaceCredentialServiceMetadata + assa_abloy_credential_service_metadata: Optional[ + AssaAbloyCredentialServiceMetadata + ] + salto_space_credential_service_metadata: Optional[ + SaltoSpaceCredentialServiceMetadata + ] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( assa_abloy_credential_service_metadata=( cls.AssaAbloyCredentialServiceMetadata.from_dict( @@ -149,8 +168,11 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -163,13 +185,16 @@ def from_dict(cls, d: Dict[str, Any]): device_type: str display_name: str errors: List[Errors] - nickname: str - properties: Properties + nickname: Optional[str] + properties: Optional[Properties] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), custom_metadata=DeepAttrDict(d.get("custom_metadata", None)), diff --git a/seam/resources/seam_event.py b/seam/resources/seam_event.py index 28a57f65..68fe4395 100644 --- a/seam/resources/seam_event.py +++ b/seam/resources/seam_event.py @@ -200,12 +200,15 @@ class ChangedProperties(ResourceMapping): :ivar to: New value of the property, or null if cleared.""" - from_: str + from_: Optional[str] property: str - to: str + to: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( from_=d.get("from", None), property=d.get("property", None), @@ -224,13 +227,16 @@ class From(ResourceMapping): :ivar starts_at: Previous start time.""" - name: str - code: str - ends_at: str - starts_at: str + name: Optional[str] + code: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( name=d.get("name", None), code=d.get("code", None), @@ -250,13 +256,16 @@ class To(ResourceMapping): :ivar starts_at: New start time.""" - name: str - code: str - ends_at: str - starts_at: str + name: Optional[str] + code: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( name=d.get("name", None), code=d.get("code", None), @@ -275,12 +284,15 @@ class RequestedMutations(ResourceMapping): :ivar to: New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like ``deleting``. """ - from_: Dict[str, Any] + from_: Optional[Dict[str, Any]] mutation_code: str - to: Dict[str, Any] + to: Optional[Dict[str, Any]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( from_=DeepAttrDict(d.get("from", None)), mutation_code=d.get("mutation_code", None), @@ -302,8 +314,11 @@ class AccessCodeErrors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -325,8 +340,11 @@ class AccessCodeWarnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -348,8 +366,11 @@ class ConnectedAccountErrors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -371,8 +392,11 @@ class ConnectedAccountWarnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -394,8 +418,11 @@ class DeviceErrors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -417,8 +444,11 @@ class DeviceWarnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -440,8 +470,11 @@ class AcsSystemErrors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -463,8 +496,11 @@ class AcsSystemWarnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -483,107 +519,113 @@ class Reason(ResourceMapping): message: str reason_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( message=d.get("message", None), reason_code=d.get("reason_code", None), ) - access_code_id: str - connected_account_custom_metadata: Dict[str, Any] - connected_account_id: str + access_code_id: Optional[str] + connected_account_custom_metadata: Optional[Dict[str, Any]] + connected_account_id: Optional[str] created_at: str - device_custom_metadata: Dict[str, Any] - device_id: str - event_description: str + device_custom_metadata: Optional[Dict[str, Any]] + device_id: Optional[str] + event_description: Optional[str] event_id: str event_type: str occurred_at: str workspace_id: str - change_reason: str - changed_properties: List[ChangedProperties] - description: str - from_: From - to: To - requested_mutations: List[RequestedMutations] - code: str - access_code_errors: List[AccessCodeErrors] - access_code_warnings: List[AccessCodeWarnings] - connected_account_errors: List[ConnectedAccountErrors] - connected_account_warnings: List[ConnectedAccountWarnings] - device_errors: List[DeviceErrors] - device_warnings: List[DeviceWarnings] - backup_access_code_id: str - access_grant_id: str - acs_entrance_id: str - access_grant_key: str - ends_at: str - starts_at: str - error_message: str - missing_device_ids: List[str] - access_grant_ids: List[str] - access_grant_keys: List[str] - access_method_id: str - is_backup_code: bool - acs_system_id: str - acs_system_errors: List[AcsSystemErrors] - acs_system_warnings: List[AcsSystemWarnings] - acs_credential_id: str - acs_user_id: str - acs_encoder_id: str - acs_access_group_id: str - client_session_id: str - connect_webview_id: str - customer_key: str - connected_account_type: str - action_attempt_id: str - action_type: str - status: str - error_code: str - battery_level: float - battery_status: str - device_name: str - minut_metadata: Dict[str, Any] - noise_level_decibels: float - noise_level_nrs: float - noise_threshold_id: str - noise_threshold_name: str - noiseaware_metadata: Dict[str, Any] - access_code_is_managed: bool - is_via_bluetooth: bool - is_via_nfc: bool - method: str - user_identity_id: str - reason: Reason - climate_preset_key: str - is_fallback_climate_preset: bool - thermostat_schedule_id: str - cooling_set_point_celsius: float - cooling_set_point_fahrenheit: float - fan_mode_setting: str - heating_set_point_celsius: float - heating_set_point_fahrenheit: float - hvac_mode_setting: str - lower_limit_celsius: float - lower_limit_fahrenheit: float - temperature_celsius: float - temperature_fahrenheit: float - upper_limit_celsius: float - upper_limit_fahrenheit: float - desired_temperature_celsius: float - desired_temperature_fahrenheit: float - activation_reason: str - image_url: str - motion_sub_type: str - video_url: str - acs_entrance_ids: List[str] - device_ids: List[str] - space_id: str - space_key: str - + change_reason: Optional[str] + changed_properties: Optional[List[ChangedProperties]] + description: Optional[str] + from_: Optional[From] + to: Optional[To] + requested_mutations: Optional[List[RequestedMutations]] + code: Optional[str] + access_code_errors: Optional[List[AccessCodeErrors]] + access_code_warnings: Optional[List[AccessCodeWarnings]] + connected_account_errors: Optional[List[ConnectedAccountErrors]] + connected_account_warnings: Optional[List[ConnectedAccountWarnings]] + device_errors: Optional[List[DeviceErrors]] + device_warnings: Optional[List[DeviceWarnings]] + backup_access_code_id: Optional[str] + access_grant_id: Optional[str] + acs_entrance_id: Optional[str] + access_grant_key: Optional[str] + ends_at: Optional[str] + starts_at: Optional[str] + error_message: Optional[str] + missing_device_ids: Optional[List[str]] + access_grant_ids: Optional[List[str]] + access_grant_keys: Optional[List[str]] + access_method_id: Optional[str] + is_backup_code: Optional[bool] + acs_system_id: Optional[str] + acs_system_errors: Optional[List[AcsSystemErrors]] + acs_system_warnings: Optional[List[AcsSystemWarnings]] + acs_credential_id: Optional[str] + acs_user_id: Optional[str] + acs_encoder_id: Optional[str] + acs_access_group_id: Optional[str] + client_session_id: Optional[str] + connect_webview_id: Optional[str] + customer_key: Optional[str] + connected_account_type: Optional[str] + action_attempt_id: Optional[str] + action_type: Optional[str] + status: Optional[str] + error_code: Optional[str] + battery_level: Optional[float] + battery_status: Optional[str] + device_name: Optional[str] + minut_metadata: Optional[Dict[str, Any]] + noise_level_decibels: Optional[float] + noise_level_nrs: Optional[float] + noise_threshold_id: Optional[str] + noise_threshold_name: Optional[str] + noiseaware_metadata: Optional[Dict[str, Any]] + access_code_is_managed: Optional[bool] + is_via_bluetooth: Optional[bool] + is_via_nfc: Optional[bool] + method: Optional[str] + user_identity_id: Optional[str] + reason: Optional[Reason] + climate_preset_key: Optional[str] + is_fallback_climate_preset: Optional[bool] + thermostat_schedule_id: Optional[str] + cooling_set_point_celsius: Optional[float] + cooling_set_point_fahrenheit: Optional[float] + fan_mode_setting: Optional[str] + heating_set_point_celsius: Optional[float] + heating_set_point_fahrenheit: Optional[float] + hvac_mode_setting: Optional[str] + lower_limit_celsius: Optional[float] + lower_limit_fahrenheit: Optional[float] + temperature_celsius: Optional[float] + temperature_fahrenheit: Optional[float] + upper_limit_celsius: Optional[float] + upper_limit_fahrenheit: Optional[float] + desired_temperature_celsius: Optional[float] + desired_temperature_fahrenheit: Optional[float] + activation_reason: Optional[str] + image_url: Optional[str] + motion_sub_type: Optional[str] + video_url: Optional[str] + acs_entrance_ids: Optional[List[str]] + device_ids: Optional[List[str]] + space_id: Optional[str] + space_key: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_code_id=d.get("access_code_id", None), connected_account_custom_metadata=DeepAttrDict( diff --git a/seam/resources/space.py b/seam/resources/space.py index 2712f62b..629fb423 100644 --- a/seam/resources/space.py +++ b/seam/resources/space.py @@ -42,13 +42,16 @@ class CustomerData(ResourceMapping): :ivar time_zone: IANA time zone for the space, e.g. America/Los_Angeles.""" - address: str - default_checkin_time: str - default_checkout_time: str - time_zone: str - + address: Optional[str] + default_checkin_time: Optional[str] + default_checkout_time: Optional[str] + time_zone: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( address=d.get("address", None), default_checkin_time=d.get("default_checkin_time", None), @@ -67,8 +70,11 @@ class Geolocation(ResourceMapping): latitude: float longitude: float + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( latitude=d.get("latitude", None), longitude=d.get("longitude", None), @@ -76,18 +82,21 @@ def from_dict(cls, d: Dict[str, Any]): acs_entrance_count: float created_at: str - customer_data: CustomerData - customer_key: str + customer_data: Optional[CustomerData] + customer_key: Optional[str] device_count: float display_name: str - geolocation: Geolocation + geolocation: Optional[Geolocation] name: str space_id: str - space_key: str + space_key: Optional[str] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_entrance_count=d.get("acs_entrance_count", None), created_at=d.get("created_at", None), diff --git a/seam/resources/thermostat_daily_program.py b/seam/resources/thermostat_daily_program.py index 3876b0d6..e6710a01 100644 --- a/seam/resources/thermostat_daily_program.py +++ b/seam/resources/thermostat_daily_program.py @@ -33,8 +33,11 @@ class Periods(ResourceMapping): climate_preset_key: str starts_at_time: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( climate_preset_key=d.get("climate_preset_key", None), starts_at_time=d.get("starts_at_time", None), @@ -42,13 +45,16 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str device_id: str - name: str + name: Optional[str] periods: List[Periods] thermostat_daily_program_id: str workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), device_id=d.get("device_id", None), diff --git a/seam/resources/thermostat_schedule.py b/seam/resources/thermostat_schedule.py index 4bb71bc9..b51d1dd8 100644 --- a/seam/resources/thermostat_schedule.py +++ b/seam/resources/thermostat_schedule.py @@ -45,8 +45,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -58,15 +61,18 @@ def from_dict(cls, d: Dict[str, Any]): device_id: str ends_at: str errors: List[Errors] - is_override_allowed: bool - max_override_period_minutes: int - name: str + is_override_allowed: Optional[bool] + max_override_period_minutes: Optional[int] + name: Optional[str] starts_at: str thermostat_schedule_id: str workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( climate_preset_key=d.get("climate_preset_key", None), created_at=d.get("created_at", None), diff --git a/seam/resources/unmanaged_access_code.py b/seam/resources/unmanaged_access_code.py index faecc79e..0d39e68d 100644 --- a/seam/resources/unmanaged_access_code.py +++ b/seam/resources/unmanaged_access_code.py @@ -72,17 +72,20 @@ class DormakabaOracodeMetadata(ResourceMapping): :ivar user_level_name: Dormakaba Oracode user level name associated with this access code. """ - is_cancellable: bool - is_early_checkin_able: bool - is_extendable: bool - is_overridable: bool - site_name: str - stay_id: float - user_level_id: str - user_level_name: str - + is_cancellable: Optional[bool] + is_early_checkin_able: Optional[bool] + is_extendable: Optional[bool] + is_overridable: Optional[bool] + site_name: Optional[str] + stay_id: Optional[float] + user_level_id: Optional[str] + user_level_name: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( is_cancellable=d.get("is_cancellable", None), is_early_checkin_able=d.get("is_early_checkin_able", None), @@ -132,31 +135,37 @@ class ModifiedFields(ResourceMapping): :ivar to: The new value of the field.""" field: str - from_: str - to: str + from_: Optional[str] + to: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( field=d.get("field", None), from_=d.get("from", None), to=d.get("to", None), ) - created_at: str + created_at: Optional[str] error_code: str - is_access_code_error: bool + is_access_code_error: Optional[bool] message: str - managed_access_code_id: str - unmanaged_access_code_id: str - change_type: str - modified_fields: List[ModifiedFields] - is_connected_account_error: bool - is_device_error: bool - is_bridge_error: bool - + managed_access_code_id: Optional[str] + unmanaged_access_code_id: Optional[str] + change_type: Optional[str] + modified_fields: Optional[List[ModifiedFields]] + is_connected_account_error: Optional[bool] + is_device_error: Optional[bool] + is_bridge_error: Optional[bool] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -200,25 +209,31 @@ class ModifiedFields(ResourceMapping): :ivar to: The new value of the field.""" field: str - from_: str - to: str + from_: Optional[str] + to: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( field=d.get("field", None), from_=d.get("from", None), to=d.get("to", None), ) - created_at: str + created_at: Optional[str] message: str warning_code: str - change_type: str - modified_fields: List[ModifiedFields] + change_type: Optional[str] + modified_fields: Optional[List[ModifiedFields]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -231,24 +246,27 @@ def from_dict(cls, d: Dict[str, Any]): ) access_code_id: str - cannot_be_managed: bool - cannot_delete_unmanaged_access_code: bool - code: str + cannot_be_managed: Optional[bool] + cannot_delete_unmanaged_access_code: Optional[bool] + code: Optional[str] created_at: str device_id: str - dormakaba_oracode_metadata: DormakabaOracodeMetadata - ends_at: str + dormakaba_oracode_metadata: Optional[DormakabaOracodeMetadata] + ends_at: Optional[str] errors: List[Errors] is_managed: bool - name: str - starts_at: str + name: Optional[str] + starts_at: Optional[str] status: str type: str warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_code_id=d.get("access_code_id", None), cannot_be_managed=d.get("cannot_be_managed", None), diff --git a/seam/resources/unmanaged_access_grant.py b/seam/resources/unmanaged_access_grant.py index 9b2c3f30..354a3d37 100644 --- a/seam/resources/unmanaged_access_grant.py +++ b/seam/resources/unmanaged_access_grant.py @@ -56,10 +56,13 @@ class Errors(ResourceMapping): created_at: str error_code: str message: str - missing_device_ids: List[str] + missing_device_ids: Optional[List[str]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -93,12 +96,15 @@ class From(ResourceMapping): :ivar starts_at: Previous start time for access.""" - device_ids: List[str] - ends_at: str - starts_at: str + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), @@ -117,13 +123,16 @@ class To(ResourceMapping): :ivar starts_at: New start time for access.""" - common_code_key: str - device_ids: List[str] - ends_at: str - starts_at: str + common_code_key: Optional[str] + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( common_code_key=d.get("common_code_key", None), device_ids=d.get("device_ids", None), @@ -132,14 +141,17 @@ def from_dict(cls, d: Dict[str, Any]): ) created_at: str - from_: From + from_: Optional[From] message: str mutation_code: str - to: To - access_method_ids: List[str] + to: Optional[To] + access_method_ids: Optional[List[str]] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), from_=( @@ -170,15 +182,18 @@ class RequestedAccessMethods(ResourceMapping): :ivar mode: Access method mode. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. """ - code: str + code: Optional[str] created_access_method_ids: List[str] created_at: str display_name: str - instant_key_max_use_count: int + instant_key_max_use_count: Optional[int] mode: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( code=d.get("code", None), created_access_method_ids=d.get("created_access_method_ids", None), @@ -225,8 +240,11 @@ class FailedDevices(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_id=d.get("device_id", None), error_code=d.get("error_code", None), @@ -236,15 +254,18 @@ def from_dict(cls, d: Dict[str, Any]): created_at: str message: str warning_code: str - failed_devices: List[FailedDevices] - access_method_ids: List[str] - device_id: str - new_code: str - original_code: str - reason: str - + failed_devices: Optional[List[FailedDevices]] + access_method_ids: Optional[List[str]] + device_id: Optional[str] + new_code: Optional[str] + original_code: Optional[str] + reason: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -264,21 +285,24 @@ def from_dict(cls, d: Dict[str, Any]): access_method_ids: List[str] created_at: str display_name: str - ends_at: str + ends_at: Optional[str] errors: List[Errors] location_ids: List[str] - name: str + name: Optional[str] pending_mutations: List[PendingMutations] requested_access_methods: List[RequestedAccessMethods] - reservation_key: str + reservation_key: Optional[str] space_ids: List[str] starts_at: str - user_identity_id: str + user_identity_id: Optional[str] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_grant_id=d.get("access_grant_id", None), access_method_ids=d.get("access_method_ids", None), diff --git a/seam/resources/unmanaged_access_method.py b/seam/resources/unmanaged_access_method.py index edd18201..0183e341 100644 --- a/seam/resources/unmanaged_access_method.py +++ b/seam/resources/unmanaged_access_method.py @@ -53,8 +53,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -85,12 +88,15 @@ class From(ResourceMapping): :ivar starts_at: Previous start time for access.""" - device_ids: List[str] - ends_at: str - starts_at: str + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), @@ -107,12 +113,15 @@ class To(ResourceMapping): :ivar starts_at: New start time for access.""" - device_ids: List[str] - ends_at: str - starts_at: str + device_ids: Optional[List[str]] + ends_at: Optional[str] + starts_at: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), @@ -120,13 +129,16 @@ def from_dict(cls, d: Dict[str, Any]): ) created_at: str - from_: From + from_: Optional[From] message: str mutation_code: str - to: To + to: Optional[To] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), from_=( @@ -155,10 +167,13 @@ class Warnings(ResourceMapping): created_at: str message: str warning_code: str - original_access_method_id: str + original_access_method_id: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -167,23 +182,26 @@ def from_dict(cls, d: Dict[str, Any]): ) access_method_id: str - code: str + code: Optional[str] created_at: str display_name: str errors: List[Errors] - is_assignment_required: bool - is_encoding_required: bool + is_assignment_required: Optional[bool] + is_encoding_required: Optional[bool] is_issued: bool - is_ready_for_assignment: bool - is_ready_for_encoding: bool - issued_at: str + is_ready_for_assignment: Optional[bool] + is_ready_for_encoding: Optional[bool] + issued_at: Optional[str] mode: str pending_mutations: List[PendingMutations] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( access_method_id=d.get("access_method_id", None), code=d.get("code", None), diff --git a/seam/resources/unmanaged_device.py b/seam/resources/unmanaged_device.py index 34d4eff7..273abc48 100644 --- a/seam/resources/unmanaged_device.py +++ b/seam/resources/unmanaged_device.py @@ -92,13 +92,16 @@ class Errors(ResourceMapping): created_at: str error_code: str - is_connected_account_error: bool - is_device_error: bool + is_connected_account_error: Optional[bool] + is_device_error: Optional[bool] message: str - is_bridge_error: bool + is_bridge_error: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), @@ -119,12 +122,15 @@ class Location(ResourceMapping): :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. """ - location_name: str - time_zone: str - timezone: str + location_name: Optional[str] + time_zone: Optional[str] + timezone: Optional[str] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( location_name=d.get("location_name", None), time_zone=d.get("time_zone", None), @@ -175,17 +181,23 @@ class Battery(ResourceMapping): level: float + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( level=d.get("level", None), ) - battery: Battery + battery: Optional[Battery] is_connected: bool + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( battery=( cls.Battery.from_dict(d.get("battery")) @@ -207,8 +219,11 @@ class Battery(ResourceMapping): level: float status: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( level=d.get("level", None), status=d.get("status", None), @@ -233,16 +248,19 @@ class Model(ResourceMapping): :ivar online_access_codes_supported: Deprecated: use device.can_program_online_access_codes. """ - accessory_keypad_supported: bool - can_connect_accessory_keypad: bool + accessory_keypad_supported: Optional[bool] + can_connect_accessory_keypad: Optional[bool] display_name: str - has_built_in_keypad: bool + has_built_in_keypad: Optional[bool] manufacturer_display_name: str - offline_access_codes_supported: bool - online_access_codes_supported: bool + offline_access_codes_supported: Optional[bool] + online_access_codes_supported: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accessory_keypad_supported=d.get( "accessory_keypad_supported", None @@ -261,20 +279,23 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - accessory_keypad: AccessoryKeypad - battery: Battery - battery_level: float - image_alt_text: str - image_url: str - manufacturer: str - model: Model + accessory_keypad: Optional[AccessoryKeypad] + battery: Optional[Battery] + battery_level: Optional[float] + image_alt_text: Optional[str] + image_url: Optional[str] + manufacturer: Optional[str] + model: Optional[Model] name: str - offline_access_codes_enabled: bool + offline_access_codes_enabled: Optional[bool] online: bool - online_access_codes_enabled: bool + online_access_codes_enabled: Optional[bool] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( accessory_keypad=( cls.AccessoryKeypad.from_dict(d.get("accessory_keypad")) @@ -321,11 +342,14 @@ class Warnings(ResourceMapping): created_at: str message: str warning_code: str - active_access_code_count: int - max_active_access_code_count: int + active_access_code_count: Optional[int] + max_active_access_code_count: Optional[int] + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -336,26 +360,26 @@ def from_dict(cls, d: Dict[str, Any]): ), ) - can_configure_auto_lock: bool - can_hvac_cool: bool - can_hvac_heat: bool - can_hvac_heat_cool: bool - can_program_offline_access_codes: bool - can_program_online_access_codes: bool - can_program_thermostat_programs_as_different_each_day: bool - can_program_thermostat_programs_as_same_each_day: bool - can_program_thermostat_programs_as_weekday_weekend: bool - can_remotely_lock: bool - can_remotely_unlock: bool - can_run_thermostat_programs: bool - can_simulate_connection: bool - can_simulate_disconnection: bool - can_simulate_hub_connection: bool - can_simulate_hub_disconnection: bool - can_simulate_paid_subscription: bool - can_simulate_removal: bool - can_turn_off_hvac: bool - can_unlock_with_code: bool + can_configure_auto_lock: Optional[bool] + can_hvac_cool: Optional[bool] + can_hvac_heat: Optional[bool] + can_hvac_heat_cool: Optional[bool] + can_program_offline_access_codes: Optional[bool] + can_program_online_access_codes: Optional[bool] + can_program_thermostat_programs_as_different_each_day: Optional[bool] + can_program_thermostat_programs_as_same_each_day: Optional[bool] + can_program_thermostat_programs_as_weekday_weekend: Optional[bool] + can_remotely_lock: Optional[bool] + can_remotely_unlock: Optional[bool] + can_run_thermostat_programs: Optional[bool] + can_simulate_connection: Optional[bool] + can_simulate_disconnection: Optional[bool] + can_simulate_hub_connection: Optional[bool] + can_simulate_hub_disconnection: Optional[bool] + can_simulate_paid_subscription: Optional[bool] + can_simulate_removal: Optional[bool] + can_turn_off_hvac: Optional[bool] + can_unlock_with_code: Optional[bool] capabilities_supported: List[str] connected_account_id: str created_at: str @@ -364,13 +388,16 @@ def from_dict(cls, d: Dict[str, Any]): device_type: str errors: List[Errors] is_managed: bool - location: Location - properties: Properties + location: Optional[Location] + properties: Optional[Properties] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( can_configure_auto_lock=d.get("can_configure_auto_lock", None), can_hvac_cool=d.get("can_hvac_cool", None), diff --git a/seam/resources/unmanaged_user_identity.py b/seam/resources/unmanaged_user_identity.py index 307cc2ea..2831e4c6 100644 --- a/seam/resources/unmanaged_user_identity.py +++ b/seam/resources/unmanaged_user_identity.py @@ -49,8 +49,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_system_id=d.get("acs_system_id", None), acs_user_id=d.get("acs_user_id", None), @@ -74,8 +77,11 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -85,16 +91,19 @@ def from_dict(cls, d: Dict[str, Any]): acs_user_ids: List[str] created_at: str display_name: str - email_address: str + email_address: Optional[str] errors: List[Errors] - full_name: str - phone_number: str + full_name: Optional[str] + phone_number: Optional[str] user_identity_id: str warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_user_ids=d.get("acs_user_ids", None), created_at=d.get("created_at", None), diff --git a/seam/resources/user_identity.py b/seam/resources/user_identity.py index d1693265..8d9e5751 100644 --- a/seam/resources/user_identity.py +++ b/seam/resources/user_identity.py @@ -51,8 +51,11 @@ class Errors(ResourceMapping): error_code: str message: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_system_id=d.get("acs_system_id", None), acs_user_id=d.get("acs_user_id", None), @@ -76,8 +79,11 @@ class Warnings(ResourceMapping): message: str warning_code: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), @@ -87,17 +93,20 @@ def from_dict(cls, d: Dict[str, Any]): acs_user_ids: List[str] created_at: str display_name: str - email_address: str + email_address: Optional[str] errors: List[Errors] - full_name: str - phone_number: str + full_name: Optional[str] + phone_number: Optional[str] user_identity_id: str - user_identity_key: str + user_identity_key: Optional[str] warnings: List[Warnings] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( acs_user_ids=d.get("acs_user_ids", None), created_at=d.get("created_at", None), diff --git a/seam/resources/webhook.py b/seam/resources/webhook.py index fba1c282..75353745 100644 --- a/seam/resources/webhook.py +++ b/seam/resources/webhook.py @@ -16,13 +16,16 @@ class Webhook: :ivar webhook_id: ID of the webhook.""" - event_types: List[str] - secret: str + event_types: Optional[List[str]] + secret: Optional[str] url: str webhook_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( event_types=d.get("event_types", None), secret=d.get("secret", None), diff --git a/seam/resources/workspace.py b/seam/resources/workspace.py index 2c89663c..06c9eb1b 100644 --- a/seam/resources/workspace.py +++ b/seam/resources/workspace.py @@ -43,14 +43,17 @@ class ConnectWebviewCustomization(ResourceMapping): :ivar success_message: Success message for `Connect Webviews `_ in the workspace. See also `Customize the Look and Feel of Your Connect Webviews `_. """ - inviter_logo_url: str - logo_shape: str - primary_button_color: str - primary_button_text_color: str - success_message: str - + inviter_logo_url: Optional[str] + logo_shape: Optional[str] + primary_button_color: Optional[str] + primary_button_text_color: Optional[str] + success_message: Optional[str] + + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( inviter_logo_url=d.get("inviter_logo_url", None), logo_shape=d.get("logo_shape", None), @@ -60,18 +63,21 @@ def from_dict(cls, d: Dict[str, Any]): ) company_name: str - connect_partner_name: str - connect_webview_customization: ConnectWebviewCustomization + connect_partner_name: Optional[str] + connect_webview_customization: Optional[ConnectWebviewCustomization] is_publishable_key_auth_enabled: bool is_sandbox: bool is_suspended: bool name: str - organization_id: str - publishable_key: str + organization_id: Optional[str] + publishable_key: Optional[str] workspace_id: str + # The payload is decoded JSON, so every value read out of it is untyped. + # Typing d as Any keeps that at this boundary instead of casting each + # read, and the dataclass fields carry the real types. @classmethod - def from_dict(cls, d: Dict[str, Any]): + def from_dict(cls, d: Any): return cls( company_name=d.get("company_name", None), connect_partner_name=d.get("connect_partner_name", None), diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index e6929152..ddccd477 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -445,7 +445,7 @@ def create( :param use_offline_access_code: Deprecated: Use ``is_offline_access_code`` instead. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -545,7 +545,7 @@ def create_multiple( :param use_backup_access_code_pool: Indicates whether to use a `backup access code pool `_ provided by Seam. If ``true``, you can use ```/access_codes/pull_backup_access_code`` `_. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_ids is not None: json_payload["device_ids"] = device_ids @@ -587,7 +587,7 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non :param device_id: ID of the device for which you want to delete the access code. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -604,7 +604,7 @@ def generate_code(self, *, device_id: str) -> AccessCode: :param device_id: ID of the device for which you want to generate a code. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -631,7 +631,7 @@ def get( :param device_id: ID of the device containing the access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -683,7 +683,7 @@ def list( :param user_identifier_key: Your user ID for the user by which to filter access codes. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_ids is not None: json_payload["access_code_ids"] = access_code_ids @@ -724,7 +724,7 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: :param access_code_id: ID of the access code for which you want to pull a backup access code. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -755,7 +755,7 @@ def report_device_constraints( :param supported_code_lengths: Array of supported code lengths as integers between 4 and 20, inclusive. You can specify either ``supported_code_lengths`` or ``min_code_length``/``max_code_length``. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -838,7 +838,7 @@ def update( :param use_offline_access_code: Deprecated: Use ``is_offline_access_code`` instead. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -911,7 +911,7 @@ def update_multiple( :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if common_code_key is not None: json_payload["common_code_key"] = common_code_key diff --git a/seam/routes/access_codes_simulate.py b/seam/routes/access_codes_simulate.py index 048c13fb..3b0b0064 100644 --- a/seam/routes/access_codes_simulate.py +++ b/seam/routes/access_codes_simulate.py @@ -39,7 +39,7 @@ def create_unmanaged_access_code( :param name: Name of the simulated unmanaged access code. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if code is not None: json_payload["code"] = code diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index da1b17f1..e0dd0612 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -137,7 +137,7 @@ def convert_to_managed( :param is_external_modification_allowed: Indicates whether `external modification `_ of the access code is allowed. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -161,7 +161,7 @@ def delete(self, *, access_code_id: str) -> None: :param access_code_id: ID of the unmanaged access code that you want to delete. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -188,7 +188,7 @@ def get( :param device_id: ID of the device containing the unmanaged access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -223,7 +223,7 @@ def list( :param user_identifier_key: Your user ID for the user by which to filter unmanaged access codes. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -261,7 +261,7 @@ def update( :param is_external_modification_allowed: Indicates whether `external modification `_ of the code is allowed. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index e9550c33..8865a7fa 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -264,7 +264,7 @@ def create( :param starts_at: Date and time at which the validity of the new grant starts, in `ISO 8601 `_ format. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if requested_access_methods is not None: json_payload["requested_access_methods"] = requested_access_methods @@ -305,7 +305,7 @@ def delete(self, *, access_grant_id: str) -> None: """Delete an Access Grant. :param access_grant_id: ID of Access Grant to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_id is not None: json_payload["access_grant_id"] = access_grant_id @@ -327,7 +327,7 @@ def get( :param access_grant_key: Unique key of Access Grant to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_id is not None: json_payload["access_grant_id"] = access_grant_id @@ -357,7 +357,7 @@ def get_related( :param include: :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_ids is not None: json_payload["access_grant_ids"] = access_grant_ids @@ -418,7 +418,7 @@ def list( :param user_identity_id: ID of user identity by which you want to filter the list of Access Grants. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -461,7 +461,7 @@ def request_access_methods( :param requested_access_methods: Array of requested access methods to add to the access grant. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_id is not None: json_payload["access_grant_id"] = access_grant_id @@ -495,7 +495,7 @@ def update( :param starts_at: Date and time at which the validity of the grant starts, in `ISO 8601 `_ format. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_id is not None: json_payload["access_grant_id"] = access_grant_id diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index 2dd8b80d..ffa6796b 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -77,7 +77,7 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: :param access_grant_id: ID of unmanaged Access Grant to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_id is not None: json_payload["access_grant_id"] = access_grant_id @@ -111,7 +111,7 @@ def list( :param user_identity_id: ID of user identity by which you want to filter the list of unmanaged Access Grants. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id @@ -149,7 +149,7 @@ def update( :param access_grant_key: Unique key for the access grant. If not provided, the existing key will be preserved. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_id is not None: json_payload["access_grant_id"] = access_grant_id diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index c92c2d10..87c94a57 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -180,7 +180,7 @@ def assign_card( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_id is not None: json_payload["access_method_id"] = access_method_id @@ -216,7 +216,7 @@ def delete( :param reservation_key: Reservation key of the access grant whose access methods should be deleted. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_id is not None: json_payload["access_method_id"] = access_method_id @@ -245,7 +245,7 @@ def encode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_id is not None: json_payload["access_method_id"] = access_method_id @@ -272,7 +272,7 @@ def get(self, *, access_method_id: str) -> AccessMethod: :param access_method_id: ID of access method to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_id is not None: json_payload["access_method_id"] = access_method_id @@ -297,7 +297,7 @@ def get_related( :param include: :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_ids is not None: json_payload["access_method_ids"] = access_method_ids @@ -341,7 +341,7 @@ def list( :param space_id: ID of the space by which to filter the returned access methods. Must be combined with ``access_grant_id``, ``access_grant_key``, or ``acs_entrance_id``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id @@ -380,7 +380,7 @@ def unlock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_id is not None: json_payload["access_method_id"] = access_method_id diff --git a/seam/routes/access_methods_unmanaged.py b/seam/routes/access_methods_unmanaged.py index a2783641..bd1e1584 100644 --- a/seam/routes/access_methods_unmanaged.py +++ b/seam/routes/access_methods_unmanaged.py @@ -49,7 +49,7 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: :param access_method_id: ID of unmanaged access method to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_id is not None: json_payload["access_method_id"] = access_method_id @@ -77,7 +77,7 @@ def list( :param space_id: ID of the space for which you want to retrieve all unmanaged access methods. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_id is not None: json_payload["access_grant_id"] = access_grant_id diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index 606ffa8d..743ae2a3 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -121,7 +121,7 @@ def add_user( :param user_identity_id: ID of the desired user identity that you want to add to an access group. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id @@ -138,7 +138,7 @@ def delete(self, *, acs_access_group_id: str) -> None: """Deletes a specified `access group `_. :param acs_access_group_id: ID of the access group that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id @@ -153,7 +153,7 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: :param acs_access_group_id: ID of the access group that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id @@ -181,7 +181,7 @@ def list( :param user_identity_id: ID of the user identity for which you want to retrieve all access groups. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -204,7 +204,7 @@ def list_accessible_entrances( :param acs_access_group_id: ID of the access group for which you want to retrieve all accessible entrances. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id @@ -221,7 +221,7 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: :param acs_access_group_id: ID of the access group for which you want to retrieve all access system users. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id @@ -245,7 +245,7 @@ def remove_user( :param user_identity_id: ID of the user identity associated with the user that you want to remove from an access group. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index cc2910bf..2ad19891 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -189,7 +189,7 @@ def assign( :param user_identity_id: ID of the user identity to whom you want to assign a credential. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the credential belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id @@ -248,7 +248,7 @@ def create( :param visionline_metadata: Visionline-specific metadata for the new credential. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method is not None: json_payload["access_method"] = access_method @@ -289,7 +289,7 @@ def delete(self, *, acs_credential_id: str) -> None: """Deletes a specified `credential `_. :param acs_credential_id: ID of the credential that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id @@ -304,7 +304,7 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: :param acs_credential_id: ID of the credential that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id @@ -344,7 +344,7 @@ def list( :param search: String for which to search. Filters returned credentials to include all records that satisfy a partial match using ``display_name``, ``code``, ``card_number``, ``acs_user_id`` or ``acs_credential_id``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id @@ -375,7 +375,7 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran :param acs_credential_id: ID of the credential for which you want to retrieve all entrances to which the credential grants access. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id @@ -401,7 +401,7 @@ def unassign( :param user_identity_id: ID of the user identity from which you want to unassign a credential. You can only provide one of acs_user_id or user_identity_id. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id @@ -429,7 +429,7 @@ def update( :param ends_at: Replacement date and time at which the validity of the credential ends, in `ISO 8601 `_ format. Must be a time in the future and after the ``starts_at`` value that you set when creating the credential. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index 3bd18abb..1ecc91ce 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -143,7 +143,7 @@ def encode_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id @@ -172,7 +172,7 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: :param acs_encoder_id: ID of the encoder that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id @@ -203,7 +203,7 @@ def list( :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -236,7 +236,7 @@ def scan_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id @@ -279,7 +279,7 @@ def scan_to_assign_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id diff --git a/seam/routes/acs_encoders_simulate.py b/seam/routes/acs_encoders_simulate.py index e9c5653a..02553872 100644 --- a/seam/routes/acs_encoders_simulate.py +++ b/seam/routes/acs_encoders_simulate.py @@ -89,7 +89,7 @@ def next_credential_encode_will_fail( :param acs_credential_id: ID of the ``acs_credential`` that will fail to be encoded onto a card in the next request. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id @@ -112,7 +112,7 @@ def next_credential_encode_will_succeed( :param acs_encoder_id: ID of the ``acs_encoder`` that will be used in the next request to encode the ``acs_credential``. :param scenario: Scenario to simulate.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id @@ -140,7 +140,7 @@ def next_credential_scan_will_fail( :param error_code: :param acs_credential_id_on_seam:""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id @@ -169,7 +169,7 @@ def next_credential_scan_will_succeed( :param acs_credential_id_on_seam: ID of the Seam ``acs_credential`` that matches the ``acs_credential`` on the encoder in this simulation. :param scenario: Scenario to simulate.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 30d38cf2..71b462ba 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -121,7 +121,7 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: :param acs_entrance_id: ID of the entrance that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id @@ -145,7 +145,7 @@ def grant_access( :param user_identity_id: ID of the user identity to whom you want to grant access to an entrance. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id @@ -198,7 +198,7 @@ def list( :param space_id: ID of the space for which you want to list entrances. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_method_id is not None: json_payload["access_method_id"] = access_method_id @@ -237,7 +237,7 @@ def list_credentials_with_access( :param include_if: Conditions that credentials must meet to be included in the returned list. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id @@ -266,7 +266,7 @@ def unlock( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index 4e582c1d..144a0c86 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -78,7 +78,7 @@ def get(self, *, acs_system_id: str) -> AcsSystem: :param acs_system_id: ID of the access system that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -105,7 +105,7 @@ def list( :param search: String for which to search. Filters returned access systems to include all records that satisfy a partial match using ``name`` or ``acs_system_id``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id @@ -128,7 +128,7 @@ def list_compatible_credential_manager_acs_systems( :param acs_system_id: ID of the access system for which you want to retrieve all compatible credential manager systems. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -154,7 +154,7 @@ def report_devices( :param acs_encoders: Array of ACS encoders to report :param acs_entrances: Array of ACS entrances to report""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index e5567333..7f57044e 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -265,7 +265,7 @@ def add_to_access_group( :param acs_user_id: ID of the access system user that you want to add to an access group. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id @@ -307,7 +307,7 @@ def create( :param user_identity_id: ID of the user identity with which you want to associate the new access system user. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -345,7 +345,7 @@ def delete( :param user_identity_id: ID of the user identity that you want to delete. You must provide either acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -374,7 +374,7 @@ def get( :param user_identity_id: ID of the user identity that you want to get. You can only provide acs_user_id or user_identity_id. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id @@ -418,7 +418,7 @@ def list( :param user_identity_phone_number: Phone number of the user identity for which you want to retrieve all access system users, in `E.164 format `_ (for example, ``+15555550100``). :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -457,7 +457,7 @@ def list_accessible_entrances( :param user_identity_id: ID of the user identity for whom you want to list accessible entrances. You can only provide acs_user_id or user_identity_id. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -487,7 +487,7 @@ def remove_from_access_group( :param user_identity_id: ID of the user identity that you want to remove from an access group. You can only provide acs_user_id or user_identity_id. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id @@ -515,7 +515,7 @@ def revoke_access_to_all_entrances( :param user_identity_id: ID of the user identity for whom you want to revoke access. You can only provide acs_user_id or user_identity_id. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -543,7 +543,7 @@ def suspend( :param user_identity_id: ID of the user identity that you want to suspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -571,7 +571,7 @@ def unsuspend( :param user_identity_id: ID of the user identity that you want to unsuspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id @@ -617,7 +617,7 @@ def update( :param user_identity_id: ID of the user identity that you want to update. You can only provide acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if access_schedule is not None: json_payload["access_schedule"] = access_schedule diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index f1291ddf..310ff7ca 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -64,7 +64,7 @@ def get( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if action_attempt_id is not None: json_payload["action_attempt_id"] = action_attempt_id @@ -102,7 +102,7 @@ def list( :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if action_attempt_ids is not None: json_payload["action_attempt_ids"] = action_attempt_ids diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index 5e318cc1..80ab10f6 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -189,7 +189,7 @@ def create( :param user_identity_ids: Deprecated: Use ``user_identity_id`` instead. IDs of the `user identities `_ that you want to associate with the client session. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_ids is not None: json_payload["connect_webview_ids"] = connect_webview_ids @@ -216,7 +216,7 @@ def delete(self, *, client_session_id: str) -> None: """Deletes a `client session `_. :param client_session_id: ID of the client session that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if client_session_id is not None: json_payload["client_session_id"] = client_session_id @@ -238,7 +238,7 @@ def get( :param user_identifier_key: User identifier key associated with the client session that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if client_session_id is not None: json_payload["client_session_id"] = client_session_id @@ -274,7 +274,7 @@ def get_or_create( :param user_identity_ids: Deprecated: Use ``user_identity_id``. IDs of the `user identities `_ that you want to associate with the client session. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_ids is not None: json_payload["connect_webview_ids"] = connect_webview_ids @@ -317,7 +317,7 @@ def grant_access( :param user_identity_ids: Deprecated: Use ``user_identity_id``. IDs of the `user identities `_ that you want to associate with the client session. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if client_session_id is not None: json_payload["client_session_id"] = client_session_id @@ -358,7 +358,7 @@ def list( :param without_user_identifier_key: Indicates whether to retrieve only client sessions without associated user identifier keys. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if client_session_id is not None: json_payload["client_session_id"] = client_session_id @@ -381,7 +381,7 @@ def revoke(self, *, client_session_id: str) -> None: Note that `deleting a client session `_ is a separate action. :param client_session_id: ID of the client session that you want to revoke.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if client_session_id is not None: json_payload["client_session_id"] = client_session_id diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index 44cd32ad..bb9a7ca6 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -149,7 +149,7 @@ def create( :param wait_for_device_creation: Indicates whether Seam should finish syncing all devices in a newly-connected account before completing the associated Connect Webview. See also: `Customize the Behavior Settings of Your Connect Webviews `_. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if accepted_capabilities is not None: json_payload["accepted_capabilities"] = accepted_capabilities @@ -184,7 +184,7 @@ def delete(self, *, connect_webview_id: str) -> None: You do not need to delete a Connect Webview once a user completes it. Instead, you can simply ignore completed Connect Webviews. :param connect_webview_id: ID of the Connect Webview that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id @@ -201,7 +201,7 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: :param connect_webview_id: ID of the Connect Webview that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id @@ -235,7 +235,7 @@ def list( :param user_identifier_key: Your user ID for the user by which you want to filter Connect Webviews. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if custom_metadata_has is not None: json_payload["custom_metadata_has"] = custom_metadata_has diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index fbd82829..16e9eafc 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -126,7 +126,7 @@ def delete(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account that you want to delete. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id @@ -145,7 +145,7 @@ def get( :param email: Email address associated with the connected account that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id @@ -184,7 +184,7 @@ def list( :param user_identifier_key: Your user ID for the user by which you want to filter connected accounts. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if custom_metadata_has is not None: json_payload["custom_metadata_has"] = custom_metadata_has @@ -210,7 +210,7 @@ def sync(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account that you want to sync. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id @@ -243,7 +243,7 @@ def update( :param display_name: Human-readable name for the connected account, shown in the dashboard. For example, ``Booking from Airbnb House 1``. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id diff --git a/seam/routes/connected_accounts_simulate.py b/seam/routes/connected_accounts_simulate.py index 3766b03b..a53f14d6 100644 --- a/seam/routes/connected_accounts_simulate.py +++ b/seam/routes/connected_accounts_simulate.py @@ -24,7 +24,7 @@ def disconnect(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account you want to simulate as disconnected. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id diff --git a/seam/routes/customers.py b/seam/routes/customers.py index 26ddfa92..dc465914 100644 --- a/seam/routes/customers.py +++ b/seam/routes/customers.py @@ -229,7 +229,7 @@ def create_portal( :param customer_data: :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if customer_resources_filters is not None: json_payload["customer_resources_filters"] = customer_resources_filters @@ -321,7 +321,7 @@ def delete_data( :param user_identity_keys: List of user identity keys to delete. :param user_keys: List of user keys to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_grant_keys is not None: json_payload["access_grant_keys"] = access_grant_keys @@ -431,7 +431,7 @@ def push_data( :param user_identities: List of user identities. :param users: List of users.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if customer_key is not None: json_payload["customer_key"] = customer_key diff --git a/seam/routes/devices.py b/seam/routes/devices.py index fcec08c9..f11e77f0 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -169,7 +169,7 @@ def get( :param name: Name of the device that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -235,7 +235,7 @@ def list( :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id @@ -286,7 +286,7 @@ def list_device_providers( :param provider_category: Category for which you want to list providers. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if provider_category is not None: json_payload["provider_category"] = provider_category @@ -299,7 +299,7 @@ def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: """Updates provider-specific metadata for devices. :param devices: Array of devices with provider metadata to update""" - json_payload = {} + json_payload: Dict[str, Any] = {} if devices is not None: json_payload["devices"] = devices @@ -333,7 +333,7 @@ def update( :param name: Name for the device. :param properties:""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/devices_simulate.py b/seam/routes/devices_simulate.py index 2ebaa239..29a49bc3 100644 --- a/seam/routes/devices_simulate.py +++ b/seam/routes/devices_simulate.py @@ -72,7 +72,7 @@ def connect(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate connecting to Seam. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -88,7 +88,7 @@ def connect_to_hub(self, *, device_id: str) -> None: This will clear the ``hub_disconnected`` error on the device. :param device_id: ID of the device whose hub you want to reconnect.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -102,7 +102,7 @@ def disconnect(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate disconnecting from Seam. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -119,7 +119,7 @@ def disconnect_from_hub(self, *, device_id: str) -> None: IglooHome bridge offline in sandbox. :param device_id: ID of the device whose hub you want to disconnect.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -136,7 +136,7 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: :param device_id: :param is_expired:""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -152,7 +152,7 @@ def remove(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate removing from Seam. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 8da04525..d7c6e9c1 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -123,7 +123,7 @@ def get( :param name: Name of the unmanaged device that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -191,7 +191,7 @@ def list( :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id @@ -247,7 +247,7 @@ def update( :param is_managed: Indicates whether the device is managed. Set this parameter to ``true`` to convert an unmanaged device to managed. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/events.py b/seam/routes/events.py index eb624c73..639fbb7b 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -141,7 +141,7 @@ def get( :param event_type: Type of the event that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if event_id is not None: json_payload["event_id"] = event_id @@ -245,7 +245,7 @@ def list( :param user_identity_id: ID of the user identity for which you want to list events. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id diff --git a/seam/routes/instant_keys.py b/seam/routes/instant_keys.py index d9af3a77..bf3ab433 100644 --- a/seam/routes/instant_keys.py +++ b/seam/routes/instant_keys.py @@ -48,7 +48,7 @@ def delete(self, *, instant_key_id: str) -> None: """Deletes a specified `Instant Key `_. :param instant_key_id: ID of the Instant Key that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if instant_key_id is not None: json_payload["instant_key_id"] = instant_key_id @@ -70,7 +70,7 @@ def get( :param instant_key_url: URL of the instant key to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if instant_key_id is not None: json_payload["instant_key_id"] = instant_key_id @@ -87,7 +87,7 @@ def list(self, *, user_identity_id: Optional[str] = None) -> List[InstantKey]: :param user_identity_id: ID of the user identity by which you want to filter the list of Instant Keys. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id diff --git a/seam/routes/locks.py b/seam/routes/locks.py index e1f168a5..effc53d1 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -171,7 +171,7 @@ def configure_auto_lock( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if auto_lock_enabled is not None: json_payload["auto_lock_enabled"] = auto_lock_enabled @@ -207,7 +207,7 @@ def get( .. deprecated:: Use ``/devices/get`` instead.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -273,7 +273,7 @@ def list( :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id @@ -325,7 +325,7 @@ def lock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -357,7 +357,7 @@ def unlock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/locks_simulate.py b/seam/routes/locks_simulate.py index 66fde77e..d53fe3e0 100644 --- a/seam/routes/locks_simulate.py +++ b/seam/routes/locks_simulate.py @@ -64,7 +64,7 @@ def keypad_code_entry( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if code is not None: json_payload["code"] = code @@ -98,7 +98,7 @@ def manual_lock_via_keypad( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/noise_sensors.py b/seam/routes/noise_sensors.py index 7c2b517b..598ce75e 100644 --- a/seam/routes/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -152,7 +152,7 @@ def list( :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index 5b62e1ce..80d5e996 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -122,7 +122,7 @@ def create( :param noise_threshold_nrs: Noise level in Noiseaware Noise Risk Score (NRS) for the new noise threshold. This parameter is only relevant for `Noiseaware sensors `_. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -149,7 +149,7 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: :param device_id: ID of the device that contains the noise threshold that you want to delete. :param noise_threshold_id: ID of the noise threshold that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -166,7 +166,7 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: :param noise_threshold_id: ID of the noise threshold that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if noise_threshold_id is not None: json_payload["noise_threshold_id"] = noise_threshold_id @@ -181,7 +181,7 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: :param device_id: ID of the device for which you want to list noise thresholds. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -219,7 +219,7 @@ def update( :param starts_daily_at: Time at which the noise threshold should become active daily. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/noise_sensors_simulate.py b/seam/routes/noise_sensors_simulate.py index 1ce320f2..445ae5ca 100644 --- a/seam/routes/noise_sensors_simulate.py +++ b/seam/routes/noise_sensors_simulate.py @@ -24,7 +24,7 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: :param device_id: ID of the device for which you want to simulate the triggering of a noise threshold. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/phones.py b/seam/routes/phones.py index 1ea284cc..cc39e6d5 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -59,7 +59,7 @@ def deactivate(self, *, device_id: str) -> None: """Deactivates a phone, which is useful, for example, if a user has lost their phone. For more information, see `App User Lost Phone Process `_. :param device_id: Device ID of the phone that you want to deactivate.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -74,7 +74,7 @@ def get(self, *, device_id: str) -> Phone: :param device_id: Device ID of the phone that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -96,7 +96,7 @@ def list( :param owner_user_identity_id: ID of the user identity that represents the owner by which you want to filter the list of returned phones. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id diff --git a/seam/routes/phones_simulate.py b/seam/routes/phones_simulate.py index b11b693a..a1b7af2f 100644 --- a/seam/routes/phones_simulate.py +++ b/seam/routes/phones_simulate.py @@ -53,7 +53,7 @@ def create_sandbox_phone( :param phone_metadata: Metadata that you want to associate with the simulated phone. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index 1add7f83..34e6f995 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -206,7 +206,7 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No :param acs_entrance_ids: IDs of the entrances that you want to add to the space. :param space_id: ID of the space to which you want to add entrances.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: json_payload["acs_entrance_ids"] = acs_entrance_ids @@ -226,7 +226,7 @@ def add_connected_account( :param space_id: ID of the space to which you want to add the connected account. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id @@ -243,7 +243,7 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: :param device_ids: IDs of the devices that you want to add to the space. :param space_id: ID of the space to which you want to add devices.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_ids is not None: json_payload["device_ids"] = device_ids @@ -282,7 +282,7 @@ def create( :param space_key: Unique key for the space within the workspace. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if name is not None: json_payload["name"] = name @@ -307,7 +307,7 @@ def delete(self, *, space_id: str) -> None: """Deletes a space. :param space_id: ID of the space that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if space_id is not None: json_payload["space_id"] = space_id @@ -326,7 +326,7 @@ def get( :param space_key: Unique key of the space that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if space_id is not None: json_payload["space_id"] = space_id @@ -356,7 +356,7 @@ def get_related( :param space_keys: Keys of the spaces that you want to get along with their related resources. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if exclude is not None: json_payload["exclude"] = exclude @@ -393,7 +393,7 @@ def list( :param space_key: Filter spaces by space_key. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if customer_key is not None: json_payload["customer_key"] = customer_key @@ -418,7 +418,7 @@ def remove_acs_entrances( :param acs_entrance_ids: IDs of the entrances that you want to remove from the space. :param space_id: ID of the space from which you want to remove entrances.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: json_payload["acs_entrance_ids"] = acs_entrance_ids @@ -438,7 +438,7 @@ def remove_connected_account( :param space_id: ID of the space from which you want to remove the connected account. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id @@ -455,7 +455,7 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: :param device_ids: IDs of the devices that you want to remove from the space. :param space_id: ID of the space from which you want to remove devices.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_ids is not None: json_payload["device_ids"] = device_ids @@ -491,7 +491,7 @@ def update( :param space_key: Unique key of the space that you want to update. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: json_payload["acs_entrance_ids"] = acs_entrance_ids diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index 2008310d..338fcfd0 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -458,7 +458,7 @@ def activate_climate_preset( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if climate_preset_key is not None: json_payload["climate_preset_key"] = climate_preset_key @@ -500,7 +500,7 @@ def cool( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -565,7 +565,7 @@ def create_climate_preset( :param name: User-friendly name to identify the `climate preset `_. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if climate_preset_key is not None: json_payload["climate_preset_key"] = climate_preset_key @@ -603,7 +603,7 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N :param device_id: ID of the thermostat device for which you want to delete a climate preset. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if climate_preset_key is not None: json_payload["climate_preset_key"] = climate_preset_key @@ -633,7 +633,7 @@ def heat( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -681,7 +681,7 @@ def heat_cool( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -763,7 +763,7 @@ def list( :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id @@ -815,7 +815,7 @@ def off( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -843,7 +843,7 @@ def set_fallback_climate_preset( :param device_id: ID of the thermostat device for which you want to set the fallback climate preset. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if climate_preset_key is not None: json_payload["climate_preset_key"] = climate_preset_key @@ -873,7 +873,7 @@ def set_fan_mode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -924,7 +924,7 @@ def set_hvac_mode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -974,7 +974,7 @@ def set_temperature_threshold( :param upper_limit_fahrenheit: Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either ``upper_limit`` but not both. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -1033,7 +1033,7 @@ def update_climate_preset( :param name: User-friendly name to identify the `climate preset `_. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if climate_preset_key is not None: json_payload["climate_preset_key"] = climate_preset_key @@ -1098,7 +1098,7 @@ def update_weekly_program( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index 5ce877ae..d1662efe 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -70,7 +70,7 @@ def create( :param periods: Array of thermostat daily program periods. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -88,7 +88,7 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: :param thermostat_daily_program_id: ID of the thermostat daily program that you want to delete. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if thermostat_daily_program_id is not None: json_payload["thermostat_daily_program_id"] = thermostat_daily_program_id @@ -116,7 +116,7 @@ def update( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if name is not None: json_payload["name"] = name diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index 6acd9c91..3b6d2340 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -131,7 +131,7 @@ def create( :param name: Name of the thermostat schedule. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if climate_preset_key is not None: json_payload["climate_preset_key"] = climate_preset_key @@ -157,7 +157,7 @@ def delete(self, *, thermostat_schedule_id: str) -> None: :param thermostat_schedule_id: ID of the thermostat schedule that you want to delete. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if thermostat_schedule_id is not None: json_payload["thermostat_schedule_id"] = thermostat_schedule_id @@ -172,7 +172,7 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: :param thermostat_schedule_id: ID of the thermostat schedule that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if thermostat_schedule_id is not None: json_payload["thermostat_schedule_id"] = thermostat_schedule_id @@ -191,7 +191,7 @@ def list( :param user_identifier_key: User identifier key by which to filter the list of returned thermostat schedules. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -231,7 +231,7 @@ def update( :param starts_at: Date and time at which the thermostat schedule starts, in `ISO 8601 `_ format. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if thermostat_schedule_id is not None: json_payload["thermostat_schedule_id"] = thermostat_schedule_id diff --git a/seam/routes/thermostats_simulate.py b/seam/routes/thermostats_simulate.py index ae488ec5..8695b23f 100644 --- a/seam/routes/thermostats_simulate.py +++ b/seam/routes/thermostats_simulate.py @@ -80,7 +80,7 @@ def hvac_mode_adjusted( :param heating_set_point_fahrenheit: Heating `set point `_ in °F that you want to simulate. You must set ``heating_set_point_fahrenheit`` or ``heating_set_point_celsius``. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -114,7 +114,7 @@ def temperature_reached( :param temperature_fahrenheit: Temperature in °F that you want simulate the thermostat reaching. You must set ``temperature_fahrenheit`` or ``temperature_celsius``. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index f6d140bc..948620fe 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -258,7 +258,7 @@ def add_acs_user( :param user_identity_key: Key of the user identity to which you want to add an access system user. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id @@ -293,7 +293,7 @@ def create( :param user_identity_key: Unique key for the new user identity. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_system_ids is not None: json_payload["acs_system_ids"] = acs_system_ids @@ -314,7 +314,7 @@ def delete(self, *, user_identity_id: str) -> None: """Deletes a specified `user identity `_. This deletes the user identity and all associated resources, including any `credentials `_, `acs users `_ and `client sessions `_. :param user_identity_id: ID of the user identity that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -339,7 +339,7 @@ def generate_instant_key( :param max_use_count: Maximum number of times the instant key can be used. Default: 1. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -367,7 +367,7 @@ def get( :param user_identity_key: :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -385,7 +385,7 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No :param user_identity_id: ID of the user identity that you want to grant access to a device. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -421,7 +421,7 @@ def list( :param user_identity_ids: Array of user identity IDs by which to filter the list of user identities. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if created_before is not None: json_payload["created_before"] = created_before @@ -448,7 +448,7 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: :param user_identity_id: ID of the user identity for which you want to retrieve all accessible devices. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -465,7 +465,7 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc :param user_identity_id: ID of the user identity for which you want to retrieve all accessible entrances. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -482,7 +482,7 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: :param user_identity_id: ID of the user identity for which you want to retrieve all access systems. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -497,7 +497,7 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: :param user_identity_id: ID of the user identity for which you want to retrieve all access system users. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -513,7 +513,7 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: :param user_identity_id: ID of the user identity from which you want to remove an access system user. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id @@ -531,7 +531,7 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N :param user_identity_id: ID of the user identity from which you want to revoke access to a device. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id @@ -562,7 +562,7 @@ def update( :param phone_number: Unique phone number for the user identity. :param user_identity_key: Unique key for the user identity.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index c5109c2c..f4de6364 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -69,7 +69,7 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: :param user_identity_id: ID of the unmanaged user identity that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id @@ -97,7 +97,7 @@ def list( :param search: String for which to search. Filters returned unmanaged user identities to include all records that satisfy a partial match using ``full_name``, ``phone_number``, ``email_address``, ``user_identity_id`` or ``acs_system_id``. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if created_before is not None: json_payload["created_before"] = created_before @@ -131,7 +131,7 @@ def update( :param user_identity_key: Unique key for the user identity. If not provided, the existing key will be preserved. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if is_managed is not None: json_payload["is_managed"] = is_managed diff --git a/seam/routes/webhooks.py b/seam/routes/webhooks.py index 444bed45..b82cd1fa 100644 --- a/seam/routes/webhooks.py +++ b/seam/routes/webhooks.py @@ -63,7 +63,7 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo :param event_types: Types of events that you want the new webhook to receive. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if url is not None: json_payload["url"] = url @@ -78,7 +78,7 @@ def delete(self, *, webhook_id: str) -> None: """Deletes a specified `webhook `_. :param webhook_id: ID of the webhook that you want to delete.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if webhook_id is not None: json_payload["webhook_id"] = webhook_id @@ -93,7 +93,7 @@ def get(self, *, webhook_id: str) -> Webhook: :param webhook_id: ID of the webhook that you want to get. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if webhook_id is not None: json_payload["webhook_id"] = webhook_id @@ -106,7 +106,7 @@ def list(self) -> List[Webhook]: """Returns a list of all `webhooks `_. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} res = self.client.post("/webhooks/list", json=json_payload) @@ -118,7 +118,7 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: :param event_types: Types of events that you want the webhook to receive. :param webhook_id: ID of the webhook that you want to update.""" - json_payload = {} + json_payload: Dict[str, Any] = {} if event_types is not None: json_payload["event_types"] = event_types diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index d223c346..4dd397d2 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -142,7 +142,7 @@ def create( :param webview_success_message: Deprecated: Use ``connect_webview_customization.webview_success_message`` instead. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} if name is not None: json_payload["name"] = name @@ -177,7 +177,7 @@ def get(self) -> Workspace: """Returns the `workspace `_ associated with the authentication value. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} res = self.client.post("/workspaces/get", json=json_payload) @@ -187,7 +187,7 @@ def list(self) -> List[Workspace]: """Returns a list of `workspaces `_ associated with the authentication value. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} res = self.client.post("/workspaces/list", json=json_payload) @@ -201,7 +201,7 @@ def reset_sandbox( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload = {} + json_payload: Dict[str, Any] = {} res = self.client.post("/workspaces/reset_sandbox", json=json_payload) @@ -241,7 +241,7 @@ def update( :param organization_id: ID of the organization to assign the workspace to. The authenticated user must be the owner of the workspace and an admin of the target organization. """ - json_payload = {} + json_payload: Dict[str, Any] = {} if connect_partner_name is not None: json_payload["connect_partner_name"] = connect_partner_name diff --git a/seam/seam.py b/seam/seam.py index 97442403..f552e36c 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -104,7 +104,10 @@ def __init__( niquests_options=niquests_options, ) - Routes.__init__(self, client=self.client, defaults=self.defaults) + # Seam and Routes are siblings under AbstractRoutes rather than parent + # and child, so borrowing this initializer to attach the route + # namespaces passes a self the signature does not admit. + Routes.__init__(self, client=self.client, defaults=self.defaults) # type: ignore[arg-type] def create_paginator( self, request: Callable, params: Optional[Dict[str, Any]] = None, / diff --git a/test/paginator_test.py b/test/paginator_test.py index 800cf78e..65af163b 100644 --- a/test/paginator_test.py +++ b/test/paginator_test.py @@ -14,7 +14,7 @@ def test_paginator_next_page_requires_a_cursor(seam: Seam): paginator = seam.create_paginator(seam.connected_accounts.list, {"limit": 2}) with pytest.raises(ValueError, match=r"next_page_cursor"): - paginator.next_page(None) + paginator.next_page(None) # type: ignore[arg-type] with pytest.raises(ValueError, match=r"next_page_cursor"): paginator.next_page("") @@ -24,8 +24,12 @@ def test_paginator_last_page_has_no_next_page(seam: Seam): paginator = seam.create_paginator(seam.connected_accounts.list, {"limit": 2}) _, first_pagination = paginator.first_page() + assert first_pagination is not None + assert first_pagination.next_page_cursor is not None + _, next_pagination = paginator.next_page(first_pagination.next_page_cursor) + assert next_pagination is not None assert next_pagination.has_next_page is False assert next_pagination.next_page_cursor is None @@ -47,7 +51,9 @@ def test_paginator_next_page(seam: Seam): first_page_accounts, first_pagination = paginator.first_page() assert len(first_page_accounts) == 2 + assert first_pagination is not None assert first_pagination.has_next_page is True + assert first_pagination.next_page_cursor is not None next_page_accounts, _ = paginator.next_page(first_pagination.next_page_cursor) From d635b5203d52acdbf3835c9aacbbdbb10bb4da9a Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 21:13:43 -0700 Subject: [PATCH 05/39] feat: Replace niquests with httpx (#604) * feat!: Replace niquests with httpx as the HTTP client The niquests dependency urllib3-future installs into the urllib3 package namespace, which conflicts with packages depending on genuine urllib3 in customer environments and cannot be mitigated from the SDK. httpx is already in the dependency tree via svix and its dependency chain does not touch urllib3. BREAKING CHANGE: The niquests_options option is now httpx_options and is passed to the underlying httpx.Client. The retries option now takes a seam.Retry instead of a urllib3.util.Retry; the fields mirror the urllib3 names. Seam.client is now an httpx.Client subclass, and request errors are raised as httpx exceptions, e.g. httpx. HTTPStatusError instead of niquests.HTTPError and httpx. TimeoutException instead of niquests.exceptions.Timeout. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * feat: Use httpx-retries instead of a hand-rolled retry transport The retries option now takes an httpx_retries.Retry, re-exported as seam.Retry. Its fields mirror the urllib3.util.Retry names. Unlike the previous urllib3 default, the default policy does not retry connection errors for POST requests; pass allowed_methods=["POST"] to opt in to retrying Seam API requests. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * feat: Apply configured retry policy to API requests regardless of method Which HTTP methods the Seam API uses is not part of the SDK's public API, so a configured retry policy must not require consumers to name them in allowed_methods. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * chore: Remove unused dataclasses-json dependency Resource classes define their own from_dict on standard library dataclasses and nothing imports dataclasses_json. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * fix: Relax httpx version constraints The SDK is a library, so keep dependency constraints as permissive as possible. The httpx floor matches svix, and the httpx-retries floor is the release that introduced Retry.copy_with. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * fix: Do not override the configured retry policy allowed_methods The retries option should not silently modify the policy it is given. Since httpx-retries does not treat POST as retryable, the retry policy currently has no effect on Seam API requests; the affected tests are marked xfail. A follow-up PR will apply the retry policy to API requests without exposing the HTTP method in the SDK's public API. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * style: Import httpx names directly except for the client Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 * style: Reword fixme comment flagged by pylint Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QxwdLpWpFhxSn7mZ2Fb6p1 --------- Co-authored-by: Claude --- README.rst | 36 ++- pyproject.toml | 4 +- seam/__init__.py | 1 + seam/client.py | 66 ++---- seam/paginator.py | 22 +- seam/seam.py | 22 +- seam/seam_without_workspace.py | 21 +- test/http_error_test.py | 12 +- test/retry_test.py | 21 +- test/timeout_test.py | 23 +- uv.lock | 421 ++------------------------------- 11 files changed, 133 insertions(+), 516 deletions(-) diff --git a/README.rst b/README.rst index 8b1c5891..3c6d2289 100644 --- a/README.rst +++ b/README.rst @@ -69,7 +69,9 @@ Contents * `Setting the request timeout`_ - * `Configuring the niquests session`_ + * `Configuring retries`_ + + * `Configuring the httpx client`_ * `Development and Testing`_ @@ -470,20 +472,40 @@ Pass the ``timeout`` option, in seconds, to override this: Setting it to ``None`` disables the timeout entirely. -A request that exceeds the timeout raises ``niquests.exceptions.Timeout``. +A request that exceeds the timeout raises ``httpx.TimeoutException``. + +Configuring retries +^^^^^^^^^^^^^^^^^^^ + +Pass the ``retries`` option to configure retry behavior. +Retries are handled by `httpx-retries `_, +and its ``Retry`` class is re-exported from ``seam`` for convenience: -Configuring the niquests session -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: python + + from seam import Seam, Retry -For control the options above do not cover, pass ``niquests_options``. -These are handed to the underlying niquests ``Session`` and take + seam = Seam( + api_key="your-api-key", + retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[503]), + ) + +Configuring the httpx client +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For control the options above do not cover, pass ``httpx_options``. +These are handed to the underlying httpx ``Client`` and take precedence over the defaults the SDK sets: .. code-block:: python + from httpx import Limits + seam = Seam( api_key="your-api-key", - niquests_options={"pool_connections": 20, "pool_maxsize": 25}, + httpx_options={ + "limits": Limits(max_connections=25, max_keepalive_connections=20), + }, ) Development and Testing diff --git a/pyproject.toml b/pyproject.toml index 25300032..2f9fa36b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,8 +8,8 @@ license-files = ["LICENSE.txt"] readme = "README.rst" requires-python = ">=3.10" dependencies = [ - "dataclasses-json>=0.6.4,<0.7", - "niquests>=3.6.4,<4", + "httpx>=0.23.0,<1", + "httpx-retries>=0.6.0,<1", "svix>=1.24.0,<2", ] diff --git a/seam/__init__.py b/seam/__init__.py index de402cd8..30e17b6c 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -2,6 +2,7 @@ from .seam import Seam from .seam_without_workspace import SeamWithoutWorkspace +from httpx_retries import Retry from .options import SeamInvalidOptionsError from .auth import SeamInvalidTokenError from .exceptions import ( diff --git a/seam/client.py b/seam/client.py index e637020b..fa88416f 100644 --- a/seam/client.py +++ b/seam/client.py @@ -1,11 +1,11 @@ from typing import Any, Dict, Optional -from urllib.parse import urljoin -import niquests as requests from importlib.metadata import version -from inspect import signature -from urllib3.util import Retry import abc +import httpx +from httpx import Response +from httpx_retries import Retry, RetryTransport + from .constants import DEFAULT_TIMEOUT, LTS_VERSION from .exceptions import ( SeamHttpApiError, @@ -21,10 +21,6 @@ DEFAULT_RETRIES = Retry() -NIQUESTS_TIMEOUT_DEFAULT = ( - signature(requests.Session.post).parameters["timeout"].default -) - class AbstractSeamHttpClient(abc.ABC): @abc.abstractmethod @@ -36,83 +32,69 @@ def request(self, method: str, url: str, *args, **kwargs): raise NotImplementedError @abc.abstractmethod - def _handle_response(self, response: requests.Response): + def _handle_response(self, response: Response): raise NotImplementedError @abc.abstractmethod - def _handle_error_response(self, response: requests.Response, status_code: int): + def _handle_error_response(self, response: Response): raise NotImplementedError -class SeamHttpClient(requests.Session, AbstractSeamHttpClient): +class SeamHttpClient(httpx.Client, AbstractSeamHttpClient): def __init__( self, base_url: str, auth_headers: Dict[str, str], retries: Optional[Retry] = DEFAULT_RETRIES, timeout: Optional[float] = DEFAULT_TIMEOUT, - niquests_options: Optional[Dict[str, Any]] = None, + httpx_options: Optional[Dict[str, Any]] = None, **kwargs, ): - # niquests.Session mounts its adapters while initializing, so retries - # must be passed through here. Assigning self.retries afterwards leaves - # the mounted adapters on their default and the option has no effect. options = { - "retries": DEFAULT_RETRIES if retries is None else retries, + "base_url": base_url, + "timeout": timeout, **kwargs, - **(niquests_options or {}), + **(httpx_options or {}), } custom_headers = options.pop("headers", {}) - super().__init__(**options) - - self.base_url = base_url + if "transport" not in options: + options["transport"] = RetryTransport( + retry=DEFAULT_RETRIES if retries is None else retries + ) - self.timeout = timeout + super().__init__(**options) headers = {**auth_headers, **custom_headers, **SDK_HEADERS} self.headers.update(headers) # request returns the decoded body rather than the Response that - # niquests.Session promises, so the verb helpers routed through it have to + # httpx.Client promises, so the verb helpers routed through it have to # say so too. Without these overrides callers see the inherited Response # type and indexing the returned payload does not type check. def get(self, url, **kwargs) -> Any: return self.request("GET", url, **kwargs) - # data and json are named rather than collected into *args because - # Session.request takes params in the position Session.post gives data. def post(self, url, data=None, json=None, **kwargs) -> Any: return self.request("POST", url, data=data, json=json, **kwargs) def request(self, method, url, *args, **kwargs) -> Any: - url = urljoin(self.base_url, url) - - if kwargs.get("timeout", NIQUESTS_TIMEOUT_DEFAULT) == NIQUESTS_TIMEOUT_DEFAULT: - kwargs["timeout"] = self.timeout - response = super().request(method, url, *args, **kwargs) return self._handle_response(response) - def _handle_response(self, response: requests.Response): - # niquests types status_code as optional because a Response exists - # before it has one. Anything reaching here has been received, so a - # missing status is an error the SDK cannot classify itself. - status_code = response.status_code - - if status_code is None: - response.raise_for_status() - elif not 200 <= status_code < 300: - self._handle_error_response(response, status_code) + def _handle_response(self, response: Response): + if not 200 <= response.status_code < 300: + self._handle_error_response(response) if "application/json" in response.headers.get("content-type", ""): return response.json() return response.text - def _handle_error_response(self, response: requests.Response, status_code: int): + def _handle_error_response(self, response: Response): + status_code = response.status_code request_id = response.headers.get("seam-request-id") if status_code == 401: @@ -138,7 +120,7 @@ def _handle_error_response(self, response: requests.Response, status_code: int): raise SeamHttpApiError(error_details, status_code, request_id) -def is_api_error_response(response: requests.Response) -> bool: +def is_api_error_response(response: Response) -> bool: try: content_type = response.headers.get("content-type", "") @@ -148,7 +130,7 @@ def is_api_error_response(response: requests.Response) -> bool: return False data = response.json() - except (ValueError, requests.exceptions.JSONDecodeError): + except ValueError: return False if not isinstance(data, dict): diff --git a/seam/paginator.py b/seam/paginator.py index 119173ca..62aa3a9b 100644 --- a/seam/paginator.py +++ b/seam/paginator.py @@ -1,6 +1,7 @@ -from typing import Callable, Dict, Any, Optional, Tuple, Generator, List, Union +from typing import Callable, Dict, Any, Optional, Tuple, Generator, List +from json import JSONDecodeError +from httpx import Response from .client import SeamHttpClient -from niquests import PreparedRequest, Response, JSONDecodeError from .pagination import Pagination @@ -34,11 +35,11 @@ def __init__( def first_page(self) -> Tuple[List[Any], Pagination | None]: """Fetches the first page of results.""" - self.client.hooks["response"].append( + self.client.event_hooks["response"].append( lambda response: self._cache_pagination(response, self._FIRST_PAGE) ) data = self._request(**self._params) - self.client.hooks["response"].pop() + self.client.event_hooks["response"].pop() pagination = self._pagination_cache.get(self._FIRST_PAGE) @@ -56,11 +57,11 @@ def next_page( "page_cursor": next_page_cursor, } - self.client.hooks["response"].append( + self.client.event_hooks["response"].append( lambda response: self._cache_pagination(response, next_page_cursor) ) data = self._request(**params) - self.client.hooks["response"].pop() + self.client.event_hooks["response"].pop() pagination = self._pagination_cache.get(next_page_cursor) @@ -92,14 +93,11 @@ def flatten(self) -> Generator[Any, None, None]: if current_items: yield from current_items - def _cache_pagination( - self, response: Union[PreparedRequest, Response], page_key: str - ) -> None: + def _cache_pagination(self, response: Response, page_key: str) -> None: """Extracts pagination dict from response, creates Pagination object, and caches it.""" - if not isinstance(response, Response): - return - try: + # httpx response hooks fire before the response body is read. + response.read() response_json = response.json() pagination = response_json.get("pagination", {}) except JSONDecodeError: diff --git a/seam/seam.py b/seam/seam.py index f552e36c..bc454fbe 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -1,6 +1,6 @@ from typing import Any, Optional, Union, Dict, Callable from typing_extensions import Self -from urllib3.util.retry import Retry +from httpx_retries import Retry from .constants import DEFAULT_TIMEOUT, LTS_VERSION from .parse_options import parse_options @@ -43,7 +43,7 @@ def __init__( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = True, retries: Optional[Retry] = None, timeout: Optional[float] = DEFAULT_TIMEOUT, - niquests_options: Optional[Dict[str, Any]] = None, + httpx_options: Optional[Dict[str, Any]] = None, ): """Initialize a Seam client instance. @@ -71,13 +71,13 @@ def __init__( 'timeout' and 'poll_interval' keys :type wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] :param retries: Configuration for retry behavior on failed requests - :type retries: Optional[urllib3.util.Retry] + :type retries: Optional[httpx_retries.Retry] :param timeout: The request timeout in seconds. Defaults to 30 seconds. Pass None for no timeout :type timeout: Optional[float] - :param niquests_options: Options passed through to the underlying - niquests Session, for control the other options do not cover - :type niquests_options: Optional[Dict[str, Any]] + :param httpx_options: Options passed through to the underlying + httpx Client, for control the other options do not cover + :type httpx_options: Optional[Dict[str, Any]] :raises SeamInvalidOptionsError: If neither api_key nor personal_access_token is provided, or if workspace_id is missing @@ -101,7 +101,7 @@ def __init__( auth_headers=auth_headers, retries=retries, timeout=timeout, - niquests_options=niquests_options, + httpx_options=httpx_options, ) # Seam and Routes are siblings under AbstractRoutes rather than parent @@ -143,7 +143,7 @@ def from_api_key( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = True, retries: Optional[Retry] = None, timeout: Optional[float] = DEFAULT_TIMEOUT, - niquests_options: Optional[Dict[str, Any]] = None, + httpx_options: Optional[Dict[str, Any]] = None, ) -> Self: """Create a Seam instance using an API key. @@ -173,7 +173,7 @@ def from_api_key( wait_for_action_attempt=wait_for_action_attempt, retries=retries, timeout=timeout, - niquests_options=niquests_options, + httpx_options=httpx_options, ) @classmethod @@ -186,7 +186,7 @@ def from_personal_access_token( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = True, retries: Optional[Retry] = None, timeout: Optional[float] = DEFAULT_TIMEOUT, - niquests_options: Optional[Dict[str, Any]] = None, + httpx_options: Optional[Dict[str, Any]] = None, ) -> Self: """Create a Seam instance using a personal access token. @@ -220,5 +220,5 @@ def from_personal_access_token( wait_for_action_attempt=wait_for_action_attempt, retries=retries, timeout=timeout, - niquests_options=niquests_options, + httpx_options=httpx_options, ) diff --git a/seam/seam_without_workspace.py b/seam/seam_without_workspace.py index c413ab25..67b3f23c 100644 --- a/seam/seam_without_workspace.py +++ b/seam/seam_without_workspace.py @@ -1,7 +1,6 @@ from typing import Any, Dict, Optional, Union -import niquests as requests from typing_extensions import Self -from urllib3.util import Retry +from httpx_retries import Retry from .constants import DEFAULT_TIMEOUT, LTS_VERSION from .parse_options import parse_without_workspace_options @@ -52,7 +51,7 @@ def __init__( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = True, retries: Optional[Retry] = None, timeout: Optional[float] = DEFAULT_TIMEOUT, - niquests_options: Optional[Dict[str, Any]] = None, + httpx_options: Optional[Dict[str, Any]] = None, ): """ Initialize a SeamWithoutWorkspace client instance. @@ -72,13 +71,13 @@ def __init__( 'timeout' and 'poll_interval' keys :type wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] :param retries: Configuration for retry behavior on failed requests - :type retries: Optional[urllib3.util.Retry] + :type retries: Optional[httpx_retries.Retry] :param timeout: The request timeout in seconds. Defaults to 30 seconds. Pass None for no timeout :type timeout: Optional[float] - :param niquests_options: Options passed through to the underlying - niquests Session, for control the other options do not cover - :type niquests_options: Optional[Dict[str, Any]] + :param httpx_options: Options passed through to the underlying + httpx Client, for control the other options do not cover + :type httpx_options: Optional[Dict[str, Any]] :raises SeamInvalidOptionsError: If no personal_access_token is provided and the SEAM_PERSONAL_ACCESS_TOKEN environment variable is not set @@ -97,7 +96,7 @@ def __init__( auth_headers=auth_headers, retries=retries, timeout=timeout, - niquests_options=niquests_options, + httpx_options=httpx_options, ) defaults = {"wait_for_action_attempt": wait_for_action_attempt} @@ -114,7 +113,7 @@ def from_personal_access_token( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = True, retries: Optional[Retry] = None, timeout: Optional[float] = DEFAULT_TIMEOUT, - niquests_options: Optional[Dict[str, Any]] = None, + httpx_options: Optional[Dict[str, Any]] = None, ) -> Self: """ Create a SeamWithoutWorkspace instance using a personal access token. @@ -131,7 +130,7 @@ def from_personal_access_token( 'timeout' and 'poll_interval' keys :type wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] :param retries: Configuration for retry behavior on failed requests - :type retries: Optional[urllib3.util.Retry] + :type retries: Optional[httpx_retries.Retry] :return: A new instance of the SeamWithoutWorkspace class authenticated with the provided personal access token :rtype: Self @@ -147,5 +146,5 @@ def from_personal_access_token( wait_for_action_attempt=wait_for_action_attempt, retries=retries, timeout=timeout, - niquests_options=niquests_options, + httpx_options=httpx_options, ) diff --git a/test/http_error_test.py b/test/http_error_test.py index d737cfcc..44fe2bb2 100644 --- a/test/http_error_test.py +++ b/test/http_error_test.py @@ -1,5 +1,5 @@ import pytest -import niquests +from httpx import HTTPStatusError from seam import Seam from seam.exceptions import ( SeamHttpApiError, @@ -56,7 +56,7 @@ def test_seam_http_throws_http_error_on_non_standard_response(server): json={"workspace_id": seed["seed_workspace_1"], "routes": ["/devices/list"]}, ) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 503 @@ -69,7 +69,7 @@ def test_seam_http_raises_http_error_on_non_json_response(recording_server): with recording_server([(500, "Internal Server Error")]) as (endpoint, _): seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 500 @@ -81,7 +81,7 @@ def test_seam_http_raises_http_error_on_malformed_json(recording_server): with recording_server(responses) as (endpoint, _): seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 500 @@ -91,7 +91,7 @@ def test_seam_http_raises_http_error_on_json_without_error_object(recording_serv with recording_server([(500, {"message": "Some error"})]) as (endpoint, _): seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 500 @@ -103,7 +103,7 @@ def test_seam_http_raises_http_error_on_error_object_without_type_and_message( with recording_server([(500, {"error": {"code": 500}})]) as (endpoint, _): seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 500 diff --git a/test/retry_test.py b/test/retry_test.py index d1575ac9..5a6172f8 100644 --- a/test/retry_test.py +++ b/test/retry_test.py @@ -1,13 +1,17 @@ -import niquests import pytest -from urllib3.util import Retry +from httpx import HTTPStatusError -from seam import Seam +from seam import Retry, Seam SERVICE_UNAVAILABLE = (503, "Service Unavailable") DEVICES = (200, {"devices": [{"device_id": "august_device_1"}]}) +# The retries option has no effect on API requests because the Seam API +# uses POST, which httpx-retries does not treat as retryable. A follow-up +# PR will apply the retry policy to API requests without exposing the +# HTTP method in the SDK's public API, then remove the xfail markers. +@pytest.mark.xfail(reason="TODO: Apply the retry policy to API requests") def test_seam_retries_service_unavailable_responses(recording_server): expected_retry_count = 2 responses = [SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE, DEVICES] @@ -24,6 +28,7 @@ def test_seam_retries_service_unavailable_responses(recording_server): assert len(requests) == expected_retry_count + 1 +@pytest.mark.xfail(reason="TODO: Apply the retry policy to API requests") def test_seam_stops_retrying_once_retries_are_exhausted(recording_server): expected_retry_count = 1 @@ -34,7 +39,7 @@ def test_seam_stops_retrying_once_retries_are_exhausted(recording_server): retries=retry_policy(total=expected_retry_count), ) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 503 @@ -47,7 +52,7 @@ def test_seam_does_not_retry_when_retries_are_disabled(recording_server): "seam_apikey_token", endpoint=endpoint, retries=retry_policy(total=0) ) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 503 @@ -68,17 +73,17 @@ def test_seam_surfaces_service_unavailable_from_a_workspace_outage(server): }, ) - with pytest.raises(niquests.HTTPError) as exc_info: + with pytest.raises(HTTPStatusError) as exc_info: seam.devices.list() assert exc_info.value.response.status_code == 503 +# The policy omits allowed_methods: the SDK must retry its own API +# requests without consumers knowing which HTTP methods those use. def retry_policy(*, total): return Retry( total=total, status_forcelist=[503], - allowed_methods=["POST"], backoff_factor=0, - raise_on_status=False, ) diff --git a/test/timeout_test.py b/test/timeout_test.py index 5e3cebcd..f30f8554 100644 --- a/test/timeout_test.py +++ b/test/timeout_test.py @@ -3,11 +3,10 @@ from contextlib import contextmanager from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer -import niquests import pytest -from urllib3.util import Retry +from httpx import Timeout, TimeoutException -from seam import Seam +from seam import Retry, Seam from seam.constants import DEFAULT_TIMEOUT @@ -15,24 +14,24 @@ def test_timeout_defaults_to_30_seconds(): seam = Seam.from_api_key("seam_apikey_token") assert DEFAULT_TIMEOUT == 30 - assert seam.client.timeout == 30 + assert seam.client.timeout == Timeout(30) def test_timeout_can_be_overridden(): seam = Seam.from_api_key("seam_apikey_token", timeout=60) - assert seam.client.timeout == 60 + assert seam.client.timeout == Timeout(60) def test_timeout_can_be_disabled_with_none(): seam = Seam.from_api_key("seam_apikey_token", timeout=None) - assert seam.client.timeout is None + assert seam.client.timeout == Timeout(None) -def test_niquests_options_are_passed_to_the_session(): +def test_httpx_options_are_passed_to_the_client(): seam = Seam.from_api_key( - "seam_apikey_token", niquests_options={"headers": {"Custom-Header": "Test"}} + "seam_apikey_token", httpx_options={"headers": {"Custom-Header": "Test"}} ) assert seam.client.headers["Custom-Header"] == "Test" @@ -40,10 +39,10 @@ def test_niquests_options_are_passed_to_the_session(): assert seam.client.headers["Authorization"] == "Bearer seam_apikey_token" -def test_niquests_options_take_precedence(): - seam = Seam.from_api_key("seam_apikey_token", niquests_options={"pool_maxsize": 25}) +def test_httpx_options_take_precedence(): + seam = Seam.from_api_key("seam_apikey_token", httpx_options={"timeout": 15}) - assert seam.client.timeout == 30 + assert seam.client.timeout == Timeout(15) def test_per_request_timeout_overrides_the_client_timeout(recording_server): @@ -64,7 +63,7 @@ def test_seam_times_out_a_slow_request(): retries=Retry(total=0), ) - with pytest.raises(niquests.exceptions.Timeout): + with pytest.raises(TimeoutException): seam.devices.list() diff --git a/uv.lock b/uv.lock index c93e5236..cbf0a220 100644 --- a/uv.lock +++ b/uv.lock @@ -100,169 +100,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, ] -[[package]] -name = "charset-normalizer" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/31/4971872b3ed8715346231fb6eb4da8fcba65a4143c189db151ee28a2812b/charset_normalizer-3.5.0.tar.gz", hash = "sha256:49bd5feb59b0bf3cbf6ebcf4352e371c95b9da9bacd4449f8b64d0ad2c10a26e", size = 169295, upload-time = "2026-08-12T14:35:31.624Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/8e/1e9a03657c2f12960c67b05e0550059787d4c8fc3fae822672f4fe7f40c5/charset_normalizer-3.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2478bd3b2ead3962a484fb802891be40d10049fb74f83e09cb4463fad023fea", size = 349795, upload-time = "2026-08-12T14:31:47.546Z" }, - { url = "https://files.pythonhosted.org/packages/76/e5/a42c8884ec0321c56124a5b43c135efe75260dad4f70ea0ffba4dcac85ec/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7cdded069549b5eae3d5d9bb6c2e5bb4fe83f9b81863e2a193cd747bf197aebb", size = 250677, upload-time = "2026-08-12T14:31:48.918Z" }, - { url = "https://files.pythonhosted.org/packages/5d/21/d94021b005d2e660fd13634eecfe4ad378b0d0bbb45134809aed42b94a99/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aff38231e3171c578b2c449a01afa44e9ff40844597a32873da102394f63d28e", size = 239868, upload-time = "2026-08-12T14:31:50.107Z" }, - { url = "https://files.pythonhosted.org/packages/47/d0/8b9b07e41501bfcdfe380b826481033ca2eb5fa4b5507eb64dbc4cba9885/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4346a693c08b1d0cfc0e3325bfb0ecd4322fb1a6904d68cf416f8da5e981b234", size = 280688, upload-time = "2026-08-12T14:31:51.182Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/b83b3469e596adff778bf3c8ca8f705822c1c3c0ada8477a0d00113bfa59/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b787efadba00f5da6fe89513bfbe3852d52ca3a448fdec165765cb3b44a80248", size = 277133, upload-time = "2026-08-12T14:31:52.362Z" }, - { url = "https://files.pythonhosted.org/packages/0c/aa/a080c4facaab4ece837558649eb6e2c760867d1c0ea52220cbbc4e89dcaa/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:143792a43e06dc3b27fc891948406e251502dc19ff9216cd80182b79131be5c5", size = 261492, upload-time = "2026-08-12T14:31:53.628Z" }, - { url = "https://files.pythonhosted.org/packages/f9/24/e23bcc9d4bc897bd620bc39c6b6052574f5ef93b85b50b175dd427a7dc07/charset_normalizer-3.5.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d74bcf1cdd8ac8267fb216473ce6b112efa07b163536288094541415084d131c", size = 259739, upload-time = "2026-08-12T14:31:54.86Z" }, - { url = "https://files.pythonhosted.org/packages/ef/85/f4c391c807f67c6483d02c02d28683e2fe229403728369c0cd33f61e4292/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbe543d957213fc9a3db4979a8e171b7aa7504c1d737029defdb03a6095a38", size = 252120, upload-time = "2026-08-12T14:31:56.227Z" }, - { url = "https://files.pythonhosted.org/packages/4e/cb/dcf5aa14e24270613fafa2f02b3979820c244610410e5508696ca96bd0d2/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3587d94b5c9f05c2dc4c3f3d47aba6375ff141a21adae3051d8d4d53e8a937c0", size = 241622, upload-time = "2026-08-12T14:31:57.54Z" }, - { url = "https://files.pythonhosted.org/packages/83/a9/decd1c1cc01ccbfce66845096e3945397e4ede354f31cc5cb28fc6ca601b/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7cb4cd266bd85613367fb85a30cfbf6fe6349919e87e18ca8dba584951bfb8a", size = 280745, upload-time = "2026-08-12T14:31:58.91Z" }, - { url = "https://files.pythonhosted.org/packages/8b/47/420ddefc04f089e4497b3bcbadea37586477713300c13204fb4a7c736071/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ffdd7ac514301d0a67f7c23b9f2b431ef909a3c3dd6c3766668d0a6f5900c94e", size = 258669, upload-time = "2026-08-12T14:32:00.069Z" }, - { url = "https://files.pythonhosted.org/packages/58/73/01d5173097a5f0a13417e77db9ba3aea24de54421344c8e0c4973bf8cf21/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3684ebbdffd51329ac44245d1d227d90b965797aa1a8abd026568a1f6ae88811", size = 277344, upload-time = "2026-08-12T14:32:01.211Z" }, - { url = "https://files.pythonhosted.org/packages/41/4a/300ef2598da520744aca7c49370e48732cab1f04de5f784f0e4f0e9e1d11/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b8788f114845c01f2b520e0b91ea58d143276cfc0483aa943e815f7b9555c15", size = 263440, upload-time = "2026-08-12T14:32:02.421Z" }, - { url = "https://files.pythonhosted.org/packages/be/5e/78dd366df9ae4810039c0f799ec47d325a5cb76e92bb707ca9039b7c2306/charset_normalizer-3.5.0-cp310-cp310-win32.whl", hash = "sha256:9a1d9b13e5e394e13e3c316f0d910d100b17681ff59797f30da1dba032061296", size = 181680, upload-time = "2026-08-12T14:32:03.932Z" }, - { url = "https://files.pythonhosted.org/packages/67/d6/455982837da317ce47cafe35260f446f218f5843090b77a59ee69aa0ad94/charset_normalizer-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a54587f93f2e289f8faf25b35c997d4cc75cf677485ac6f50c985715989f99c", size = 205665, upload-time = "2026-08-12T14:32:05.045Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e5/5b285d50d4fc82111c18ea4fff8b5e1916a257b98dcd5cd0abf1c165473b/charset_normalizer-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:38a395079f229a631dece74e24c69c1f612536dd51f345a7d6a98abe2d3e047a", size = 184409, upload-time = "2026-08-12T14:32:06.503Z" }, - { url = "https://files.pythonhosted.org/packages/50/42/71e4e3bfe59202feef062c68487f54c6adf501cfbe087ecd93e3cd597fea/charset_normalizer-3.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46a37ea7fcf9ae01d71b2e5ece19f1565987f3e308394b829197cbefc061f92", size = 349110, upload-time = "2026-08-12T14:32:07.832Z" }, - { url = "https://files.pythonhosted.org/packages/0f/dd/fd3386d0fbd358d3b5c7a2fa5bf312afe6159b04fafeb67d39fa971d7448/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cdfed4d7a59333c8220c67dd3be4e7a6c887b67453a64394022dcc919570add", size = 250773, upload-time = "2026-08-12T14:32:09.113Z" }, - { url = "https://files.pythonhosted.org/packages/17/ad/4901a66d6d3b17f1096725d7e50266132c16555aa6a70047fe1cf262b4b2/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9491f594859b68052edebd69e05fb045055a713b57a67974e6c1553b4e503c39", size = 240229, upload-time = "2026-08-12T14:32:10.43Z" }, - { url = "https://files.pythonhosted.org/packages/43/b7/1d790e0425e0f4c99e9b89a3956a94a6d2d0c6f01b2a5eca93d8f082d5ac/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:420b19411959eec115063229536788e6b32d0a7fa907d6b940317919120d702d", size = 280757, upload-time = "2026-08-12T14:32:11.628Z" }, - { url = "https://files.pythonhosted.org/packages/44/bb/4b8c8086c67636e52d6354ad17697f54a00b40041ca53dc765737e21709b/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a565303d118ea3b94a4b6c076bf568069726be414e43b06d58f7070b076ce11d", size = 276174, upload-time = "2026-08-12T14:32:12.808Z" }, - { url = "https://files.pythonhosted.org/packages/25/fa/690a11924c40c766d258d2b74d817cc5efe7bbcfceeedc5c0f35256d7524/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:815f143a91983ba3041bba066e492ae3c42de523fb1c699685a1abf3313b7d1b", size = 261808, upload-time = "2026-08-12T14:32:14.138Z" }, - { url = "https://files.pythonhosted.org/packages/53/2d/4a6945eff0c8f684e3f5b7b978644ab18ba9198da060dbaa1d9206bc6cc9/charset_normalizer-3.5.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c5e981a5ac8641381efe6f0029467500661616a530d27bc6eedfe45f840599f8", size = 259922, upload-time = "2026-08-12T14:32:15.485Z" }, - { url = "https://files.pythonhosted.org/packages/60/4d/10ac7e07bbf7ea569effeb9524e32f345f7e643800b20d538fb4706eab4e/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1a573e1e428f93908e79e04b349717f400e720f2f82285f0aaaf3ee0ff7f4c79", size = 252315, upload-time = "2026-08-12T14:32:16.764Z" }, - { url = "https://files.pythonhosted.org/packages/13/bf/1ddacf7aa7da12097f229a6a4e71a70200937a621b3617f6dc819fb99a66/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:32e6d56dd825205f81e5c45bcebb4df6a11fb2bbf4969a01ef156d6ced90c224", size = 240600, upload-time = "2026-08-12T14:32:17.956Z" }, - { url = "https://files.pythonhosted.org/packages/c2/88/293444d48c8d86fe51552262117134a9fc666d68cbbbc9b8c1b2b35a29be/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:30ae26a1adcd943690dcbbc47f28be762bae9e08ad7442b78c86b1c0dd5a626c", size = 280788, upload-time = "2026-08-12T14:32:19.1Z" }, - { url = "https://files.pythonhosted.org/packages/12/ed/0e34e40584f51eda38d4a5daf25fd8586366347efd4b2470dbf64710e778/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f51a19dc52197a20218b05ec5336d0c6b3b09935f838724722032c8d45dc91a", size = 258425, upload-time = "2026-08-12T14:32:20.207Z" }, - { url = "https://files.pythonhosted.org/packages/72/b2/c1b1c27f6f0ef35b21a8bdb592854bbf7f219629db3477f74c0b1380e0ed/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6e44bc2780516b3df986d6fe33103c7080cd9dcd5576fe3cb4b0f64309c8f22b", size = 277437, upload-time = "2026-08-12T14:32:21.345Z" }, - { url = "https://files.pythonhosted.org/packages/b2/bd/a1b7d959a37847675adb2f5978f81703306dd78df4fefb82ee5a1cc5e37f/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7faa47b56070b3dd6f4898ed28528843ab130d53266cb9948d9b1f3bb1a5c5e8", size = 262947, upload-time = "2026-08-12T14:32:22.613Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fd/a76a2d7e639bfc6a9c869371e34f28231eaca7d7126ec19895aa38eedb51/charset_normalizer-3.5.0-cp311-cp311-win32.whl", hash = "sha256:830c04a49998b5ed58c8b642c65b7b26419397f52392a64121ba9fd0e95e7f9f", size = 181313, upload-time = "2026-08-12T14:32:23.736Z" }, - { url = "https://files.pythonhosted.org/packages/97/84/6fc03e802578df41a2ab9b6a1f26657fb92e32285a603ad5852a4a4f68c1/charset_normalizer-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:8cb9b6892b53bd6d11fa4cde3dbee020b1f0b6656be1fbaa1ec0d4324a7839db", size = 206197, upload-time = "2026-08-12T14:32:24.938Z" }, - { url = "https://files.pythonhosted.org/packages/0a/27/7208360ff1901607359869fcc45ca0989d597f3e30777ba30c7254170587/charset_normalizer-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:2403b489c103e9a18c835863fc6dd54361355c8291d4cafdb37492b683440b9b", size = 184925, upload-time = "2026-08-12T14:32:26.123Z" }, - { url = "https://files.pythonhosted.org/packages/6d/3c/045ea64ea5a550870dd8ab60b2242870328d53f17d2be593b4f9f3121474/charset_normalizer-3.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:98820e1ceb25c6df7a80c4fd8efa59cb121f99bc7c4c1693ad94a2caff5b311d", size = 343861, upload-time = "2026-08-12T14:32:27.254Z" }, - { url = "https://files.pythonhosted.org/packages/fc/1b/7502be709db899d5b4801509829188b3a5a10969411da9c846115a5f1b70/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:608553f476fca509537e804c4a71f5eb166ce63b75141f89c2c686ce1aa36956", size = 237550, upload-time = "2026-08-12T14:32:28.385Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ce/66392661375148d9455c17bee25509a54e28c39969e34befa48ec8777936/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6753de11eef42f1c321b26d682957d92c7f7bbce6530f34bbe0f9291dd37cc6f", size = 229673, upload-time = "2026-08-12T14:32:29.668Z" }, - { url = "https://files.pythonhosted.org/packages/6a/64/f58c32a8d4ecf55b82ee61ee9aa6a664d4afcd36c72feb4c926fd6fe9af8/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f76dc0a47f94cb9b69d86f01e477f4b0371ca70208b9ccea7e063c41eed9046", size = 260768, upload-time = "2026-08-12T14:32:30.891Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ea/36c90e59a96386174377e855479ec154221ef001e96637e0b23be92489c4/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c387c6bf91b4774e359a48a179e2872b8e8bf741e4fde06ba8d1665eb9a4760a", size = 257880, upload-time = "2026-08-12T14:32:32.175Z" }, - { url = "https://files.pythonhosted.org/packages/23/35/5b85772eb82528ef22ba29487ad544a7049dfd27f35b1a5a55dbc0843048/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14f6904a3cf870abf044df3a8c4924ac6c8ef77e9896586fd37e73ae96cff2af", size = 247547, upload-time = "2026-08-12T14:32:33.495Z" }, - { url = "https://files.pythonhosted.org/packages/b6/14/ba11a99c2a22ab04c2d5383a700b378cb463a78ab15f36444cabc10cd671/charset_normalizer-3.5.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cce46dd29d73e135e8087b96eb62a4aca6d69391b7f97808c6588ebed3178f3", size = 243326, upload-time = "2026-08-12T14:32:34.794Z" }, - { url = "https://files.pythonhosted.org/packages/4c/4a/cadba3f2400b45aa1d62a4ae0298bf58a3b30b1158baf15c338c7ce5b601/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b476cdb63df22da2b91837593380be3ddbe406f36c506c1c91d80e7196b66288", size = 238820, upload-time = "2026-08-12T14:32:35.984Z" }, - { url = "https://files.pythonhosted.org/packages/47/41/d5188b9342d75b72c2b05d3ee373f01a691397e770f001ee05e3b37925f5/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1f56ce84b317ef2a59d7d3461891c7597c79247d2192bb8114c68a1a1debfcc0", size = 231661, upload-time = "2026-08-12T14:32:37.191Z" }, - { url = "https://files.pythonhosted.org/packages/13/5f/df38fa972c4e945c3d8cee2bc4e610613af522fd359c7dc7a74c419f0278/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9ce0f885239357379d92fd9a5fddbe20f0e30e0527c29ba69f8e99eeb1304a76", size = 261459, upload-time = "2026-08-12T14:32:38.369Z" }, - { url = "https://files.pythonhosted.org/packages/a5/d7/043ff7720067a3beee05523465ac9c1c846c68b7884930dd483f72ee5ab6/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:96ae7ab5d8155fde927aa0864fbc8ba3cc4fde6d41ab0c7cea9d6012b4978603", size = 242300, upload-time = "2026-08-12T14:32:39.505Z" }, - { url = "https://files.pythonhosted.org/packages/19/f6/33980b7b802a048e546a6d9ad2ea783a6cf6b10a86aaccd10db462d8b913/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bf91921009025e96ce57a03ced6d14604fc3baf0530351638e9504a55da6fa3b", size = 259101, upload-time = "2026-08-12T14:32:41.02Z" }, - { url = "https://files.pythonhosted.org/packages/a6/b7/0d19bde844bff9165377c1da9ef3c4792a4c24bd49b5b7094d9e6f6ab58b/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0b2e44e6d42d1a4ff78ccc219a93c5449105d10b16198d1aea581080df8073f9", size = 249246, upload-time = "2026-08-12T14:32:42.47Z" }, - { url = "https://files.pythonhosted.org/packages/1c/cc/2c34fdfaacdf0e96e880ef562cbf80a9b5f8ea97e0dd9e57ba348a9c65cf/charset_normalizer-3.5.0-cp312-cp312-win32.whl", hash = "sha256:deb99535e9bf0bea8e274c6413eb939a21be35a3f492678dba4d5b1f4d70f142", size = 178025, upload-time = "2026-08-12T14:32:43.909Z" }, - { url = "https://files.pythonhosted.org/packages/76/d0/c34dbd1df23bcbdc1b5d2f48256340d72fa747f1eb03924a9d2fa35ed85b/charset_normalizer-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:e54dd1a66fa4bce0ccaf0db9dde336e49b3eec646dc4c1c0991279369d373a14", size = 200143, upload-time = "2026-08-12T14:32:45.254Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ea/4e6bf1465d60c3d8f488d5bde140d0cb91cd23ccab0dc2895cc6c6982047/charset_normalizer-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:b8ea208b304587d47931b36481342d20336e0d338ab052f8b4305926482598d6", size = 180046, upload-time = "2026-08-12T14:32:46.545Z" }, - { url = "https://files.pythonhosted.org/packages/1d/be/cc7b7b6fc41984902c0d31b06f5d9297e67705c1dae9352608e5540fad09/charset_normalizer-3.5.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:5c23fa4f6eccdd601949cb00f3988c01d64e671d8faba356397971077022e144", size = 211050, upload-time = "2026-08-12T14:32:47.81Z" }, - { url = "https://files.pythonhosted.org/packages/d3/ae/e3ec8f17313609f43f7b323012fdb1ee37b83432277ca4eceba83e00366c/charset_normalizer-3.5.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:07f6f42b5a6325df35b458004fb5f9f29bf502d89287a33c7cdef3590e31de0f", size = 222768, upload-time = "2026-08-12T14:32:49.027Z" }, - { url = "https://files.pythonhosted.org/packages/c5/50/9f9c0d7ccc1512d49e27a0e7c12c58ec71dfe91698fa4326f058c33e1f1b/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8efc3f1563ed431882dd0dc0411b5f8ace1b1b89074981deaf6bd8af77dbe1bc", size = 193907, upload-time = "2026-08-12T14:32:50.414Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1d/cfe7b745ef7f4c3b7214581955b5a0869ba2ac551a58fc11036281ae167c/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:368eb2fc9482158b3a3386e8f01fa61f479c968e9a19ceab8f0188b86b312991", size = 197135, upload-time = "2026-08-12T14:32:51.605Z" }, - { url = "https://files.pythonhosted.org/packages/28/55/30fafdcfca9ba616bc394240545e4cd52f4f66dea43ded81b7d2d5274fde/charset_normalizer-3.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:826a295a039178479a325be1ae60eded1f0b10f7dda749df59e2440de8f61d64", size = 339892, upload-time = "2026-08-12T14:32:52.82Z" }, - { url = "https://files.pythonhosted.org/packages/f4/08/bdca5fc2bdc36ee443673dc7d12b23885a5a7b282bef85a1a4c3b325b40e/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70ff1c16eb0eb5ee6bb12739292347f981a5ba764cc4df1bc2e69b0405d4ac3b", size = 239439, upload-time = "2026-08-12T14:32:54.058Z" }, - { url = "https://files.pythonhosted.org/packages/d1/9e/506c8d7a7722bba7c8cdd78c1b5ef23bda92bfbe0b3e28ea84673d519a0f/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cfdab178a4add5483e26a9bb1c16d8018ccf39b4be7a3aea6c3979e6828f2ee", size = 227896, upload-time = "2026-08-12T14:32:55.326Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1a/dd828f2b1d6f4bf10821b9a74d866be05ffcdbfcddfc501d6fe6428762a7/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6083d10a846218502d664375b9448508d9fa580bd834567423156c6abfbe899d", size = 262548, upload-time = "2026-08-12T14:32:56.484Z" }, - { url = "https://files.pythonhosted.org/packages/be/81/196d26f6bd78b93e0d451b69082a71027ceeddd4b0be9170b81bb038f824/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f211c21aa316cb6e2662e54a1194633a79d98a50a876addacfce7ba5b34b09f", size = 259986, upload-time = "2026-08-12T14:32:57.661Z" }, - { url = "https://files.pythonhosted.org/packages/3a/6a/5b964a1eb0f9075ecd45083eeb21aaec215334f98bac3d400302ea73875d/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b08ebf9488c7ff5eff038e48e6ea938178dfd9dcc8598b5ca941e4ae27b20be", size = 249853, upload-time = "2026-08-12T14:32:59.123Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b4/aee3a9d82edd0e931091ef3e9f03e46491ae3590e96e998d0975dadbe17c/charset_normalizer-3.5.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95450fce59f00c6d08eff6572ec2e736e5054c9450253afd5748f8416f2eb9", size = 244217, upload-time = "2026-08-12T14:33:00.341Z" }, - { url = "https://files.pythonhosted.org/packages/22/3e/33f72ca11c1b619b220fd9f35905ebd171cbd0e0470f2357e467b9e861ee/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5780a29823e1d2bec69b7a104ead4195a43f3e97782efaedbf1f79a0157af715", size = 241307, upload-time = "2026-08-12T14:33:01.589Z" }, - { url = "https://files.pythonhosted.org/packages/78/27/6029dccba958621c7f3a65136f87c5512d712aef9e890f09512cc171bd03/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:054420b5db984971d886e5e4e2c37c760ae6682aedbd066687ff0949d9ed5f08", size = 233305, upload-time = "2026-08-12T14:33:02.866Z" }, - { url = "https://files.pythonhosted.org/packages/18/d7/691c967be459153fe9faf49bf78bc95639ef8bf6dd008f38cc6389a349eb/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d016dc857136c726958102c3b8a3986acdc65ace6fbf12cfdc09cc4bfa2935b2", size = 263465, upload-time = "2026-08-12T14:33:04.166Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2d/9202221be5c90b2a835924191e362690ed8dc8c7d6606100c2bd03fe0f8c/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f2ce3d39fb4a9d674e6639dd5d3146b2e273475d2260f10163228d66fc04433d", size = 245060, upload-time = "2026-08-12T14:33:05.325Z" }, - { url = "https://files.pythonhosted.org/packages/b3/9a/298772fd0a0cbccadf36451a1cd7eef4b66a11e99b4a7f6fafc47cc62c75/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a5613a3a82c974227bde18f03409e30c467f8065cb56d822e3eb83708a5f223d", size = 261091, upload-time = "2026-08-12T14:33:06.475Z" }, - { url = "https://files.pythonhosted.org/packages/82/3b/1a11fe66e555dbe2f5714ade6ba74fa29edc9155d9cf1001d4d6ed096aa7/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fded2e82ff082e5d8e017e2ddcc1411bd8cb83b8585097fc401ef574f756b888", size = 251857, upload-time = "2026-08-12T14:33:07.784Z" }, - { url = "https://files.pythonhosted.org/packages/17/fc/73b817e8af3f1d25ec5cf458d405abba5a144cf9812238a61530f5eac186/charset_normalizer-3.5.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:8f006866047c6ec4b627ec144b1e0bbc7427cb31fd7c08d19897d0ac9032af3d", size = 139745, upload-time = "2026-08-12T14:33:09.123Z" }, - { url = "https://files.pythonhosted.org/packages/b3/fb/ddb66303c86f7dc5043a457dad9fa82b4d6d0cb97094f9bcdde21693fd58/charset_normalizer-3.5.0-cp313-cp313-win32.whl", hash = "sha256:196e270c4e80827b5072eed7d6aa661d133afada94fe366669f9609e718d305e", size = 177217, upload-time = "2026-08-12T14:33:10.305Z" }, - { url = "https://files.pythonhosted.org/packages/fb/88/6018cc8d76ea2b7cb02918f37e23e86c261d1a102713d7e88d2cfb8b211c/charset_normalizer-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:72982d9958a42f8132bf2d6b90214ed66477295ef1188731f98ae3511c6eeb5a", size = 198896, upload-time = "2026-08-12T14:33:11.51Z" }, - { url = "https://files.pythonhosted.org/packages/20/2e/04c0bbfc8d9abf91959f7a3d207d45cbf63a8116984caae2381890019bb5/charset_normalizer-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:0b373bab0b867b68b8eb249da9478cab9181a42993437cd2f5dba5fb0b4fbd1b", size = 179193, upload-time = "2026-08-12T14:33:12.726Z" }, - { url = "https://files.pythonhosted.org/packages/43/14/d098868dac5ff27e0258f548b1c74c6484be528384965d8fcf8fc6a4011d/charset_normalizer-3.5.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d95244906ed69d0f79f190893c65e336c15959003e21449256dc05c001b52ea2", size = 211664, upload-time = "2026-08-12T14:33:14.153Z" }, - { url = "https://files.pythonhosted.org/packages/e7/da/a944b32a46601ae5a4c3499e8d64ecd14fe82313f00da74dcdf00273a0b4/charset_normalizer-3.5.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d788e2ded0c4c47efa4d73cfe59eaf975ee32f425219873d2cb3e3fbaa00f636", size = 224375, upload-time = "2026-08-12T14:33:15.472Z" }, - { url = "https://files.pythonhosted.org/packages/f7/db/eabb5996be2f529744755e7b2fc9396eff4a64961f034e7fd49d54b9afb2/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:f9f91d3e8382900f3a68fa0ce94294479de9cd2de6bc0c70acd0f0dfd511836b", size = 194364, upload-time = "2026-08-12T14:33:16.607Z" }, - { url = "https://files.pythonhosted.org/packages/78/65/4ad3c5be108930310d8003f5602861d5b89f728293b9f09c3a4837f7ba10/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d54625cbf4e6b60bf0639728cb8b4cb541e340f6d7cafae5806051a40ddf4c45", size = 197643, upload-time = "2026-08-12T14:33:17.88Z" }, - { url = "https://files.pythonhosted.org/packages/3d/39/8fee3201b98d52289be60a775797d69be05a04fb6cfb48c1587dad33e649/charset_normalizer-3.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:1f99a8c3a1da5d955edbad18208b3d627bdd54c48a6e739fa877bdca98c686d6", size = 341384, upload-time = "2026-08-12T14:33:19.239Z" }, - { url = "https://files.pythonhosted.org/packages/a7/dd/9e757101d1f76c35c0643684ba499ac3a181fb2b264c68174bf727d627e8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf1e75dc07a3850b53d1e5f75e04d3ae12afe56284be7821771eaa2466350c73", size = 241637, upload-time = "2026-08-12T14:33:20.619Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e4/7857023015400bc4aa0a82fbcca29fa2dc7ec25f971a130764cb2dc7a589/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ac5a9cc079c67d75f4ddf343276031879eadbb333d1bb231cce297b8d7b9aae8", size = 226170, upload-time = "2026-08-12T14:33:21.773Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ae/8b52935b304f7b6bbf33151ed2b75266b09aa4b6f8f04230d948885b2577/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0dfe83c1b4d00abbf433998117a14f56a5c2bc68226c0d331709eed0d1ce539b", size = 265093, upload-time = "2026-08-12T14:33:22.999Z" }, - { url = "https://files.pythonhosted.org/packages/dc/78/6e838f6bb059f2c0afc60a4e7f294252f043c254656ad4114c50302cae4d/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e8fa586df2208ef040684751345f10f503834a757c9a74ecd19c1a2f9b1ccd", size = 262789, upload-time = "2026-08-12T14:33:24.214Z" }, - { url = "https://files.pythonhosted.org/packages/c2/08/189b27e51fddc9d6b3695331da0e31792c1d88b953ad854e57f06e9b2cc8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82cc5835997ec78afe293a192e385099355770a7db94b2fb1239d36b32796f1c", size = 250580, upload-time = "2026-08-12T14:33:25.707Z" }, - { url = "https://files.pythonhosted.org/packages/ac/55/64854e99b25841f83e8e37d9df2f3d1f96f693439f80e5fabd542a7e47ab/charset_normalizer-3.5.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:19e52bda45086df8a4be4bb5910af6f5d9d3b538c78712c8ae09ef10b85bf458", size = 245008, upload-time = "2026-08-12T14:33:26.971Z" }, - { url = "https://files.pythonhosted.org/packages/4f/01/7720c904fa635d4260b4dced6029cf3d298c57b26741365d5a8d28c54043/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3418edd0ecb72a0a3861cf72f31be0ad9b7fe338ce2b58fb5cc80b9aeb792700", size = 243892, upload-time = "2026-08-12T14:33:28.237Z" }, - { url = "https://files.pythonhosted.org/packages/70/50/7bfcb327631d4870c720872b548745f6ec8baa044d51c21b5d1d32ac4e3a/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:125ee619611019471b177c70bc3e9d4cda9fad7e01d93523501d3b188df0193a", size = 230996, upload-time = "2026-08-12T14:33:29.511Z" }, - { url = "https://files.pythonhosted.org/packages/24/51/40c45d6d940c04005ed721aa54bdebf1ebb2930f8a2ae537e8d60484fb27/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a3ad0e3da22852533858663848608f3f24c0d35e5cde415a4903476f2b4c88ec", size = 265834, upload-time = "2026-08-12T14:33:30.689Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d4/ef7a227ef89d215b47f9df79c3966610b17faa13bb2f236989207a631622/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:4ebebb410bc517e1d284c52a123e82704b21e4e7e26a21ebecf7439d0647b8a3", size = 245544, upload-time = "2026-08-12T14:33:31.859Z" }, - { url = "https://files.pythonhosted.org/packages/37/a9/a4ca9156964ded61c7718eba410ce11be2fd2b263fda4bcf08367b6578cd/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:91f9f7c151e772acebe489eaec96e96a2877202d7dd144e3f96b8676881715a0", size = 264110, upload-time = "2026-08-12T14:33:33.13Z" }, - { url = "https://files.pythonhosted.org/packages/38/6a/838364bb8702229c6e5f8b23f80ff0f052a12dfaf3113a12fd6acbe92a44/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:401ea6e7af9e7852ed818f64714b579c1935482049670847ca3bd7ba45dc63fb", size = 252303, upload-time = "2026-08-12T14:33:34.98Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5f/d88032edce951f499a2321cf7ae0d35a043c74be12bc22d81084cc7afbcc/charset_normalizer-3.5.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f7496aed56b06325a1ad419c5bf23c6dd042558e874f71dd1b958f3e255f3053", size = 139964, upload-time = "2026-08-12T14:33:36.195Z" }, - { url = "https://files.pythonhosted.org/packages/37/ae/1c4a46b6b00d1c34d2ee355ef99ad6173674166800d1af0f05f85028d513/charset_normalizer-3.5.0-cp314-cp314-win32.whl", hash = "sha256:606a86c1c3196f3738de39a67a7490bbd61cb31c0e0436070bd0c6a48170b38e", size = 179790, upload-time = "2026-08-12T14:33:37.356Z" }, - { url = "https://files.pythonhosted.org/packages/01/51/f94dcf34fa8eba48c1fb89b6490a5f1426e19488fe5f38aac6c648c99057/charset_normalizer-3.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:ec6c464cf45867f66a2273e2214d9199a8fbad5cb95ca0fd45f6a2fe1d9d2cf4", size = 203723, upload-time = "2026-08-12T14:33:38.639Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ba/91d386870b5d9e4b0d8c4034f63877cc2e47b99c81ef05f3e6d42bf9a53f/charset_normalizer-3.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:dc28949de1bb5f7f30a46f15d74ce7ac5aaa63e03c5de04d68f571c7423af834", size = 183423, upload-time = "2026-08-12T14:33:39.899Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c9/534ecb17b7fb95f9052c4a44cf316316a27d4a8f73e8475ff55e778dcdd7/charset_normalizer-3.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:68b7e84ae8239a94f8d2c8f3f3a3a81bcde54805ec8f42a34de927d155688ec6", size = 368967, upload-time = "2026-08-12T14:33:41.093Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/ad7c3242d7fe55cd55126c22c65cb1b49779782cdf8932fd01d12232d86a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58ca5dc0a0ef99f2801ec0574214c978e9574055bc783830bbb6e7433218609f", size = 239478, upload-time = "2026-08-12T14:33:42.428Z" }, - { url = "https://files.pythonhosted.org/packages/7e/62/77f0b850048e430fc350ec58876b0c020f5c8d0d3956fd1a4d6ae2fa292f/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6c57af4084c10cb3286688d65e4c654190ff5edcbc2411d08cdca0a8a44c59a1", size = 227036, upload-time = "2026-08-12T14:33:43.635Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ba/47d951e1a51dddbaad0a1410baf49fb1d897ceb00281568f1183b79bce9a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1619a3cc174a7e3963dd34348e6fceb6e50db0ddeb0031bd7c73a58286454fa", size = 260772, upload-time = "2026-08-12T14:33:44.96Z" }, - { url = "https://files.pythonhosted.org/packages/b0/61/8c7ff4c81b2a88271126acf4b83ab3e31f6d63868b0f01d331eaa0f9cb67/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1328cc57dd4372be1265f68232cee890e087416e3e6e93e6ffb32c2bad4d36a4", size = 259273, upload-time = "2026-08-12T14:33:46.185Z" }, - { url = "https://files.pythonhosted.org/packages/e1/fd/36129689be08dc287b951306946657ff70d76e287dd57018861f86d0e474/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06f4fb62a9139bef056b8b2da6773c94c2f259f90e4b8e53b166f3d0372d7cf6", size = 248086, upload-time = "2026-08-12T14:33:47.54Z" }, - { url = "https://files.pythonhosted.org/packages/cf/f8/bcae67f994c8fd31dda445e5ebf84045823c31443fe46f0e9ee6aca99aa0/charset_normalizer-3.5.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:478650a70a750d75d5add401606c77f77069c32e4ba2c9131dc6cee566962ca0", size = 242671, upload-time = "2026-08-12T14:33:48.746Z" }, - { url = "https://files.pythonhosted.org/packages/61/92/0472cdad1061c2f0e4d3aee29973eb6e81bb8fe256ff2860cf115b15f1c9/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48920bf6fe83eb2226756ac623fa54940487154eb18f80889d5735cf234965c0", size = 241311, upload-time = "2026-08-12T14:33:50.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/89/04a03de5d27c77c624d9fcf6287073754bd438df1b58cb7d030c57c2824d/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:168a0cb536b5123a77bc42ecf5e0bf6f923d0d9ae43c42a14eb0677c19ac6c19", size = 229898, upload-time = "2026-08-12T14:33:51.523Z" }, - { url = "https://files.pythonhosted.org/packages/42/a2/639c4278adcb7ed1f4db608dd9ac19b6774fa2285a96b1c0bdb9c124ccbd/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f278e131afa96a3622cef9211c406ea2ad1b68eb06f8837cd443684a40e0ae50", size = 262852, upload-time = "2026-08-12T14:33:52.924Z" }, - { url = "https://files.pythonhosted.org/packages/9d/95/02e34c97bedfd0c5574efb9179c850591acc7f967ba039ed8dd29d332b73/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d6100f877d2ed95f0856a3fde25334153add94bf2224c43f45f88e7039262aaa", size = 242913, upload-time = "2026-08-12T14:33:54.18Z" }, - { url = "https://files.pythonhosted.org/packages/a3/64/0946aeab6462dad9f160a50dfb4704d3f58a5ee708f085abc2105fbbff0c/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4c440122e1ea68b1f8b44a631ebf49c39180f6869b1da22d76e8a724208ec6e9", size = 257938, upload-time = "2026-08-12T14:33:55.802Z" }, - { url = "https://files.pythonhosted.org/packages/79/77/36787d41ead124746506a4425c729f4f17c68280af8a6a5baa0a598cae86/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e5f834965c2fe589837bac1002e07e25734ff70381903ccd95b3d649e22bfa40", size = 249467, upload-time = "2026-08-12T14:33:57.081Z" }, - { url = "https://files.pythonhosted.org/packages/65/10/d9f6c5589cd24198d4ce6cd2948191c18e657272f433e5a00d258d9f5c22/charset_normalizer-3.5.0-cp314-cp314t-win32.whl", hash = "sha256:076cf9d3f3c7e410295c09d96355cf3b1bcae74990034d80e4371e20fe1ba4c6", size = 190624, upload-time = "2026-08-12T14:33:58.449Z" }, - { url = "https://files.pythonhosted.org/packages/6c/81/43e0584a802051a22c725795ebe1df78263abc7de858eef6cdc9b36637e9/charset_normalizer-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3288a560dc3114d5d2ebe309b1ef43f8af355eafe25856832415c2a8196c9db3", size = 215902, upload-time = "2026-08-12T14:33:59.753Z" }, - { url = "https://files.pythonhosted.org/packages/30/f3/af6a1160fef0eac4510d035241e11eccf78e5350e4cd4de79e79fe02a5e5/charset_normalizer-3.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a284c36b9c6616bf0a8aa4aabba668a0c75ba65ccf40a79868aeaa69ad996897", size = 193452, upload-time = "2026-08-12T14:34:01.017Z" }, - { url = "https://files.pythonhosted.org/packages/42/a4/dee470afb7a55c4f78b6fef37306c51fed17ebf94dbe530798c91d394350/charset_normalizer-3.5.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:c38d1e9bc2073b0984d2099ea647fd7f6c0d8f83a1e14e0cd32926f16e4c44ce", size = 341595, upload-time = "2026-08-12T14:34:02.4Z" }, - { url = "https://files.pythonhosted.org/packages/6a/32/9c3126dc429c6d9d7f79c52681a7c4453ed20a26267c9a8275d7ab620aba/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9f45186390aee4d1f26f723c615b67df346766c3b16df000d84d6e374f06757", size = 242177, upload-time = "2026-08-12T14:34:03.741Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9d/5b616a887301ff4cc0916b39ba44257390d3da80deeed6e8b6f2f26b14a8/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f0fde5e5100c735b2274ab898f0742a5dcde492796296cfbe7e0ad6a4cd1a396", size = 236730, upload-time = "2026-08-12T14:34:04.991Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/d6d3e70be93ebe5fabef65e4c7ac113e1d1705cbaeb5fb72467e713aca17/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d00e18e7bbf47e332ab63903d18bae31efc701b1d8cca0382b97784a621fc44", size = 265158, upload-time = "2026-08-12T14:34:06.235Z" }, - { url = "https://files.pythonhosted.org/packages/30/e7/3f1fafa87e2643257474f9c4eec609f2193a61d907dce7dd4f3f2390ebd5/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b81980668800dd1c69faad8aea6e85a8cee0e13bcd3bba7671695ff16260293", size = 262931, upload-time = "2026-08-12T14:34:07.511Z" }, - { url = "https://files.pythonhosted.org/packages/0a/df/ebeb224a949d91829e5e114c6b64372a3c792b00762a9e951ce416f3a32d/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb3e0d1345b9c0fe73673ea656375f38a78ec679c2edeae0c24800f04798a85", size = 251388, upload-time = "2026-08-12T14:34:08.949Z" }, - { url = "https://files.pythonhosted.org/packages/a0/64/9a6ce2e7acc5cf1b4636f78f82e89ff581e06a0216a40678b28bd4d832c4/charset_normalizer-3.5.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2401f7671242e921e604f609d429f6b282ea4ca787a6ffd22ed7372011ddb9d1", size = 251821, upload-time = "2026-08-12T14:34:10.138Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b1/6e69b8056f615e5ccff6b91ca16db2d47922251f016821a300c115267fef/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:96720f2aeed3434bc48f4d52fbad64ecc820cfed88915d664780ed9ba09ede78", size = 244507, upload-time = "2026-08-12T14:34:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/0f/34/02c15d6a0aa6b934dcdc136b111da63ae857b9fd51cf5505b0736337c2eb/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:c455829625df983f716cbaecbba77f2d1dc2e0e0ed1638c059cece15a279344b", size = 240951, upload-time = "2026-08-12T14:34:12.991Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/0fe893d3e1c7d111280bd6c4bd4c1e431487a1124a1bcbce78dfeda3a3a8/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:c41b067eddcfa5ee6b1169c287605be7fb6b0ea22bba6474c5bb978a668def4f", size = 266162, upload-time = "2026-08-12T14:34:14.232Z" }, - { url = "https://files.pythonhosted.org/packages/55/ea/eca03527307670f5d102c295671a800c404ca958cf94fefd10fc963a72f0/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:e31786a947b136329bfdc458c82c06d4ec539b4a4436b7da4df4aafc9902ee80", size = 251835, upload-time = "2026-08-12T14:34:15.48Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/fee5633081e595fe9e191df6f215106c791ad596eddf5e41e39b8ea0f2e2/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:c5c6d47a865147e0ae3322ce92e7fb52ba3169d94b447deda56897ea2aa6fac9", size = 264314, upload-time = "2026-08-12T14:34:16.679Z" }, - { url = "https://files.pythonhosted.org/packages/cf/fb/17f47ae6ca35b562fb6e6f4b05f7aec6034217353eb4a23aaa3566dc7340/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c75191e3c8052045179646cb40e280800a4e0bdfda34d9c949c2f268d44e80e4", size = 253194, upload-time = "2026-08-12T14:34:17.975Z" }, - { url = "https://files.pythonhosted.org/packages/59/88/f2b0f7ebb92493e925889ff29239b3b0073ffafd91230dbfc69e5cf9389c/charset_normalizer-3.5.0-cp315-cp315-win32.whl", hash = "sha256:83b62410bd36bb1178a7d563e2ee0cf21eb1c980c912ab99c2c78f06227f1731", size = 179800, upload-time = "2026-08-12T14:34:19.409Z" }, - { url = "https://files.pythonhosted.org/packages/e2/f0/afb5bfdea52fd943b1960403847a276b8e900c6e4cd6a38752321b4eda64/charset_normalizer-3.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:e3b9eaa99a6d8c9ace4cd303915947ef55088d4cd87c6676874f98c5c03aa040", size = 203726, upload-time = "2026-08-12T14:34:20.656Z" }, - { url = "https://files.pythonhosted.org/packages/fc/71/219783eb691aa2ec879c0e521afdfe2b826f9678eed51b9c039d03e0db2b/charset_normalizer-3.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:fec352b793cdc183cc9e7e0b6c10fd7bff38ec54ba44cc43599b9b56f7f3db2e", size = 183428, upload-time = "2026-08-12T14:34:21.975Z" }, - { url = "https://files.pythonhosted.org/packages/d1/d0/14aef3b9f80f2593c039d897e89034635b9eb0eb44b6ce5173bbd79ff338/charset_normalizer-3.5.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:c9bde7a960720c8b8e1b5ef7afaa0c9a2f3b55c44abd635b2b29dd066b298e3a", size = 368728, upload-time = "2026-08-12T14:34:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/10/fc/b249466ddbbeffa448b6597631e9091d1f01b5132ff8e7a0e21a6eb72b63/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8cd1283a9fe6c2065c807e9d5da81afe5e1e004caef39adc0d8ae86dd883698", size = 240925, upload-time = "2026-08-12T14:34:24.504Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b9/c17e72aaa1b3e1ca6c184e8025cf138ed492d01a54f85286ff7d31253a4b/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1c010dd86d3f4c4433c9634d33ce8147393b270dfa54f217f965540b8ae8e075", size = 234932, upload-time = "2026-08-12T14:34:25.822Z" }, - { url = "https://files.pythonhosted.org/packages/18/d7/f84ef0966bbe216f71029e34e7fa425a16b1682e2a40265e679dedf2b655/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5e68229977b2dea28e7061c0c0630a23f2f9f6e9c6fb38d77d3d6dbfe3768b74", size = 261733, upload-time = "2026-08-12T14:34:27.112Z" }, - { url = "https://files.pythonhosted.org/packages/3b/73/3e887fa0781a395339355ed934ab6561ceb5bb52574160f070224039c630/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a60773eb5fda796e6e6f76b9c152d270fe59f9788a51a6ff8ba44082d8548ae4", size = 258460, upload-time = "2026-08-12T14:34:28.431Z" }, - { url = "https://files.pythonhosted.org/packages/b3/81/52ebd9849bf9e35d0b21fff115cb6543162a8e1f2f564e8f87121a336b8c/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9419f44e568f7fafcdc0b3b5c766a2364e705a9b34fb8a56b431e0d1f3f4258", size = 249894, upload-time = "2026-08-12T14:34:29.698Z" }, - { url = "https://files.pythonhosted.org/packages/85/f3/9366492b8a5fe0187de282e001d61345740cf79eb4a5f20181d769be02b5/charset_normalizer-3.5.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75e243abbb528c1a774390ed71e3f868a9f37b1373442e4bbadd401cfc505ff4", size = 249540, upload-time = "2026-08-12T14:34:30.96Z" }, - { url = "https://files.pythonhosted.org/packages/0b/af/28bb5e5dbd3e67cb9196a62781ac2b6d79492f4fc7a069b6ca7d6d6c8d58/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:54c963ce6404e52255b737e8a06d356fc762d59096ae566203a67cf2b7d050f2", size = 242734, upload-time = "2026-08-12T14:34:32.481Z" }, - { url = "https://files.pythonhosted.org/packages/26/d6/7ccfa62b53b40fc06b2d3504825aa400764740bd10cf248fdc4272441b93/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:63ea0cc840c66670183578c2630d138c0e944aeadfc33f25173ee240f5db780d", size = 239580, upload-time = "2026-08-12T14:34:33.916Z" }, - { url = "https://files.pythonhosted.org/packages/0b/82/71c0c9b046697b8da66b3acefa8d5f92d00a9ef433ad7c3522b971d0369a/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6c06875a1d4a7537bef70f659b55c6b55b9a47ec3ba8f2db610350c2d9915e6e", size = 263281, upload-time = "2026-08-12T14:34:35.333Z" }, - { url = "https://files.pythonhosted.org/packages/d6/01/d027583c869f40ba980c1c76994adbd522c360a6327e72beb44d7c267385/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:4253da1b4456b633651a8d59eb1dc7a8a8fa38241014dd7c217b353e547ae394", size = 250027, upload-time = "2026-08-12T14:34:36.991Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e9/6475d739e0ec8bb1236e06263dc3affaffdf947d8114ad27024932f325da/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:f044cb1cf44012184715f46584658993b5fee9344d71c4b0c455a17a299730c0", size = 257547, upload-time = "2026-08-12T14:34:38.277Z" }, - { url = "https://files.pythonhosted.org/packages/a5/60/d1f502fcaa048a2aca3ab80bfef8407659c131e4f1792fa805fec14b4960/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a17864853f7c518ae7d4b368af98f427f9396805476af40af8698560f09d7d97", size = 251718, upload-time = "2026-08-12T14:34:39.562Z" }, - { url = "https://files.pythonhosted.org/packages/c3/69/76343dcf4381a698807ff8a20d89f66bbdd9f6222b0b17740f77ab764335/charset_normalizer-3.5.0-cp315-cp315t-win32.whl", hash = "sha256:9e726478d7a213847860219d74665a6892a643ac93b8f76580f6cf9ed39996b7", size = 190757, upload-time = "2026-08-12T14:34:41.053Z" }, - { url = "https://files.pythonhosted.org/packages/e1/ea/d18147626a1667cc773c42104ab155a4ca5d6d4d174b7a35e01062213ea5/charset_normalizer-3.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:d7229a99120c6c2792d96f4857c2648ce5530e93667a2c2388c5ef69a6b84775", size = 215431, upload-time = "2026-08-12T14:34:42.501Z" }, - { url = "https://files.pythonhosted.org/packages/47/21/4869598aae0872d94faa5933918a4fe37ab2c5af9d095786e241f9506fed/charset_normalizer-3.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:527e28a5e751d9e11369b9c5f9ab35c748eb9c109101920c7deb40d6eadf8d03", size = 193205, upload-time = "2026-08-12T14:34:43.781Z" }, - { url = "https://files.pythonhosted.org/packages/5b/f3/7b523d807cb5e73562ef8acf21d39cdb9d704955327362c781bc3478a73d/charset_normalizer-3.5.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:5a4ee37248dfac25107c758bda99d545ce73e60b44d2dd39e4a2bb9f2831e9f5", size = 330840, upload-time = "2026-08-12T14:34:45.06Z" }, - { url = "https://files.pythonhosted.org/packages/f0/de/fc68978fe78ca97063c96d764e41ff92ca639948f319271e0ff450e577a2/charset_normalizer-3.5.0-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a864bdcacd8bff58bb4845304e031f821a3ec64b2b7259f2d409cd49c9e59ca3", size = 251862, upload-time = "2026-08-12T14:34:46.58Z" }, - { url = "https://files.pythonhosted.org/packages/a9/cb/82b41a0ab7fb1a88065f1d78ad32696ad88ea3fe8e25b8189d08833938de/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:84b736e3b391601bc47b86da381c749c0f894e9191aaca9f31f30c2632206df3", size = 239484, upload-time = "2026-08-12T14:34:47.869Z" }, - { url = "https://files.pythonhosted.org/packages/e8/0c/19608b631f4538f908098d4a2d56a8f79a665e27cc58e9d90479761a9227/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6abb1f356fb865baeb6ebc3fadd843e9a96fbf49b9adcca55037f3cceccb7438", size = 230602, upload-time = "2026-08-12T14:34:49.265Z" }, - { url = "https://files.pythonhosted.org/packages/29/db/f648eb30e14eba301aed61e11672156f137905c1bdbb530151abe8065943/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d366548d2ee28a8cfdcc4296363978cc644a728333be9824d2de4652e83df0a", size = 259208, upload-time = "2026-08-12T14:34:50.632Z" }, - { url = "https://files.pythonhosted.org/packages/d3/e0/ed2c8bdbac484d69614d6993143aeb6cb0f4dd1561c883402517b623c8ef/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d672f329ae504ee240eb39b6effb3318aa8e7e8924c0ce8eee5760b3fad98539", size = 253659, upload-time = "2026-08-12T14:34:52.11Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/b4907cb9ec5b521d9d024ced13611240b86ef065c2eb15b3ad2334dc9940/charset_normalizer-3.5.0-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c54036a518748b6c02e666f6d46c3817561998fb904c3be25b56fb4fe3dc5706", size = 248821, upload-time = "2026-08-12T14:34:53.399Z" }, - { url = "https://files.pythonhosted.org/packages/12/b2/e2d1abcfbc05822f0030869efb4e9f8a3658e13b4821796d4b62da917327/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:22a1889f1c9b752c63c36758a0c2145458e3cadb20fced7a0790002e9dd12b26", size = 240271, upload-time = "2026-08-12T14:34:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/dc/f9/4ba127ad610542fa3eabfa41c45bf12d357860a815b3566374ec0188e213/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b7eb3eab5c646d3de7dcb14a7c9caebace5249c5767da39e1761cb1576e521a3", size = 232155, upload-time = "2026-08-12T14:34:56.543Z" }, - { url = "https://files.pythonhosted.org/packages/01/68/40613182366d00bd6dbd5f6c84a926cbd120960e038a8269e9ae7d782762/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:2080aa129a28267984cdc902898993d788c995c384e285d0d19199f56760d52e", size = 259674, upload-time = "2026-08-12T14:34:57.815Z" }, - { url = "https://files.pythonhosted.org/packages/0a/53/4574a14fa4c9de4a6c9f31725354bfa40b67f653e6d594ce1654f9a41b32/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:fd68c825548a611158230e2f9222e210ceb2e3391995c0aa5865cbdf3ab4bd49", size = 246122, upload-time = "2026-08-12T14:34:59.337Z" }, - { url = "https://files.pythonhosted.org/packages/5a/02/bd8030d13d92c058ca7b2b9615bbb3169569e144db64d65c149cd45abf5e/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:d8a9316f4da85e937242642b537c6d55d7e9287dd38e5634732f8233932aff45", size = 255221, upload-time = "2026-08-12T14:35:00.71Z" }, - { url = "https://files.pythonhosted.org/packages/77/9d/10ecd3bcbe2666b3d4d4026c97b48f73990682815db516052a1e8f4a31c5/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d90254c8f609338c53ec180fcd4c4f9c16502e238e3fc88ca7fd4c2f38d445b8", size = 253450, upload-time = "2026-08-12T14:35:02.217Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/d044c4872c0938a84f87b5027a698c0e61bacfc5c3551a4e749ca9b7bc5c/charset_normalizer-3.5.0-cp37-abi3-win32.whl", hash = "sha256:8b3e9e29b8b07cc461b9ce7768db7693a93979d0dadf22046f6f3555ded2f516", size = 173594, upload-time = "2026-08-12T14:35:03.845Z" }, - { url = "https://files.pythonhosted.org/packages/10/6b/6046773901f1944b9a89436351529811ee958afc7b774563be9d74a6f0c3/charset_normalizer-3.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:0c8953d9d1617794cfc40d81179571c9ba3805dd029623a15c93f1fb70e60a74", size = 198959, upload-time = "2026-08-12T14:35:05.187Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a6/b57708ac92aefc8e8389d51d5178129b81f03196da61ee2c23e687b8178a/charset_normalizer-3.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:562d24ca7797c1af8852994950c2e623a907b201fc4b0ed29e92af173d3828ca", size = 267055, upload-time = "2026-08-12T14:35:06.533Z" }, - { url = "https://files.pythonhosted.org/packages/22/c7/754d09943a616937df61e4ba367c409ded2a987e872972098d51a6fcf73b/charset_normalizer-3.5.0-py3-none-any.whl", hash = "sha256:993dfcbe75a85a3784abb5084f2c41b915767c90546fcc92803cffa28611baea", size = 67943, upload-time = "2026-08-12T14:35:30.363Z" }, -] - [[package]] name = "click" version = "8.4.2" @@ -417,19 +254,6 @@ toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] -[[package]] -name = "dataclasses-json" -version = "0.6.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "marshmallow" }, - { name = "typing-inspect" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227, upload-time = "2024-06-09T16:20:19.103Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, -] - [[package]] name = "deprecated" version = "1.3.1" @@ -515,6 +339,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "httpx-retries" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/d3/b7a8bb09543af40009717a08a2ceba90b6d4c6f0cdf171404217d8f4c37d/httpx_retries-0.6.0.tar.gz", hash = "sha256:3e0b404969a564829d368417964fd21e6b400a10d17c92d29b8bc247ce8186e3", size = 21122, upload-time = "2026-07-06T00:52:30.093Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c6/7f3d6ab3549267a1959161b38df4c0fb435eceaf2d531d8addfac01abaca/httpx_retries-0.6.0-py3-none-any.whl", hash = "sha256:d1e52a8f68a5df42de75ab89049d5020b2d0ab2f5f8bceacda008d12aa1257a3", size = 11776, upload-time = "2026-07-06T00:52:31.033Z" }, +] + [[package]] name = "idna" version = "3.18" @@ -542,84 +378,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310, upload-time = "2023-12-13T20:37:23.244Z" }, ] -[[package]] -name = "jh2" -version = "5.0.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/b1/b2b7389b2e0ddac90a1aecbf4a761db8790de85dace7695c01173ed083cc/jh2-5.0.13.tar.gz", hash = "sha256:f8c78cffb3a35c4410513c3eb7989de36028c84277c04f07c97909dd94c23a75", size = 7322505, upload-time = "2026-05-29T05:21:43.516Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/96/c38d3555cc9bbb1c309c896e99dbb0b1f76308bed7f2d6226cdbeaeb3eee/jh2-5.0.13-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2195557f5953ceee743d0eaab0b09ec537bbc1b9a2c6c1463106bfca9b03805f", size = 601454, upload-time = "2026-05-29T05:19:23.763Z" }, - { url = "https://files.pythonhosted.org/packages/4e/84/a3043ddaf636cec0f4c3aa179a9ef3a59db3d06f770ab6007133ca31bc5f/jh2-5.0.13-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e734f8a5cf002316cc81b80d8a91a0f7c62d631270abd913746644fb9ae288d", size = 384187, upload-time = "2026-05-29T05:19:25.504Z" }, - { url = "https://files.pythonhosted.org/packages/fb/8b/72dba3fb64bf01355a0bbd78cbe0a2635759d4a6f273c1b118760ca8ecf0/jh2-5.0.13-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba5e545b3209e8aa5b6af88e7032814473e57d60d32d3fc6b6185453733a4b26", size = 389346, upload-time = "2026-05-29T05:19:26.926Z" }, - { url = "https://files.pythonhosted.org/packages/8e/2e/6f07e42de6101d4555f14be1e4f42a056c9a44d7ee8f745b3c188c96cbe0/jh2-5.0.13-cp313-cp313t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2a37164eb8f2462268e6252db93b009cb685fa060b62ad217bf1b333b450c68d", size = 510278, upload-time = "2026-05-29T05:19:28.175Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a5/85682dcc959682aede0f80206a16574c51af51ed26eb1e87252fd31c6ded/jh2-5.0.13-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4eae2d5bc1a91033450fd4381c04f33db7d4c9cfd31046c4708c8c1323c06d0", size = 503090, upload-time = "2026-05-29T05:19:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4c/a8a301d494c0a1c2859951d44b4fa54f3b2db91826c719ce5be8b662c5c8/jh2-5.0.13-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d0a10036cdf8bf1a36b07c10b69ad0d08ce31d99b8962e3d055ee417fe3423", size = 402843, upload-time = "2026-05-29T05:19:30.505Z" }, - { url = "https://files.pythonhosted.org/packages/21/5f/5f9e0933f458a89005af3b2aa7d1c61cc3cc57c8a633fd54a95acff85121/jh2-5.0.13-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ad4d68340e38a8474a48c424e2929d3e2e82e35e4e8ff1188decb1985d1b4d7", size = 386327, upload-time = "2026-05-29T05:19:31.988Z" }, - { url = "https://files.pythonhosted.org/packages/75/3d/353ec4a6763e8b6be8d162e6b23f7e1499e75b1bb934cee1e5b5fc062f1e/jh2-5.0.13-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3f8c5e81254b986f8abe9fd974e7ed595aeaead3f3066ce9ed95dfff236ceca9", size = 406978, upload-time = "2026-05-29T05:19:33.438Z" }, - { url = "https://files.pythonhosted.org/packages/4c/e5/cca58b58cb8bab1891f45da43ef91b498ff93d573a1a2836af11962ccfb8/jh2-5.0.13-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:da030729eb8ef386569e78c10e2429adb10026ce82989fdeb906b037314d66c7", size = 560030, upload-time = "2026-05-29T05:19:34.879Z" }, - { url = "https://files.pythonhosted.org/packages/c7/c9/c3391d8607781016daa1881d8d80f81e0cf0302ff06c86342c492279cf48/jh2-5.0.13-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:a5d2ceb2e9d42ac236f0da7b2f9e8237818d32a10d892b06f30f7160ee4365ae", size = 664407, upload-time = "2026-05-29T05:19:36.138Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b4/3375744e2f33d097da241dfd5262c9997a87c2b948db2385fbf100c7dd12/jh2-5.0.13-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:1f4dffe1c6ed0d4b0d2d9a45d064622bb786cbf87db35eac469dd4c376ac6fb6", size = 625242, upload-time = "2026-05-29T05:19:37.93Z" }, - { url = "https://files.pythonhosted.org/packages/9e/2f/e33763b1f66817bd8c848777f05cf3781ad9ee1c642741458bfb66da1921/jh2-5.0.13-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:bdeb48657db508e8bc299744aa431798efdf090feb3959cf0123167c9251ebb0", size = 591124, upload-time = "2026-05-29T05:19:39.373Z" }, - { url = "https://files.pythonhosted.org/packages/90/92/5a8de2f6936c7af77555299732795f4607d2cefb974ad485acf25b3f998b/jh2-5.0.13-cp313-cp313t-win32.whl", hash = "sha256:b41867d5decd7cd62e12cdacfc45c5d8f6eae872578ae01d78900174d5e47b82", size = 238101, upload-time = "2026-05-29T05:19:40.917Z" }, - { url = "https://files.pythonhosted.org/packages/e7/71/0f7ba74bb6681e60e871284772a216a50cfd63f4f62ef52edd13d8a7c057/jh2-5.0.13-cp313-cp313t-win_amd64.whl", hash = "sha256:62aee4bd128e17871980e4847cc4b94b67651de612bc96dd3d7ebf647c68a6e4", size = 245923, upload-time = "2026-05-29T05:19:42.338Z" }, - { url = "https://files.pythonhosted.org/packages/b1/dc/11434a6deee70630f5f38cc40813692e1faadd4259f242f4f810f35709eb/jh2-5.0.13-cp313-cp313t-win_arm64.whl", hash = "sha256:267aaa7a96f54452e1e07c7701799bc7817e1f3bb2de09301b2c492841e4c98e", size = 242596, upload-time = "2026-05-29T05:19:43.853Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f8/e9e82b7b51947edbf762edb26eb9bcfadda10fc7526d092244528e9cea8c/jh2-5.0.13-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1dd137086b13d55a53c4b7b8b75869c824060a4d35615e4f49180ae58c7783f5", size = 601736, upload-time = "2026-05-29T05:19:45.14Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f4/e82b4c5797c20facb2f1b54f893a422f8ba44f83bc66da3d60d90a1c3064/jh2-5.0.13-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75d0b2bffb2e260a0f3578a11a4999a713c94d1bd8de7cfe692c840e20ae83cf", size = 383983, upload-time = "2026-05-29T05:19:46.687Z" }, - { url = "https://files.pythonhosted.org/packages/27/df/f439704e7a884b5bd0181188b30217d71d48ce6bb9a9d7e8378d00ed3497/jh2-5.0.13-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f681e197bf6552df3bb7a3abfd676e81316b69e36e7263e72383477174032ca2", size = 389233, upload-time = "2026-05-29T05:19:47.973Z" }, - { url = "https://files.pythonhosted.org/packages/49/9e/c2cbdb2261724edb83fa0bdb260f75ad9b091189ada1651608f8ade71906/jh2-5.0.13-cp314-cp314t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bde7e7ff0f17e87aca9ef23f66b80540a2de0ff8e2c26af55eb2e288a2a5f74f", size = 510268, upload-time = "2026-05-29T05:19:49.183Z" }, - { url = "https://files.pythonhosted.org/packages/1d/cf/65925f2aaea92e5f4a90a578b86a975bf6327ad53d82035a4d94f8f63580/jh2-5.0.13-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f8d364d53cc0854d910f2305726a8254acb527f13fa933dc213a591e21e7ffa", size = 503003, upload-time = "2026-05-29T05:19:50.699Z" }, - { url = "https://files.pythonhosted.org/packages/d4/fc/684ae4efe7568ec9f8ce427b6ecdc7387f870b3bbdcee75c370326685865/jh2-5.0.13-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b572daaafbd867bb5c5f324c918704ddd6d76af1cc7c2be483c3649b76ce7940", size = 402856, upload-time = "2026-05-29T05:19:52.004Z" }, - { url = "https://files.pythonhosted.org/packages/0d/41/009c8b95f471ca83667e63e87a845f634f7b100f9fa2c1cbfc090c142ec9/jh2-5.0.13-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0772ad66f346038fe05cb2928866ff1b21bcaa9ec3f14709fa1d419ed1dc287", size = 386592, upload-time = "2026-05-29T05:19:53.201Z" }, - { url = "https://files.pythonhosted.org/packages/d2/8b/5b49f66cadc6d89db5339b602c06a9567ef493c33b860324f24a96b390a4/jh2-5.0.13-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1d8313b13acfdef19c5ff793fdf397bae3d8d75181f6eee62604f13e4ec20a8", size = 406930, upload-time = "2026-05-29T05:19:54.441Z" }, - { url = "https://files.pythonhosted.org/packages/55/a4/7b997c7787ee6316c6b4f7bc0ff4d80c062ef3288038fbee870ed83f0946/jh2-5.0.13-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:99aedd20daacb32f377152c9103d8d0271f32d1932bd39a5ed2cd2eef97f7e07", size = 559781, upload-time = "2026-05-29T05:19:55.659Z" }, - { url = "https://files.pythonhosted.org/packages/ba/08/b75afc38215f961ba8d92af64a9e7cba57767829786484c5cb98fc1a1d2f/jh2-5.0.13-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:53b1df29ffe966cde1d2f0085b6acfcd237504627d406a51717ef3849ec88831", size = 664237, upload-time = "2026-05-29T05:19:57.031Z" }, - { url = "https://files.pythonhosted.org/packages/a1/92/0fad513d95376611b337c631abd0263e641b78b0adbe80f3cf561e614d6e/jh2-5.0.13-cp314-cp314t-musllinux_1_1_i686.whl", hash = "sha256:0ee6a5dd24a1dc0772a89b3498ed20f60167fbc633404b1e02895f04ae66c47c", size = 625249, upload-time = "2026-05-29T05:19:58.447Z" }, - { url = "https://files.pythonhosted.org/packages/79/28/4f70df7bfaf1cdb560340400b46849cfa96e4b08d2430793501013d7ee8a/jh2-5.0.13-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:521f871864880cabe361060f070516a56a54abc3e96f07bee9ba8b061eada891", size = 591017, upload-time = "2026-05-29T05:19:59.839Z" }, - { url = "https://files.pythonhosted.org/packages/dd/58/ed891762aa68542fa7969c4cbb109817231740f342931165f60ea7a2a923/jh2-5.0.13-cp314-cp314t-win32.whl", hash = "sha256:5909e2273f8b6fcea93c95564aa0f366ff9261a544a7a5d640ea95341321cc37", size = 238041, upload-time = "2026-05-29T05:20:01.228Z" }, - { url = "https://files.pythonhosted.org/packages/48/13/46ae2bf16baad7841526dba44668083f9de3e427027e4fa44a567cb778d6/jh2-5.0.13-cp314-cp314t-win_amd64.whl", hash = "sha256:2cc4923ac6f57753e0cb4aef054a04f5d5c744b274dabf863f695fa3c2fd75f9", size = 245861, upload-time = "2026-05-29T05:20:02.757Z" }, - { url = "https://files.pythonhosted.org/packages/79/49/3652903fc82c0e5c82e9caa3445e3e514b168969c2dae3da15c46627661f/jh2-5.0.13-cp314-cp314t-win_arm64.whl", hash = "sha256:d3cf203de31371289d4c1e6b4d30c9f6de95b2bd2b300fe5468ff946299d0e48", size = 242699, upload-time = "2026-05-29T05:20:03.9Z" }, - { url = "https://files.pythonhosted.org/packages/75/f3/8c151f371f7962902e6f1f494b50abc1b8be27305a9d12cb88cf049c5a80/jh2-5.0.13-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3f54095cde010ff4e52f23dda92b31d3d3160c64f5f23fc6e181f8c7938afd33", size = 618581, upload-time = "2026-05-29T05:20:05.043Z" }, - { url = "https://files.pythonhosted.org/packages/71/3f/a2d5458586c024e0076ae8182dcbb71450bd9d4dd65ffdaa659313461bd4/jh2-5.0.13-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f810dd02b4a9647eacbe4eab617111c0e3ffc68e923e3ff172948db601d2ba1e", size = 392776, upload-time = "2026-05-29T05:20:06.178Z" }, - { url = "https://files.pythonhosted.org/packages/7e/1f/cf205104cfcef4bd26aa2b736f36ddf3739b69ddaf14d23a5f7697673d74/jh2-5.0.13-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29c23f3937f257beb5cd5b17f8727404f1267ff25d26e54c0a35d5defd0c895e", size = 397943, upload-time = "2026-05-29T05:20:07.479Z" }, - { url = "https://files.pythonhosted.org/packages/cf/82/bd218132e5a081293cf3d7de5cf41d871f3f88808c23f6c6aebbe073fb5e/jh2-5.0.13-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c697b250a935030a128b45b41f507a4d33a082d8b8e12311c7e73fbb3970f057", size = 520351, upload-time = "2026-05-29T05:20:08.936Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/78b564899237909ddf4103569be59ea31b6c00f8b4f1c178cc57c6e4e45d/jh2-5.0.13-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47d3203ed6fec39df4922764817979fad3a13d22cf750adc9cf9dc2b3399eacf", size = 511162, upload-time = "2026-05-29T05:20:10.239Z" }, - { url = "https://files.pythonhosted.org/packages/db/3a/b59157363139436cb60620353aa8b687197c9a4ec1eb87952004a8843e6e/jh2-5.0.13-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5146468a76bbe8f4035f23a78813c5c433c70c0ca44eb0ba5558627a9e644b8", size = 411838, upload-time = "2026-05-29T05:20:11.515Z" }, - { url = "https://files.pythonhosted.org/packages/15/a4/18b35000b00d9fb72003e0eacf66b3af828a0bb897c21a287a33d2025138/jh2-5.0.13-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85d3d338284fbfbe173243c3cc62e510c6fa0d6d023e67da2292bd7578c734e3", size = 395975, upload-time = "2026-05-29T05:20:13.157Z" }, - { url = "https://files.pythonhosted.org/packages/ab/42/f017b6b7b666e6ef615f7c64fd748201d0b20edeed094182c4f37f23a6e8/jh2-5.0.13-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:641f65f5414b8990251bd7609513a696831558f69d2ca77d65dc3f7087f31267", size = 417493, upload-time = "2026-05-29T05:20:14.394Z" }, - { url = "https://files.pythonhosted.org/packages/bb/fc/054443bd40a9827ade99508482185692530cf56a50d4ad8ed2aba761f11d/jh2-5.0.13-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:cf3dc6f7c14340241260bf63e5d6006b2ee7db45dafcb39c16fdf164ea0240a0", size = 568373, upload-time = "2026-05-29T05:20:15.914Z" }, - { url = "https://files.pythonhosted.org/packages/47/48/dde093626bb976722c940053ceaa82407bd358e07fa6a1690590456d21b5/jh2-5.0.13-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:fb8e37ecf37382c02af83da521df5a77561441bc4cbb93b00627e015b7bbf349", size = 673766, upload-time = "2026-05-29T05:20:17.136Z" }, - { url = "https://files.pythonhosted.org/packages/96/94/edae999301bb28738e17cf54ecd50b7d7d9fd96d9536376bbeb9b8498526/jh2-5.0.13-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:296ee44dfad08ccc3857daf433dfa4a555d5b45f2df39503ccdd90ff10b32943", size = 635045, upload-time = "2026-05-29T05:20:18.633Z" }, - { url = "https://files.pythonhosted.org/packages/c3/5b/d9fd0fc71e43c37c30d581affbb7371fa8177ce211b663457939bbed9e61/jh2-5.0.13-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1a75eed545420a31ea356e8046b8d86fcf694234dd1035191734a03720fb8a44", size = 599870, upload-time = "2026-05-29T05:20:20.117Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ec/66c17d8aec77d41c8f93145dd7faa7138b9f1bc370fa685008bf92d75f9f/jh2-5.0.13-cp37-abi3-win32.whl", hash = "sha256:4812710810010c428db04e9290a13117f7b7c5a3a663b46a0586fe101cf25c9d", size = 245231, upload-time = "2026-05-29T05:20:21.275Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/107f16a84c8becef5281040fdb50681f06e4615bb4a754f211abe7d61a6f/jh2-5.0.13-cp37-abi3-win_amd64.whl", hash = "sha256:5d61bc0a62b4eee11039bf29d4bdbb26efcff760dfb9d8e0739740028aa52b0f", size = 252085, upload-time = "2026-05-29T05:20:22.426Z" }, - { url = "https://files.pythonhosted.org/packages/47/7b/e8baeef7655449a6c9abee0165fcb1c63d12ec5432b8d810ff5910184690/jh2-5.0.13-cp37-abi3-win_arm64.whl", hash = "sha256:3f74dad9036e599eed51576492b00955237b86cf886f96844708a81cd0f5c710", size = 249139, upload-time = "2026-05-29T05:20:23.558Z" }, - { url = "https://files.pythonhosted.org/packages/64/5c/5f006d5bec874c9b12f36f20fbdd3fce545994b3d43eb216266d36c1cd09/jh2-5.0.13-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:72122b850d58c4ae0612c9b9bbabeddbb092b503eb39d87249da323ba1b074a8", size = 614502, upload-time = "2026-05-29T05:20:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/61/9e/ee99e108c6b458ec3298a76b59385ffe0e8f82d93ad30efbfb23632f6991/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c4d4ef923464dc5b39d39b1a41209a23e387698a254e81813d0757f76ff00a", size = 390823, upload-time = "2026-05-29T05:20:25.959Z" }, - { url = "https://files.pythonhosted.org/packages/3d/64/740cc33301c84ba79304567c3d199bf269805b7baebe8e0082739b97524a/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc238641d3bc35dc79965f330c7f0f16ca992cf999c8892dce0bd36c32056eee", size = 396388, upload-time = "2026-05-29T05:20:27.202Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ed/522f67aa1946c27ef66dd85408fb16b94fe280b84833f06455e2ead119bc/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f9a82f90a21660fd0313a81c838bff89a13c7921c317abfc3117e49f9d46c84", size = 517651, upload-time = "2026-05-29T05:20:28.353Z" }, - { url = "https://files.pythonhosted.org/packages/ea/5b/e5d8568a60b265e6b34672269407ff6d3a6c43be821b433be54dc8ce63cd/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1ef1d6c90389c366ade34bc6b100e823569c60bbb0b0f2cc3b308031090b252", size = 508661, upload-time = "2026-05-29T05:20:29.739Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ea/1490d995d08cfb67350625f306297b9407ce7012ddbb83f97b1d18c63be8/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95d72124353a530ffa5a65f1f94a4396521f5472f0fde79b93dd66a3addaadff", size = 409884, upload-time = "2026-05-29T05:20:31.016Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b1/f8a48046f17d927d5847504b1199212755baba5f1d842f9a0bc7abd5d7db/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50d224b482e1e18fa6bbe90d05125993122c5530b0b83f48829d7a535e2b60f2", size = 393947, upload-time = "2026-05-29T05:20:32.222Z" }, - { url = "https://files.pythonhosted.org/packages/12/f0/001ea64504515c7990394ebaedb285611c7dd8786ad2005a4220e4e0575e/jh2-5.0.13-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:49531e0042b3a4d35d34ddb892bb734d77969c9b787e6f2f0253cb10b498d4e8", size = 414876, upload-time = "2026-05-29T05:20:33.519Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4d/25126b7a084c946326d3fa73d24473853229dd8d3f5f0e26d3ef6ded36f5/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a8bfbc5f77e95f485bf046d4a19bb773866cdc9d1b21c27a194366a55d7fe361", size = 566960, upload-time = "2026-05-29T05:20:35.12Z" }, - { url = "https://files.pythonhosted.org/packages/bf/bf/2bd0a1eb5f8cb1e516ad774efa827647ade125aa9bf658944ee5366dbb60/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3e5e73bbfcf0690561a1e2bf8b2d8f1e0bfd1aca67ebbed2a753cf9440f912ef", size = 671979, upload-time = "2026-05-29T05:20:36.383Z" }, - { url = "https://files.pythonhosted.org/packages/4c/bb/e58646c5764ff6f6c1ee9f437c43247a3043cc7cb524d9777368c390ba32/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:4efed5de773df44042a07a50ea8dc476a4c120bfd3fcc4fd7a7ea145196832b2", size = 633173, upload-time = "2026-05-29T05:20:37.632Z" }, - { url = "https://files.pythonhosted.org/packages/3e/54/0bcf70845cc7ae1c0cf0da56f81460bbc2f774a03708d0fbfa164439d684/jh2-5.0.13-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a915e77adc126bd79867b7cfecc930021ef1a2599475dda058ae80537a7068ec", size = 597680, upload-time = "2026-05-29T05:20:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b2/7f5e5305e261df12244d28eb5d8150affbe6e93e3d301df7d71ca872d89a/jh2-5.0.13-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f3768fa39d0c5a2578b285e49e8d42230bb182bb2f7984840549f89c04ce4a56", size = 608031, upload-time = "2026-05-29T05:20:40.212Z" }, - { url = "https://files.pythonhosted.org/packages/ac/8a/68b232de0eecc6e52548d9ef7ca20baf18d8cfb5cc64186d8015dda0188e/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ff9abf4b4769c9c8117d929f87c90415cb65f54faa22f64fbf044427127e65f", size = 388655, upload-time = "2026-05-29T05:20:41.707Z" }, - { url = "https://files.pythonhosted.org/packages/2c/31/dacd2315290e309df81b931434f30e3e73e0fededb9e0487667a5287635f/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:797e8846a4c7a18a9b3abdd437ca2fa06d39223df7a6fdcae57adeb7cc3d7a95", size = 392725, upload-time = "2026-05-29T05:20:42.912Z" }, - { url = "https://files.pythonhosted.org/packages/04/3e/a9fa9e093e984fd778bedcab2246f16b0e5a5b147466dd1d5784348239b5/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:31fec2270a204669fd5183465e3ef6a666a0539af61bf8e995df91f16c945c0b", size = 515975, upload-time = "2026-05-29T05:20:44.213Z" }, - { url = "https://files.pythonhosted.org/packages/48/ab/2bd00b443b4cb018ed522b1ecadb1edcfbda2226b5f1dc4b733488cd1a9f/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f48eeba6c0bb91321dbefc60527d945f20ccee3f0066bb3ba0d9b71120ec9417", size = 505682, upload-time = "2026-05-29T05:20:45.38Z" }, - { url = "https://files.pythonhosted.org/packages/83/bd/63d467fda5a86abec281ebfbe7d21f49689d97e0473800f11815fe92dd10/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05849bda038c2f8cee50559e3fd363af39c80fb1f24ecf947ccbc7e1a890683f", size = 406385, upload-time = "2026-05-29T05:20:46.568Z" }, - { url = "https://files.pythonhosted.org/packages/71/8e/146902bf3b16bcca18daf217ee9a1af34a47f6913f28c24bb403faf9832d/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:882b13542d7bd20f79cb92fc31ecc9587037278d0a6b36c14b734f9ac2cb8890", size = 391626, upload-time = "2026-05-29T05:20:47.916Z" }, - { url = "https://files.pythonhosted.org/packages/4b/bc/d500231f338aa889b29cdab977d99bbb94bec198f7fa0adc741b51118446/jh2-5.0.13-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84d42f63b1e7368194704159637e534280540369b35758a4daa712bf277e80ef", size = 411317, upload-time = "2026-05-29T05:20:49.336Z" }, - { url = "https://files.pythonhosted.org/packages/08/b8/0f7e84ab491aee9348731e4ef266595b8153aa9da63d5376e96970fb3795/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f6e95bfee4a72a7eb6fb4ca057ac5da297df68d302c63c300a5f400a0003fe31", size = 564695, upload-time = "2026-05-29T05:20:50.604Z" }, - { url = "https://files.pythonhosted.org/packages/8e/fc/776a56566dacc3918f51bbbaebab6c23a2e979a70b40ba1f526272f7b9c1/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:6eaa9919f7faf162d2acf66e1632c4588840589eb0030688907c9eb6a5ecae17", size = 668164, upload-time = "2026-05-29T05:20:51.905Z" }, - { url = "https://files.pythonhosted.org/packages/9c/54/ebcc2970d6ba34fdc6be0b8fac07a6fefea7e07e704654fd6dcc33d57f53/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_i686.whl", hash = "sha256:7586a835ed5e213593e35e0d6f90197dd76412fdcba62e05b15df72806af1e2b", size = 629234, upload-time = "2026-05-29T05:20:53.281Z" }, - { url = "https://files.pythonhosted.org/packages/cc/a7/cfa24ecc4f3a70d16d4a33557b57f1c6673fd9fa0fd10b7a932342bb27d4/jh2-5.0.13-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:6d84d87f6c1b2131273b8078f5863d980269fa53139d7f7fa76e6099e2a00bd1", size = 594400, upload-time = "2026-05-29T05:20:54.63Z" }, - { url = "https://files.pythonhosted.org/packages/ba/1e/be44f6e9ff5689fcdf9a2c0ed30880fd5da303676d89d6457a603450b7b1/jh2-5.0.13-py3-none-any.whl", hash = "sha256:2b311414989da48a155721fe47d0dc3e3b75360efd62d779aaff72d786be91cf", size = 98909, upload-time = "2026-05-29T05:21:42.086Z" }, -] - [[package]] name = "librt" version = "0.15.0" @@ -762,18 +520,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, ] -[[package]] -name = "marshmallow" -version = "3.26.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/79/de6c16cc902f4fc372236926b0ce2ab7845268dcc30fb2fbb7f71b418631/marshmallow-3.26.2.tar.gz", hash = "sha256:bbe2adb5a03e6e3571b573f42527c6fe926e17467833660bebd11593ab8dfd57", size = 222095, upload-time = "2025-12-22T06:53:53.309Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/2f/5108cb3ee4ba6501748c4908b908e55f42a5b66245b4cfe0c99326e1ef6e/marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73", size = 50964, upload-time = "2025-12-22T06:53:51.801Z" }, -] - [[package]] name = "mccabe" version = "0.7.0" @@ -847,20 +593,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] -[[package]] -name = "niquests" -version = "3.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "charset-normalizer" }, - { name = "urllib3-future" }, - { name = "wassima", marker = "sys_platform != 'emscripten'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/03/1c/836ad34fb18fac781722019100e2bffd81eb9a0011d7a91c434a7094460d/niquests-3.21.0.tar.gz", hash = "sha256:5b7d10a05f4c7ed08cede0af74f492ae7a8a5a71291833d029e23365fc3ea80a", size = 1070211, upload-time = "2026-07-29T10:26:24.993Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/9b/c08403f905f9aaffc74b483fbb0ad4cc372d1da22cccb4ca771a3e65a185/niquests-3.21.0-py3-none-any.whl", hash = "sha256:dbea441b9e9d5d755e02caa2b57b94cdb97b23d754ead21b80a4ac18ef66805f", size = 230382, upload-time = "2026-07-29T10:26:23.194Z" }, -] - [[package]] name = "packaging" version = "26.3" @@ -1120,91 +852,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] -[[package]] -name = "qh3" -version = "1.9.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/ae/9d42d6df0ab9a014138332346fd690f7b0be0556861421d2459caec28d6f/qh3-1.9.4.tar.gz", hash = "sha256:bd2ea9baf19656c544a48a56a195f2ac257cd973b566f5f2998fa3b7446281a1", size = 346724, upload-time = "2026-07-13T06:36:32.05Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/b2/7a8f6dcb93a7a23db07c556fe57b167be7b880dd1771d3df62ab1dce4e56/qh3-1.9.4-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:66567176a55bc92f12a7cdaae6d0245f808333a6a55221f37577f79c56848b71", size = 4359072, upload-time = "2026-07-13T06:33:03.308Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ef/343c02a1fe2ee1215120483fee965eab0ee72ea4a9ae122a9b31add14ea6/qh3-1.9.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a931eba5b6b11292ed65e164a416b78a50139fb9c9dc9dce90006cbd853d1fdd", size = 2159084, upload-time = "2026-07-13T06:33:05.531Z" }, - { url = "https://files.pythonhosted.org/packages/57/c5/c92e03e4711a1b3e09924d654f3c17dd62b91233302a77ce5695215a2b5b/qh3-1.9.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:325ee1f729e7b2f8600e71e354715db18ec4e669a001449f5924ba0521b9bafe", size = 1862100, upload-time = "2026-07-13T06:33:07.147Z" }, - { url = "https://files.pythonhosted.org/packages/73/9d/88f5088430d25f3a0bc414b15b2b0b22a117468e49bba981c140337bb90b/qh3-1.9.4-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9b38c9e5c6552828118676795b0a270e2337571e25d17bf57c18c2c5c346df5", size = 2032620, upload-time = "2026-07-13T06:33:08.738Z" }, - { url = "https://files.pythonhosted.org/packages/49/0e/26470349a310715c05776c26e289e54c6a49f08289249445d5a660d364bd/qh3-1.9.4-cp313-cp313t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9cf35ac8f4133a88ad3d8e35d63bfd566f369071a496a16ed69ae09b01b9a64a", size = 2034678, upload-time = "2026-07-13T06:33:10.398Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ae/ad55ebd12852e5c714e1c2956c4497d6d09417e9af5c66eb672b79cdd7f6/qh3-1.9.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e5458cdc9e5214b111d13b86207c2ac799e1191d2e084c442d4fae1ec73fd9b", size = 2032574, upload-time = "2026-07-13T06:33:11.973Z" }, - { url = "https://files.pythonhosted.org/packages/2d/27/62b09bdee46d603fd581bec43c98a86c9380f2722d10cb7885d623c1d4e8/qh3-1.9.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9486a8dfc1d8eba625e7f92f3a1c45e4a131c7d96d33b73c20d584cf6bb99055", size = 2089542, upload-time = "2026-07-13T06:33:13.591Z" }, - { url = "https://files.pythonhosted.org/packages/96/b4/b89476bfd17c47c29425f3b9cdf5bfbe27872853c1d0a53c9315be05c89b/qh3-1.9.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3e2e90656a46c49ec04e920fbdff28a14eb4439fb660e5de3074c3fbab4ee13", size = 2367301, upload-time = "2026-07-13T06:33:15.142Z" }, - { url = "https://files.pythonhosted.org/packages/46/a9/f21458c4ee1b527030f3d23d67ecf97caa75a8a9c0e021ddaac02083413a/qh3-1.9.4-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:4555d49cb3167904a48dec772fb3a71279282c05e70cc2be9d59b597d1fe71a0", size = 2015315, upload-time = "2026-07-13T06:33:17.003Z" }, - { url = "https://files.pythonhosted.org/packages/df/9f/30d83600b6f6bf8a3a995e05b46578e333c8ea5e0a799ea28d5f6f28c262/qh3-1.9.4-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d5b6a7af8eb57dd168c023ffb041274b35b120a8eddf0a37f9bebcf4d69bf3c9", size = 2349006, upload-time = "2026-07-13T06:33:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/7dc1ac91848f2774c68935da37ecf5e5d069a02b4f98934a74f8868a531a/qh3-1.9.4-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:593165d62fb259ee9a756e1bf07aa66cb445680e5bbb7fa15d8d3ba59dcf923e", size = 2133242, upload-time = "2026-07-13T06:33:21.12Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/d7d59c1474be53394555b575614eb086d10ed23b7ef3d83f93571ee40fd2/qh3-1.9.4-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:2369dd42c97de4cf628503fd5c02b359b23cabd0ff1773d33b135395748c6e42", size = 2230318, upload-time = "2026-07-13T06:33:22.584Z" }, - { url = "https://files.pythonhosted.org/packages/3e/42/eeca764d3e78b6a3910f95bd33af638f15fc88c6af98807abdc9fb0d75ec/qh3-1.9.4-cp313-cp313t-musllinux_1_1_riscv64.whl", hash = "sha256:eb99f81ad441f64f29da560a0619c9e5b8d9f4532e611c1d6319b536a9d36f53", size = 2125385, upload-time = "2026-07-13T06:33:24.284Z" }, - { url = "https://files.pythonhosted.org/packages/fd/4d/41d6e4f8d06052873a590557769dfce8a14627e1cc03f74600313ed0c552/qh3-1.9.4-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2a875175ab7c03d06fdb6784bf207d6aa1d30d2bef8170cde49b6960a0ca96a1", size = 2587129, upload-time = "2026-07-13T06:33:25.841Z" }, - { url = "https://files.pythonhosted.org/packages/65/79/05804bdedc1be747e319091ec770cc8ce5499008b16a70ce2cde035d2558/qh3-1.9.4-cp313-cp313t-win32.whl", hash = "sha256:72c37f9fcf7b1136f245a333bceb7582fb4dcc955bfad0937ca6c857748b5638", size = 1855980, upload-time = "2026-07-13T06:33:27.522Z" }, - { url = "https://files.pythonhosted.org/packages/86/2c/4eb17c6e4743b8b003ed9c4fffe3acfaa29c000de09a9d8ce6ade1f0b428/qh3-1.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:257a1454732d261b3cf8af90d52cfa13ebb213a899d7983d7707b0c30f4a2533", size = 2122099, upload-time = "2026-07-13T06:33:29.075Z" }, - { url = "https://files.pythonhosted.org/packages/e2/bb/b8101f7a96237516a57b6fbcfebacc2c3349ab0b6ff528784f1c90d69edf/qh3-1.9.4-cp313-cp313t-win_arm64.whl", hash = "sha256:a21c68c0724521fedc4df1abc4402020a1eb37500d138a1bee1c3e50c1b0edb4", size = 1970072, upload-time = "2026-07-13T06:33:30.535Z" }, - { url = "https://files.pythonhosted.org/packages/0b/89/1b52aa142d772479393c4868f41255ddaf29627db72a36c1f635f51987ad/qh3-1.9.4-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:100368855b0e103774438aaf8b38c711fb51b79f18e3b9dc873ea573648bcc3d", size = 4360094, upload-time = "2026-07-13T06:33:32.269Z" }, - { url = "https://files.pythonhosted.org/packages/f3/71/c9dd5b4093999b651869e5277ed33d88fa2ae4eca064896c66319563dd16/qh3-1.9.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9d45e544e0a556380bd2a92af180ee98d32bca6cf622f28350ebc000611bdf", size = 2159530, upload-time = "2026-07-13T06:33:34.077Z" }, - { url = "https://files.pythonhosted.org/packages/ec/74/b4b34a93b1acf1476a757b1c5bac9d38d5c7cb2272d68b3ce1457476101f/qh3-1.9.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43f80d662078c26563a1aac32a4df22649473d996bf2422e073c8aa55b1c837c", size = 1862303, upload-time = "2026-07-13T06:33:35.801Z" }, - { url = "https://files.pythonhosted.org/packages/31/cb/dc3004df9af3922cab529c53bd6b58a7d44cce02f6b6dd7f795d8a3350ce/qh3-1.9.4-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:551f60d487d849d6f2313dda94e54ebb16b1a29acb93676540ae790a56308c24", size = 2033172, upload-time = "2026-07-13T06:33:37.677Z" }, - { url = "https://files.pythonhosted.org/packages/82/be/6952fde329c37b8c2f64548df726bd86b8dfda12df740b244114adc7e60d/qh3-1.9.4-cp314-cp314t-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3755d777ff35561bc719f571ebc4b91aaa8a945c210e50b2576bf2cc69cb511f", size = 2035596, upload-time = "2026-07-13T06:33:39.391Z" }, - { url = "https://files.pythonhosted.org/packages/98/5b/67676a616baba1265e5f06ef202779099a9b4926c81342c007528c68a3f8/qh3-1.9.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04b64bb18e10d8f482bea1ef43e5da3448e2d5ba78bb0c735da5b9e02d7dfb0e", size = 2033387, upload-time = "2026-07-13T06:33:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/94/03/6823de539154091b46234c667e49bff9b06bae44a82d14895d813f909fae/qh3-1.9.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b47597daa4ae9195f81a35822217a395d3277f01a393d9c2bb3796a6b458b22c", size = 2090092, upload-time = "2026-07-13T06:33:42.875Z" }, - { url = "https://files.pythonhosted.org/packages/1f/5c/8a3e3b005f66091baf0310a429003733986d9e001d13f3f7781ff3849873/qh3-1.9.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9bb9012bc02c44da6af1b931b6082588519685fad8a1f866c341f749394f53f", size = 2368083, upload-time = "2026-07-13T06:33:45.085Z" }, - { url = "https://files.pythonhosted.org/packages/02/3b/6feff7b78e7cf9eaf3b25af6d7239754c32d62881f9f392bf4d18d60a907/qh3-1.9.4-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:9c7f3781ec022dd958b5eb0e5cb2e2a54e207a3d8837fb7290bc239912648fd7", size = 2015665, upload-time = "2026-07-13T06:33:46.794Z" }, - { url = "https://files.pythonhosted.org/packages/54/24/07692a20232b6f8788a54c7f06b25ce702b4a94a5ad79bf680a9e32fae08/qh3-1.9.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:633a24ab2db76d64881876c9d0f1b226a47d892bd7d78e61ea2c788ab59570af", size = 2349383, upload-time = "2026-07-13T06:33:48.495Z" }, - { url = "https://files.pythonhosted.org/packages/38/15/f447e2ea4216757fbb5627ce96bb79e386133b0cd3e8f3c1b0142d4d1bdd/qh3-1.9.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:f2ff36b84a4ab9b68806af1f6985fa3b47e29695effe766a3e70c21efac8157c", size = 2133616, upload-time = "2026-07-13T06:33:50.079Z" }, - { url = "https://files.pythonhosted.org/packages/54/4a/dc4a346000aeb4d21aabb4b6daf47231556e47d534be3fa4a9ce95ee6d83/qh3-1.9.4-cp314-cp314t-musllinux_1_1_i686.whl", hash = "sha256:2100697d736dbdfd1d3f0266c9fcadea6230b66ed59e3e7f8c9a9d1a6eec0c65", size = 2231106, upload-time = "2026-07-13T06:33:51.678Z" }, - { url = "https://files.pythonhosted.org/packages/38/b8/6090e97b28c7486d045309ebf010f8bd79a94b5838edeb7b39080ef8b1ad/qh3-1.9.4-cp314-cp314t-musllinux_1_1_riscv64.whl", hash = "sha256:9ce179efaf97f927f01fe158619f5624d8d874c7161b7c497f06897cdde210d5", size = 2125844, upload-time = "2026-07-13T06:33:54.318Z" }, - { url = "https://files.pythonhosted.org/packages/f5/b7/a8d8d0568758ac85e87003aa550b7557b502eb1b600309abbdbe6246294b/qh3-1.9.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:476ee4fcdbd3454eb0eb4dee5678294f06c08ca8e8b83160e2d0ae3c4ae34603", size = 2588008, upload-time = "2026-07-13T06:33:55.915Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ab/318fb071ed092edc1b6572066157b6a029c15c42369520d7d7353a126334/qh3-1.9.4-cp314-cp314t-win32.whl", hash = "sha256:fe9df8136bf358d45a2757287523abaeb208fcbe2c1e34664562eb2d8f64dbdb", size = 1855852, upload-time = "2026-07-13T06:33:57.59Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5f/2b617597c665739ee6d0b190aef22df378e759a810a86fe1de7acab31857/qh3-1.9.4-cp314-cp314t-win_amd64.whl", hash = "sha256:1fbdf53feb1732faf6c1cb34f2e1cbe4b543054db7b4d19d8752f1162e9370be", size = 2122462, upload-time = "2026-07-13T06:33:59.182Z" }, - { url = "https://files.pythonhosted.org/packages/85/3e/56694a36751508ec18f66ba115c83a4dad6d576ebbb50523c940eb89b3ca/qh3-1.9.4-cp314-cp314t-win_arm64.whl", hash = "sha256:e03ee1e2157a31c69415c3dbfbfe64e02469499ca46e968d13d8a7b289bf520a", size = 1971117, upload-time = "2026-07-13T06:34:00.709Z" }, - { url = "https://files.pythonhosted.org/packages/f2/48/646ee80a03ef7fac2b70cccea8a8f17c284fbb86fb854227f6ce70898053/qh3-1.9.4-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:26f7c8ed1a7ac0e25b654f030a4f472830c80890cc284dc40a31c17ecc21fbf0", size = 4376855, upload-time = "2026-07-13T06:34:02.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/86/d6777ff057a45debcfeb2a55b21692ce0c6f32a3c3a45531e83cf8ed4ef6/qh3-1.9.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57b3dfe5dd2da2084adea63dba0bf587a4096ebf9207339187c90139d1f460d", size = 2164636, upload-time = "2026-07-13T06:34:04.437Z" }, - { url = "https://files.pythonhosted.org/packages/0e/7b/7ebfd6babc3aaac74fb40beb5f85c289e2dcc40116282a3e48db99558eb7/qh3-1.9.4-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7fa1a3debb5199da3c7f45b0ca6efa55174dfd6229370e774099073143ebe9e9", size = 1865942, upload-time = "2026-07-13T06:34:06.03Z" }, - { url = "https://files.pythonhosted.org/packages/62/8f/bbc0b834b1252356d49d8813156606d426eb7f90ed5bc6aa479bc8aef7c6/qh3-1.9.4-cp37-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dc9ff40d3a06f27b47d93417666f03ba6fd6b9585d9711ebbddf84fa4b7c50f", size = 2038977, upload-time = "2026-07-13T06:34:09.165Z" }, - { url = "https://files.pythonhosted.org/packages/c4/32/fe10f124f2062cb9004c18c1b506d29961e2e4d72d2c20ea1b3503ebe6fe/qh3-1.9.4-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:010b132c33719af3df4c1d6f569e52a2a20046c55a31c5532e741019c8c53868", size = 2042505, upload-time = "2026-07-13T06:34:10.932Z" }, - { url = "https://files.pythonhosted.org/packages/c8/eb/b7b93687c033d4291c8207f47b2b0c7fdb738db89ab5beceae08d866734f/qh3-1.9.4-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:124045b0c04c05afc038a239d7717019b4c4b9d0cb967040b4331f4d588eff7d", size = 2039083, upload-time = "2026-07-13T06:34:12.952Z" }, - { url = "https://files.pythonhosted.org/packages/1b/22/b064ec3fd14f121a8710f41bb42680ed96635b55bc78f2e85d475e8dbd65/qh3-1.9.4-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a94a1726bf46a451a0295d68fafde28794e83ab6fda7457cbd9309c38151de9", size = 2092767, upload-time = "2026-07-13T06:34:14.925Z" }, - { url = "https://files.pythonhosted.org/packages/29/92/54bac0ab3aad24b84f1e3c4868399e42cb8678054650f5f2e436acc2554c/qh3-1.9.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b361e627b6a529ecb231c1d5f3f2abd6d1952b0d7cc02c7718d9657d55dae37", size = 2372367, upload-time = "2026-07-13T06:34:16.589Z" }, - { url = "https://files.pythonhosted.org/packages/4c/78/99f27d776e54724050d41a14762e25e6b8d251b20943b59928f4fca366ad/qh3-1.9.4-cp37-abi3-manylinux_2_39_riscv64.whl", hash = "sha256:43b6cfb2240836e3e54bbe61ad1b8fa5716516ff5359131a2421c55676a24f26", size = 2017823, upload-time = "2026-07-13T06:34:18.114Z" }, - { url = "https://files.pythonhosted.org/packages/9f/f7/ac383f019b69e9c7021e300cde900de36953d44e408fbd323993575ce765/qh3-1.9.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e6db3def610e9c2d2f3602d3e9c8b2156273225f6eedf74a4749d3bc21af8167", size = 2353468, upload-time = "2026-07-13T06:34:19.751Z" }, - { url = "https://files.pythonhosted.org/packages/17/db/d60b95e872368e51756877e98841b77691acb833e89997839a7adfe053d0/qh3-1.9.4-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:22d5879dc93d2c7ee14cc5933b12c7780d9c7705dff4553becea152fcb767d99", size = 2136024, upload-time = "2026-07-13T06:34:21.837Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ed/a84cbc776edb96b8502d9e9093432431927dd827ca2d064b196a43418bb7/qh3-1.9.4-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:2c2b2005fe6c605d61cac05ec2b3c019cc546184fa43444f3d1e3d22ed020ab2", size = 2235665, upload-time = "2026-07-13T06:34:23.425Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0b/ef538579ba7144fadc6865df30bc9ccb3b78ae1dd61899a5da115b0ea461/qh3-1.9.4-cp37-abi3-musllinux_1_1_riscv64.whl", hash = "sha256:e991b0f20b172fea466d0ffd499a85bf2f26decdeeda537275c72b5f7b26ccfe", size = 2128989, upload-time = "2026-07-13T06:34:25.636Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ad/fb9f922d0ea410c7f7f19a746f02be0fab2b5b5c061872ca0965fe767c52/qh3-1.9.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c13775ed27a88b297f6a1aa05ffb286879453f99e1e53e0af2b22e7ed8f92ebf", size = 2593826, upload-time = "2026-07-13T06:34:27.288Z" }, - { url = "https://files.pythonhosted.org/packages/75/a5/aeef82a0edae7003d42c0587dfbceae4ff0ce375f8c2c13398a4aa4a29f3/qh3-1.9.4-cp37-abi3-win32.whl", hash = "sha256:952abe1be90c0114993886792a8a5ec2211f8391df41227f5b7a5a02ff71bc26", size = 1868838, upload-time = "2026-07-13T06:34:28.944Z" }, - { url = "https://files.pythonhosted.org/packages/05/48/77f3055a3790a515fc59b37cdf1f23c374f8a235c6fa1b110153b78668e4/qh3-1.9.4-cp37-abi3-win_amd64.whl", hash = "sha256:5543cb40a48d401f3fa4ec89398e3a213634f195716a9d06d70417c05edbbb32", size = 2130155, upload-time = "2026-07-13T06:34:30.526Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bc/aec1f86da0d093a319f43d88b005b4b71efa94e8f675c2c9764098c2abb2/qh3-1.9.4-cp37-abi3-win_arm64.whl", hash = "sha256:d0bd73b3e73f4f80d2430d410b532222ecf57e9d4fe8600b7683c775cbff9479", size = 1980028, upload-time = "2026-07-13T06:34:32.376Z" }, - { url = "https://files.pythonhosted.org/packages/4e/5a/7c8a46d4ee36b7f18bfa444e63d9cbb6c15141508ae12232ae10bc86a45b/qh3-1.9.4-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2beae7faf80fa9c2c79c29d45e27686ff9d785b46f7da9848c88cce0d867569e", size = 4375023, upload-time = "2026-07-13T06:34:34.247Z" }, - { url = "https://files.pythonhosted.org/packages/61/2a/a096a6fc146806e6848ea1442c2d96f75c5cf6c98bf0136c5b18236bba76/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:817bad9bc133cf6fa393ab6f06ada20d27f8c6c8fce0d64147013f7d802b636c", size = 2162875, upload-time = "2026-07-13T06:34:35.888Z" }, - { url = "https://files.pythonhosted.org/packages/8d/67/8679f6ac6b11a607dad9b7d01ac9c14cdad0d40e372a00821c9185c04e3d/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3154bcc3070d7d674748c7a618ec2718255ba7e8976da4f71c9c6077efc49a85", size = 1866020, upload-time = "2026-07-13T06:34:37.478Z" }, - { url = "https://files.pythonhosted.org/packages/4e/47/e03dbfcbd149346a3e86b4e2051244bdc5bf8d7e7439c1bb392dc879a667/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47f08aa99fd79a3313c0426220541afa266bafe3b966b74f8b54ee3df1ce07bd", size = 2038665, upload-time = "2026-07-13T06:34:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/65/fb/ad57dff7d22640068a4952e36e9ac5d6821dd05130d0779d34daef2cd335/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0738a83037d2a832ca4902110e2a8d45896ea1a5ae102e9127a57fd96f5d3dfc", size = 2040646, upload-time = "2026-07-13T06:34:41.456Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/65f9e1b84b1e56854674beae605a1abe97ecd35a83146b4e8a9ce72badb7/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca757d585d4e235f4f376a43d293db539331cfe7f597b84b405754195f6c1d77", size = 2038019, upload-time = "2026-07-13T06:34:43.13Z" }, - { url = "https://files.pythonhosted.org/packages/12/bf/10c0567b4f3c31d8e8d4fb5dde3a830e2e6446fb47703a9e78f4a48e2f0e/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd763fcd3508d3366bb5c93eadc7f0064b5453c982edac7aed8b0e112f34ae30", size = 2091474, upload-time = "2026-07-13T06:34:44.79Z" }, - { url = "https://files.pythonhosted.org/packages/f5/0a/fdf9a93c8a8d95cc266593e325b8b08618445603f2b4e5308d50e36ee251/qh3-1.9.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42a8e2a3a313d7f32cd1ebc8c29ad42f967e797660a4d7c37f0c1354a48d7d5f", size = 2371394, upload-time = "2026-07-13T06:34:46.477Z" }, - { url = "https://files.pythonhosted.org/packages/28/34/27e2ddb5d3d901b3768c2bfed17f61136bdb71eb03d338d60af470428240/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a292ef84826309e12b530ef95dd37af28029d7976521eac3441812a0ae94761e", size = 2352089, upload-time = "2026-07-13T06:34:48.452Z" }, - { url = "https://files.pythonhosted.org/packages/b1/9e/f1cca3bf6f155a78586985bb3583b0d538d89ca5d3b8e26160abb8020ed1/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a5df03681010f359e21cf9175cfbaf25e637abe970d0eb6448e78ec37abe9b48", size = 2136049, upload-time = "2026-07-13T06:34:50.108Z" }, - { url = "https://files.pythonhosted.org/packages/a8/82/3d57fd79ec778de3110f04f84c982e3c5bfae9c51ec9fd6e387989daec75/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:53cb4a2d96d1f908821589104fc3bcc5521d3fcd0ef7bd91e2301e581a33fdc6", size = 2235239, upload-time = "2026-07-13T06:34:51.813Z" }, - { url = "https://files.pythonhosted.org/packages/70/cd/37c353f0020fe26e24d6f5bc69a93e4c8d8c602e4cdc10bfaf087340b96d/qh3-1.9.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9010ed3f82b8f9c4de43da0ce8691f2a0ccb9fc2d64b080f7d99218a6303667d", size = 2592896, upload-time = "2026-07-13T06:34:53.479Z" }, - { url = "https://files.pythonhosted.org/packages/57/0e/b9dcfb39ad8d40271f422b8371536dc460de52ca2493c3f0f75b9adbe432/qh3-1.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ddf624234a3cd54672fa081fbac250371683e12595a0328c52e6af84dd01a847", size = 2128044, upload-time = "2026-07-13T06:34:55.142Z" }, - { url = "https://files.pythonhosted.org/packages/94/59/8ca93971288d3dfd2492df8cacb8a346f7c977d446284c9ff3ce19654f55/qh3-1.9.4-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9356db6fe28a3368b27479b8a23409074c9a567b2250fc8ff99373929b4ac291", size = 4368393, upload-time = "2026-07-13T06:34:56.802Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ef/2acba8fbeeb77f1092a0ea29c7d055e37150ea2aecaec223266fa69b114c/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d46c0687461468411ced872d199f2c60a9f8163d96622ab9f4737c991e427de", size = 2160060, upload-time = "2026-07-13T06:34:58.64Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6f/00bc375338f5022c06d60ff34e82b9522fd0c79f58392f855b9b8f1a28ea/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6da6de3744fc8db6915f667196bb8cd0bf291f91f3e9a880e2761950558c795f", size = 1862733, upload-time = "2026-07-13T06:35:00.446Z" }, - { url = "https://files.pythonhosted.org/packages/df/2c/7bc75c988d4a1df3dd16e54347dd2ead43609f8eab2071f72f2e681928da/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c111428fcbbb228284e0efab10df49ef3523cf3bf86ca81ed5e694909b7fb616", size = 2036108, upload-time = "2026-07-13T06:35:02.11Z" }, - { url = "https://files.pythonhosted.org/packages/24/ba/ef236210448a327738a1139dae0fca9ff24df2d5a82bd6a105e794c092c8/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7dca54c459d5ff0e5cf16e9bb8c274270377259e0f8ddad534933a42f840dd8d", size = 2037947, upload-time = "2026-07-13T06:35:03.823Z" }, - { url = "https://files.pythonhosted.org/packages/10/d7/2e3e752a94c87cb7634a060e50cf3ff85f1c8c5d9b1e10708f21d6f5c000/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d25e7be545cb549c59db2b14a09214991ccca804e395eab8fcaff258e2991d13", size = 2035584, upload-time = "2026-07-13T06:35:05.709Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3a/2f55f5a9cb3c99335c5950890c97e062bb2f57eb4f725448d72adfe7dc3c/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73028c91f3b5c177ee0b0de699dbc29eb322f9d6a6892d37d2add50c8ff7f0f4", size = 2086910, upload-time = "2026-07-13T06:35:07.638Z" }, - { url = "https://files.pythonhosted.org/packages/dc/91/fcb1337f27610b2f2ec66c7118a754c17053681b22e6d56b527704e6f6f0/qh3-1.9.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c107efab234839fce48f00b9215fb422ed1692176221518b5bf55b10df27ee3", size = 2368581, upload-time = "2026-07-13T06:35:09.476Z" }, - { url = "https://files.pythonhosted.org/packages/dc/74/262e4efe0fdc7f6331997f8bfe8b921754bdc94876f6f795e71d86dea32f/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:28b6caf25e1581328b20ab709eb93290ca73c583e9c2cf873e90f42fbceabb08", size = 2349548, upload-time = "2026-07-13T06:35:11.391Z" }, - { url = "https://files.pythonhosted.org/packages/c2/2b/c74620eff5bc451eeff4e15aa4ba02dc38bce0fa834d1dea5a13bca844b9/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:6a07b90d6519d2265c2fd8a579ef657b9431bc6c7cddc6861b192c89da869e2a", size = 2133554, upload-time = "2026-07-13T06:35:13.088Z" }, - { url = "https://files.pythonhosted.org/packages/a6/9c/92b8fcaad91ab7e7d9c4710bbcf06a6d20289276e737843fc837063349a8/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_i686.whl", hash = "sha256:c53e4e452593c2049ddb6fd3065ccb217c3d2d3648c0611f07d214becc0ce360", size = 2232502, upload-time = "2026-07-13T06:35:14.771Z" }, - { url = "https://files.pythonhosted.org/packages/2f/0e/dfe43082db1d44751eb4e5673aec13d8ff1075bd99aa2f44d6588fcb2b3c/qh3-1.9.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9e09e5c20d92b5fdc6c487c523f4e488bc39c2eec44b940efd196ecf11f37bf8", size = 2588725, upload-time = "2026-07-13T06:35:16.478Z" }, - { url = "https://files.pythonhosted.org/packages/90/e2/58db66254ba98b605249478974be63026d88a72dacbaf67e3258d4e7238d/qh3-1.9.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e95808b3d37d06a1411cfa33ae7c414ea4d01b38921c300634c9109c4e45f1ba", size = 2125025, upload-time = "2026-07-13T06:35:18.399Z" }, -] - [[package]] name = "rich" version = "15.0.0" @@ -1249,8 +896,8 @@ name = "seam" version = "3.0.0b1" source = { editable = "." } dependencies = [ - { name = "dataclasses-json" }, - { name = "niquests" }, + { name = "httpx" }, + { name = "httpx-retries" }, { name = "svix" }, ] @@ -1268,8 +915,8 @@ dev = [ [package.metadata] requires-dist = [ - { name = "dataclasses-json", specifier = ">=0.6.4,<0.7" }, - { name = "niquests", specifier = ">=3.6.4,<4" }, + { name = "httpx", specifier = ">=0.23.0,<1" }, + { name = "httpx-retries", specifier = ">=0.6.0,<1" }, { name = "svix", specifier = ">=1.24.0,<2" }, ] @@ -1416,19 +1063,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] -[[package]] -name = "typing-inspect" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825, upload-time = "2023-05-24T20:25:47.612Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827, upload-time = "2023-05-24T20:25:45.287Z" }, -] - [[package]] name = "typing-inspection" version = "0.4.4" @@ -1441,29 +1075,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, ] -[[package]] -name = "urllib3-future" -version = "2.24.900" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "h11" }, - { name = "jh2" }, - { name = "qh3", marker = "(python_full_version < '3.12' and platform_machine == 'AMD64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'ARM64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'arm64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'armv7l' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'i686' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'ppc64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'ppc64le' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'riscv64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'riscv64gc' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 's390x' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'x86' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'x86_64' and platform_python_implementation == 'PyPy' and sys_platform == 'darwin') or (python_full_version < '3.12' and platform_machine == 'AMD64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'ARM64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'arm64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'armv7l' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'i686' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'ppc64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'ppc64le' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'riscv64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'riscv64gc' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 's390x' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'x86' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'x86_64' and platform_python_implementation == 'PyPy' and sys_platform == 'linux') or (python_full_version < '3.12' and platform_machine == 'AMD64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'ARM64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'aarch64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'arm64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'armv7l' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'i686' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'ppc64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'ppc64le' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'riscv64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'riscv64gc' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 's390x' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'x86' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (python_full_version < '3.12' and platform_machine == 'x86_64' and platform_python_implementation == 'PyPy' and sys_platform == 'win32') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'armv7l' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'i686' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'ppc64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'ppc64le' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'riscv64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'riscv64gc' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 's390x' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'x86' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'armv7l' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'i686' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'ppc64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'ppc64le' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'riscv64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'riscv64gc' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 's390x' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'x86' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'armv7l' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'i686' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ppc64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ppc64le' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'riscv64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'riscv64gc' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 's390x' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'x86' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'win32')" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/9f/590bfa6575e8872829dc65d02c887d0599b3897912479e5ca198660a8c1e/urllib3_future-2.24.900.tar.gz", hash = "sha256:05c2e9d09293ab29a154fed8f5b60644c7cdaffe4ae2587a07b1c7f00f23a7fb", size = 1370419, upload-time = "2026-07-27T06:16:52.572Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/b6/1a7cf7971c979e56ecff5eb76cf8d748d567ef5f7a4fa39fb87adac922e4/urllib3_future-2.24.900-py3-none-any.whl", hash = "sha256:a4fe06b59b0c3ce2fffa821c3de72eaf59b1e61619becc255dee18919ee25260", size = 809879, upload-time = "2026-07-27T06:16:50.77Z" }, -] - -[[package]] -name = "wassima" -version = "2.1.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/a1/714674b53d3a57013730187e027e291c652a25db053e02236798bec49d61/wassima-2.1.3.tar.gz", hash = "sha256:fcd6c38be0f909c393da35cb2a993101fcdcff673b8fa8d5da228f73b630d0d0", size = 140075, upload-time = "2026-07-24T18:36:21.235Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/a2/4c8cd0dddfe40a779739afab2f09e84286bea37cdb73c7b1c12b4990ff12/wassima-2.1.3-py3-none-any.whl", hash = "sha256:f8251c23720dd092117a554b3012756caee3feed6d0122c2458a2aca5d7d92c5", size = 131416, upload-time = "2026-07-24T18:36:19.36Z" }, -] - [[package]] name = "watchdog" version = "6.0.0" From 483371da167f65080482ce536dd9832a5fed35fc Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 21:36:06 -0700 Subject: [PATCH 06/39] feat: Remove lts_version unused --- seam/client.py | 3 +-- seam/constants.py | 2 -- seam/models.py | 3 --- seam/seam.py | 8 +------- seam/seam_without_workspace.py | 8 +------- test/headers_test.py | 5 +---- 6 files changed, 4 insertions(+), 25 deletions(-) diff --git a/seam/client.py b/seam/client.py index fa88416f..72d60568 100644 --- a/seam/client.py +++ b/seam/client.py @@ -6,7 +6,7 @@ from httpx import Response from httpx_retries import Retry, RetryTransport -from .constants import DEFAULT_TIMEOUT, LTS_VERSION +from .constants import DEFAULT_TIMEOUT from .exceptions import ( SeamHttpApiError, SeamHttpInvalidInputError, @@ -16,7 +16,6 @@ SDK_HEADERS = { "seam-sdk-name": "seamapi/python", "seam-sdk-version": version("seam"), - "seam-lts-version": LTS_VERSION, } DEFAULT_RETRIES = Retry() diff --git a/seam/constants.py b/seam/constants.py index 751b277b..ab59913d 100644 --- a/seam/constants.py +++ b/seam/constants.py @@ -1,5 +1,3 @@ -LTS_VERSION = "1.0.0" - DEFAULT_ENDPOINT = "https://connect.getseam.com" DEFAULT_TIMEOUT = 30 diff --git a/seam/models.py b/seam/models.py index 98d96b26..288e2c0f 100644 --- a/seam/models.py +++ b/seam/models.py @@ -7,8 +7,6 @@ class AbstractSeam(AbstractRoutes): - lts_version: str - @abc.abstractmethod def __init__( self, @@ -66,7 +64,6 @@ def list( class AbstractSeamWithoutWorkspace: - lts_version: str wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] @abc.abstractmethod diff --git a/seam/seam.py b/seam/seam.py index bc454fbe..db96efbd 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -2,7 +2,7 @@ from typing_extensions import Self from httpx_retries import Retry -from .constants import DEFAULT_TIMEOUT, LTS_VERSION +from .constants import DEFAULT_TIMEOUT from .parse_options import parse_options from .routes import Routes from .models import AbstractSeam @@ -17,9 +17,6 @@ class Seam(AbstractSeam): Seam API endpoints, including devices, access codes, action_attempts, and more. It supports authentication via API key or personal access token. - :cvar lts_version: The long-term support (LTS) version of the Seam - Python SDK - :vartype lts_version: str :ivar defaults: Default settings for API requests :vartype defaults: Dict[str, Any] :ivar client: The HTTP client used for making API requests @@ -31,8 +28,6 @@ class Seam(AbstractSeam): For more information about the Seam API, visit https://docs.seam.co/ """ - lts_version: str = LTS_VERSION - def __init__( self, api_key: Optional[str] = None, @@ -86,7 +81,6 @@ def __init__( access token format is invalid """ - self.lts_version = Seam.lts_version self.wait_for_action_attempt = wait_for_action_attempt auth_headers, endpoint = parse_options( api_key=api_key, diff --git a/seam/seam_without_workspace.py b/seam/seam_without_workspace.py index 67b3f23c..4a38a9bf 100644 --- a/seam/seam_without_workspace.py +++ b/seam/seam_without_workspace.py @@ -2,7 +2,7 @@ from typing_extensions import Self from httpx_retries import Retry -from .constants import DEFAULT_TIMEOUT, LTS_VERSION +from .constants import DEFAULT_TIMEOUT from .parse_options import parse_without_workspace_options from .client import SeamHttpClient from .models import AbstractSeamWithoutWorkspace @@ -29,9 +29,6 @@ class SeamWithoutWorkspace(AbstractSeamWithoutWorkspace): This class provides methods to authenticate and interact with Seam API endpoints that can operate without being tied to a specific workspace. It supports operations such as creating and listing workspaces. - :cvar lts_version: The long-term support (LTS) version of the Seam - Python SDK - :vartype lts_version: str :ivar wait_for_action_attempt: Controls whether to wait for an action attempt to complete :vartype wait_for_action_attempt: Union[bool, Dict[str, float]] @@ -41,8 +38,6 @@ class SeamWithoutWorkspace(AbstractSeamWithoutWorkspace): :vartype workspaces: WorkspacesProxy """ - lts_version: str = LTS_VERSION - def __init__( self, personal_access_token: Optional[str] = None, @@ -84,7 +79,6 @@ def __init__( :raises SeamInvalidTokenError: If the provided personal access token format is invalid """ - self.lts_version = SeamWithoutWorkspace.lts_version self.wait_for_action_attempt = wait_for_action_attempt auth_headers, endpoint = parse_without_workspace_options( personal_access_token=personal_access_token, diff --git a/test/headers_test.py b/test/headers_test.py index a9f47e8e..7ee6ea06 100644 --- a/test/headers_test.py +++ b/test/headers_test.py @@ -2,7 +2,6 @@ from importlib.metadata import version from seam import Seam -from seam.constants import LTS_VERSION def test_seam_sends_default_headers(recording_server): @@ -23,11 +22,9 @@ def test_seam_sends_default_headers(recording_server): assert request["headers"]["seam-sdk-name"] == "seamapi/python" assert request["headers"]["seam-sdk-version"] == version("seam") - assert request["headers"]["seam-lts-version"] == LTS_VERSION + assert "seam-lts-version" not in request["headers"] assert request["headers"]["authorization"] == "Bearer seam_apikey_token" - assert Seam.lts_version == seam.lts_version - def test_seam_sends_workspace_header_with_personal_access_token(recording_server): device_id = str(uuid.uuid4()) From 4983aec6d8ab0588a3f983bed3041c5bb7131ae3 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 21:42:45 -0700 Subject: [PATCH 07/39] Update to uv v12 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2f9fa36b..30d20fe9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,11 +30,11 @@ dev = [ ] [build-system] -requires = ["uv_build>=0.8.17,<0.9.0"] +requires = ["uv_build>=0.12.0,<0.13.0"] build-backend = "uv_build" [tool.uv] -required-version = ">=0.8.17,<0.9.0" +required-version = ">=0.12.0,<0.13.0" [tool.uv.build-backend] module-root = "" From 6d9f79e97ffaf5d819c9070306ad3776ddcc7776 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 21:50:11 -0700 Subject: [PATCH 08/39] Fix prune workflow job name --- .github/workflows/prune.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prune.yml b/.github/workflows/prune.yml index 562d16cc..1c484837 100644 --- a/.github/workflows/prune.yml +++ b/.github/workflows/prune.yml @@ -7,7 +7,7 @@ on: - cron: '0 15 * * 3' jobs: - tag: + branches: name: Prune Branches runs-on: 'ubuntu-latest' timeout-minutes: 30 From b560ab80f38bf08d630b6d931cf49afb97da3608 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 22:10:53 -0700 Subject: [PATCH 09/39] Handle retry for httpx transports --- seam/client.py | 18 +++++++++++++----- test/timeout_test.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/seam/client.py b/seam/client.py index 72d60568..f1aace5b 100644 --- a/seam/client.py +++ b/seam/client.py @@ -57,17 +57,25 @@ def __init__( } custom_headers = options.pop("headers", {}) - - if "transport" not in options: - options["transport"] = RetryTransport( - retry=DEFAULT_RETRIES if retries is None else retries - ) + self._retry_policy = DEFAULT_RETRIES if retries is None else retries super().__init__(**options) headers = {**auth_headers, **custom_headers, **SDK_HEADERS} self.headers.update(headers) + def _init_transport(self, *args, **kwargs) -> httpx.BaseTransport: + transport = super()._init_transport(*args, **kwargs) + + if kwargs.get("transport") is not None: + return transport + + return RetryTransport(transport=transport, retry=self._retry_policy) + + def _init_proxy_transport(self, *args, **kwargs) -> httpx.BaseTransport: + transport = super()._init_proxy_transport(*args, **kwargs) + return RetryTransport(transport=transport, retry=self._retry_policy) + # request returns the decoded body rather than the Response that # httpx.Client promises, so the verb helpers routed through it have to # say so too. Without these overrides callers see the inherited Response diff --git a/test/timeout_test.py b/test/timeout_test.py index f30f8554..f10a4bd0 100644 --- a/test/timeout_test.py +++ b/test/timeout_test.py @@ -1,10 +1,12 @@ +import ssl import threading import time from contextlib import contextmanager from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import pytest -from httpx import Timeout, TimeoutException +from httpx import HTTPTransport, Limits, Timeout, TimeoutException +from httpx_retries import RetryTransport from seam import Retry, Seam from seam.constants import DEFAULT_TIMEOUT @@ -45,6 +47,45 @@ def test_httpx_options_take_precedence(): assert seam.client.timeout == Timeout(15) +def test_transport_httpx_options_are_applied(): + seam = Seam.from_api_key( + "seam_apikey_token", + httpx_options={ + "limits": Limits(max_connections=25, max_keepalive_connections=20), + "verify": False, + }, + ) + + transport = vars(seam.client)["_transport"] + assert isinstance(transport, RetryTransport) + inner_transport = vars(transport)["_sync_transport"] + assert isinstance(inner_transport, HTTPTransport) + pool = vars(inner_transport)["_pool"] + assert vars(pool)["_max_connections"] == 25 + assert vars(pool)["_ssl_context"].verify_mode == ssl.CERT_NONE + + +def test_environment_proxy_transport_is_used(monkeypatch): + for variable in ( + "ALL_PROXY", + "HTTP_PROXY", + "NO_PROXY", + "all_proxy", + "http_proxy", + "https_proxy", + "no_proxy", + ): + monkeypatch.delenv(variable, raising=False) + monkeypatch.setenv("HTTPS_PROXY", "http://localhost:8080") + + seam = Seam.from_api_key("seam_apikey_token") + + mounts = vars(seam.client)["_mounts"].values() + proxy_transports = [transport for transport in mounts if transport is not None] + assert proxy_transports + assert all(isinstance(transport, RetryTransport) for transport in proxy_transports) + + def test_per_request_timeout_overrides_the_client_timeout(recording_server): with recording_server([(200, {"devices": []})]) as (endpoint, _): seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint, timeout=30) From a6f5f6d5bab357d3aaa6b7c8cd85b185b68bc2d3 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 22:19:11 -0700 Subject: [PATCH 10/39] Remove redundant comments --- codegen/layouts/partials/resource-dataclass.hbs | 4 ---- test/conftest.py | 2 +- test/http_error_test.py | 3 --- test/nested_resource_test.py | 3 --- test/retry_test.py | 2 -- test/timeout_test.py | 4 +--- test/wait_for_action_attempt_test.py | 1 - 7 files changed, 2 insertions(+), 17 deletions(-) diff --git a/codegen/layouts/partials/resource-dataclass.hbs b/codegen/layouts/partials/resource-dataclass.hbs index c0297ba0..7375389b 100644 --- a/codegen/layouts/partials/resource-dataclass.hbs +++ b/codegen/layouts/partials/resource-dataclass.hbs @@ -15,13 +15,9 @@ {{../memberIndent}}{{pythonIdentifier name}}: {{type}} {{/each}} -{{memberIndent}}# The payload is decoded JSON, so every value read out of it is untyped. -{{memberIndent}}# Typing d as Any keeps that at this boundary instead of casting each -{{memberIndent}}# read, and the dataclass fields carry the real types. {{memberIndent}}@classmethod {{memberIndent}}def from_dict(cls, d: Any): {{#unless properties}} -{{memberIndent}} # This shape documents no properties, so there is nothing to read. {{memberIndent}} # pylint: disable=unused-argument {{/unless}} {{memberIndent}} return cls( diff --git a/test/conftest.py b/test/conftest.py index bacc479f..9cdc3ab2 100755 --- a/test/conftest.py +++ b/test/conftest.py @@ -71,7 +71,7 @@ class Handler(BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" # pylint: disable-next=invalid-name - def do_POST(self): # BaseHTTPRequestHandler dispatches on this name. + def do_POST(self): content_length = int(self.headers.get("content-length", 0)) raw_body = self.rfile.read(content_length) diff --git a/test/http_error_test.py b/test/http_error_test.py index 44fe2bb2..4fc1ac46 100644 --- a/test/http_error_test.py +++ b/test/http_error_test.py @@ -62,9 +62,6 @@ def test_seam_http_throws_http_error_on_non_standard_response(server): assert exc_info.value.response.status_code == 503 -# The fake cannot produce malformed error responses, so the recording server -# drives the bodies that must fall through is_api_error_response and raise a -# plain HTTPError rather than being parsed into a SeamHttpApiError. def test_seam_http_raises_http_error_on_non_json_response(recording_server): with recording_server([(500, "Internal Server Error")]) as (endpoint, _): seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) diff --git a/test/nested_resource_test.py b/test/nested_resource_test.py index 5e84488a..74fb7823 100644 --- a/test/nested_resource_test.py +++ b/test/nested_resource_test.py @@ -65,7 +65,6 @@ def test_action_attempt_union_hydrates_nested_result_and_error(): def test_merged_variants_keep_every_variant_field(): result_fields = {f.name for f in dataclasses.fields(ActionAttempt.Result)} - # One field from each of several action attempt variants. assert "was_confirmed_by_device" in result_fields assert "acs_credential_on_encoder" in result_fields assert "instant_key_url" in result_fields @@ -94,7 +93,6 @@ def test_merged_variants_keep_every_variant_field(): def test_merged_variants_recurse_into_nested_objects(): from_fields = {f.name for f in dataclasses.fields(AcsUser.PendingMutations.From)} - # Each of these arrives from a different pending mutation variant. assert "full_name" in from_fields assert "starts_at" in from_fields assert "is_suspended" in from_fields @@ -129,5 +127,4 @@ def test_nested_classes_are_scoped_to_their_owner(): assert "climate_ref" in preset_metadata.__dataclass_fields__ assert "ecobee_device_id" in device_metadata.__dataclass_fields__ - # Nested shapes stay off the module namespace. assert not hasattr(device_module, "DeviceProperties") diff --git a/test/retry_test.py b/test/retry_test.py index 5a6172f8..bdd382bd 100644 --- a/test/retry_test.py +++ b/test/retry_test.py @@ -79,8 +79,6 @@ def test_seam_surfaces_service_unavailable_from_a_workspace_outage(server): assert exc_info.value.response.status_code == 503 -# The policy omits allowed_methods: the SDK must retry its own API -# requests without consumers knowing which HTTP methods those use. def retry_policy(*, total): return Retry( total=total, diff --git a/test/timeout_test.py b/test/timeout_test.py index f10a4bd0..a7aff192 100644 --- a/test/timeout_test.py +++ b/test/timeout_test.py @@ -110,13 +110,11 @@ def test_seam_times_out_a_slow_request(): @contextmanager def slow_server(): - """Serve a response too slowly for the client timeout to tolerate.""" - class Handler(BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" # pylint: disable-next=invalid-name - def do_POST(self): # BaseHTTPRequestHandler dispatches on this name. + def do_POST(self): time.sleep(5) self.send_response(200) self.send_header("content-length", "0") diff --git a/test/wait_for_action_attempt_test.py b/test/wait_for_action_attempt_test.py index 369178a3..c2ea9c4b 100644 --- a/test/wait_for_action_attempt_test.py +++ b/test/wait_for_action_attempt_test.py @@ -77,7 +77,6 @@ def update_action_attempt(): }, ) - # Use Timer to schedule the update after 1 second t = Timer(1.0, update_action_attempt) t.start() From 70b55195c6ff508d3e5cf013cafe89218e9a5f52 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 05:19:50 +0000 Subject: [PATCH 11/39] ci: Generate code --- seam/resources/access_code.py | 27 --- seam/resources/access_grant.py | 24 -- seam/resources/access_method.py | 18 -- seam/resources/acs_access_group.py | 21 -- seam/resources/acs_credential.py | 15 -- seam/resources/acs_encoder.py | 6 - seam/resources/acs_entrance.py | 48 ---- seam/resources/acs_system.py | 15 -- seam/resources/acs_user.py | 27 --- seam/resources/action_attempt.py | 51 ----- seam/resources/batch.py | 3 - seam/resources/client_session.py | 3 - seam/resources/connect_webview.py | 3 - seam/resources/connected_account.py | 24 -- seam/resources/customer_portal.py | 3 - seam/resources/device.py | 244 --------------------- seam/resources/device_provider.py | 3 - seam/resources/instant_key.py | 6 - seam/resources/noise_threshold.py | 3 - seam/resources/pagination.py | 3 - seam/resources/phone.py | 21 -- seam/resources/seam_event.py | 42 ---- seam/resources/space.py | 9 - seam/resources/thermostat_daily_program.py | 6 - seam/resources/thermostat_schedule.py | 6 - seam/resources/unmanaged_access_code.py | 18 -- seam/resources/unmanaged_access_grant.py | 24 -- seam/resources/unmanaged_access_method.py | 18 -- seam/resources/unmanaged_device.py | 27 --- seam/resources/unmanaged_user_identity.py | 9 - seam/resources/user_identity.py | 9 - seam/resources/webhook.py | 3 - seam/resources/workspace.py | 6 - 33 files changed, 745 deletions(-) diff --git a/seam/resources/access_code.py b/seam/resources/access_code.py index 7cb5baba..7991193d 100644 --- a/seam/resources/access_code.py +++ b/seam/resources/access_code.py @@ -95,9 +95,6 @@ class DormakabaOracodeMetadata(ResourceMapping): user_level_id: Optional[str] user_level_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -152,9 +149,6 @@ class ModifiedFields(ResourceMapping): from_: Optional[str] to: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -175,9 +169,6 @@ def from_dict(cls, d: Any): is_device_error: Optional[bool] is_bridge_error: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -230,9 +221,6 @@ class From(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -259,9 +247,6 @@ class To(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -278,9 +263,6 @@ def from_dict(cls, d: Any): from_: Optional[From] to: Optional[To] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -325,9 +307,6 @@ class ModifiedFields(ResourceMapping): from_: Optional[str] to: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -342,9 +321,6 @@ def from_dict(cls, d: Any): change_type: Optional[str] modified_fields: Optional[List[ModifiedFields]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -383,9 +359,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/access_grant.py b/seam/resources/access_grant.py index 4eac1e84..1d442c5f 100644 --- a/seam/resources/access_grant.py +++ b/seam/resources/access_grant.py @@ -66,9 +66,6 @@ class Errors(ResourceMapping): message: str missing_device_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -108,9 +105,6 @@ class From(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -136,9 +130,6 @@ class To(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -155,9 +146,6 @@ def from_dict(cls, d: Any): to: Optional[To] access_method_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -197,9 +185,6 @@ class RequestedAccessMethods(ResourceMapping): instant_key_max_use_count: Optional[int] mode: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -248,9 +233,6 @@ class FailedDevices(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -269,9 +251,6 @@ def from_dict(cls, d: Any): original_code: Optional[str] reason: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -310,9 +289,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/access_method.py b/seam/resources/access_method.py index 595d6611..d7614ada 100644 --- a/seam/resources/access_method.py +++ b/seam/resources/access_method.py @@ -59,9 +59,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -98,9 +95,6 @@ class From(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -123,9 +117,6 @@ class To(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -140,9 +131,6 @@ def from_dict(cls, d: Any): mutation_code: str to: Optional[To] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -175,9 +163,6 @@ class Warnings(ResourceMapping): warning_code: str original_access_method_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -206,9 +191,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/acs_access_group.py b/seam/resources/acs_access_group.py index ca19719f..ca32527b 100644 --- a/seam/resources/acs_access_group.py +++ b/seam/resources/acs_access_group.py @@ -56,9 +56,6 @@ class AccessSchedule(ResourceMapping): ends_at: Optional[str] starts_at: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -81,9 +78,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -131,9 +125,6 @@ class From(ResourceMapping): acs_user_id: Optional[str] acs_entrance_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -164,9 +155,6 @@ class To(ResourceMapping): acs_user_id: Optional[str] acs_entrance_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -185,9 +173,6 @@ def from_dict(cls, d: Any): acs_user_id: Optional[str] variant: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -219,9 +204,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -247,9 +229,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/acs_credential.py b/seam/resources/acs_credential.py index a33ca7b1..99f92b44 100644 --- a/seam/resources/acs_credential.py +++ b/seam/resources/acs_credential.py @@ -95,9 +95,6 @@ class AssaAbloyVostioMetadata(ResourceMapping): key_issuing_request_id: Optional[str] override_guest_acs_entrance_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -125,9 +122,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -166,9 +160,6 @@ class VisionlineMetadata(ResourceMapping): is_valid: Optional[bool] joiner_acs_credential_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -197,9 +188,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -237,9 +225,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/acs_encoder.py b/seam/resources/acs_encoder.py index b048ad95..c1e4f936 100644 --- a/seam/resources/acs_encoder.py +++ b/seam/resources/acs_encoder.py @@ -51,9 +51,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -70,9 +67,6 @@ def from_dict(cls, d: Any): errors: List[Errors] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/acs_entrance.py b/seam/resources/acs_entrance.py index 6c5ccb71..491e6677 100644 --- a/seam/resources/acs_entrance.py +++ b/seam/resources/acs_entrance.py @@ -84,9 +84,6 @@ class Actions(ResourceMapping): id: Optional[str] name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -99,9 +96,6 @@ def from_dict(cls, d: Any): site_id: Optional[str] site_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -132,9 +126,6 @@ class AssaAbloyVostioMetadata(ResourceMapping): pms_id: Optional[str] stand_open: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -171,9 +162,6 @@ class AvigilonAltaMetadata(ResourceMapping): zone_id: Optional[float] zone_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -200,9 +188,6 @@ class BrivoMetadata(ResourceMapping): site_id: Optional[float] site_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -220,9 +205,6 @@ class DormakabaAmbianceMetadata(ResourceMapping): access_point_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -238,9 +220,6 @@ class DormakabaCommunityMetadata(ResourceMapping): access_point_profile: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -262,9 +241,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -287,9 +263,6 @@ class HotekMetadata(ResourceMapping): common_area_number: Optional[str] room_number: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -315,9 +288,6 @@ class LatchMetadata(ResourceMapping): door_type: Optional[str] is_connected: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -356,9 +326,6 @@ class SaltoKsMetadata(ResourceMapping): online: Optional[bool] privacy_mode: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -395,9 +362,6 @@ class SaltoSpaceMetadata(ResourceMapping): room_description: Optional[str] room_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -431,9 +395,6 @@ class Profiles(ResourceMapping): visionline_door_profile_id: Optional[str] visionline_door_profile_type: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -449,9 +410,6 @@ def from_dict(cls, d: Any): door_name: Optional[str] profiles: Optional[List[Profiles]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -475,9 +433,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -512,9 +467,6 @@ def from_dict(cls, d: Any): visionline_metadata: Optional[VisionlineMetadata] warnings: List[Warnings] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/acs_system.py b/seam/resources/acs_system.py index 3c1ebf54..976a5070 100644 --- a/seam/resources/acs_system.py +++ b/seam/resources/acs_system.py @@ -71,9 +71,6 @@ class Errors(ResourceMapping): message: str is_bridge_error: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -92,9 +89,6 @@ class Location(ResourceMapping): time_zone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -116,9 +110,6 @@ class VisionlineMetadata(ResourceMapping): mobile_access_uuid: Optional[str] system_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -144,9 +135,6 @@ class Warnings(ResourceMapping): warning_code: str misconfigured_acs_entrance_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -179,9 +167,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/acs_user.py b/seam/resources/acs_user.py index 18ce9aa2..3c794ab2 100644 --- a/seam/resources/acs_user.py +++ b/seam/resources/acs_user.py @@ -75,9 +75,6 @@ class AccessSchedule(ResourceMapping): ends_at: Optional[str] starts_at: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -100,9 +97,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -161,9 +155,6 @@ class From(ResourceMapping): acs_access_group_id: Optional[str] acs_credential_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -206,9 +197,6 @@ class To(ResourceMapping): acs_access_group_id: Optional[str] acs_credential_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -231,9 +219,6 @@ def from_dict(cls, d: Any): acs_access_group_id: Optional[str] variant: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -260,9 +245,6 @@ class SaltoKsMetadata(ResourceMapping): is_subscribed: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -280,9 +262,6 @@ class SaltoSpaceMetadata(ResourceMapping): audit_openings: Optional[bool] user_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -304,9 +283,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -341,9 +317,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/action_attempt.py b/seam/resources/action_attempt.py index a9bb54b3..b371cad6 100644 --- a/seam/resources/action_attempt.py +++ b/seam/resources/action_attempt.py @@ -29,9 +29,6 @@ class Error(ResourceMapping): message: str type: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -185,9 +182,6 @@ class VisionlineMetadata(ResourceMapping): overwritten: Optional[bool] pending_auto_update: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -212,9 +206,6 @@ def from_dict(cls, d: Any): starts_at: Optional[str] visionline_metadata: Optional[VisionlineMetadata] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -315,9 +306,6 @@ class AssaAbloyVostioMetadata(ResourceMapping): key_issuing_request_id: Optional[str] override_guest_acs_entrance_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -345,9 +333,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -386,9 +371,6 @@ class VisionlineMetadata(ResourceMapping): is_valid: Optional[bool] joiner_acs_credential_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -419,9 +401,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -459,9 +438,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -536,9 +512,6 @@ class Warnings(ResourceMapping): message: Optional[str] original_access_method_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -573,9 +546,6 @@ class AssaAbloyVostioMetadata(ResourceMapping): key_issuing_request_id: Optional[str] override_guest_acs_entrance_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -604,9 +574,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -645,9 +612,6 @@ class VisionlineMetadata(ResourceMapping): is_valid: Optional[bool] joiner_acs_credential_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -686,9 +650,6 @@ class From(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -707,9 +668,6 @@ class To(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -723,9 +681,6 @@ def from_dict(cls, d: Any): mutation_code: str to: Optional[To] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -786,9 +741,6 @@ def from_dict(cls, d: Any): mode: Optional[str] pending_mutations: Optional[List[PendingMutations]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -870,9 +822,6 @@ def from_dict(cls, d: Any): result: Optional[Result] status: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/batch.py b/seam/resources/batch.py index 6e4c2e2b..02ae27b1 100644 --- a/seam/resources/batch.py +++ b/seam/resources/batch.py @@ -159,9 +159,6 @@ class Batch: user_identities: Optional[List[Dict[str, Any]]] workspaces: Optional[List[Dict[str, Any]]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/client_session.py b/seam/resources/client_session.py index 38456094..c4ec26b1 100644 --- a/seam/resources/client_session.py +++ b/seam/resources/client_session.py @@ -53,9 +53,6 @@ class ClientSession: user_identity_ids: List[str] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/connect_webview.py b/seam/resources/connect_webview.py index 77d06b1d..582894a2 100644 --- a/seam/resources/connect_webview.py +++ b/seam/resources/connect_webview.py @@ -76,9 +76,6 @@ class ConnectWebview: wait_for_device_creation: bool workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/connected_account.py b/seam/resources/connected_account.py index a7277447..c27e4e7f 100644 --- a/seam/resources/connected_account.py +++ b/seam/resources/connected_account.py @@ -86,9 +86,6 @@ class Sites(ResourceMapping): site_user_subscription_limit: Optional[int] subscribed_site_user_count: Optional[int] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -104,9 +101,6 @@ def from_dict(cls, d: Any): sites: Optional[List[Sites]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -120,9 +114,6 @@ def from_dict(cls, d: Any): message: str salto_ks_metadata: Optional[SaltoKsMetadata] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -159,9 +150,6 @@ class UserIdentifier(ResourceMapping): phone: Optional[str] username: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -210,9 +198,6 @@ class Sites(ResourceMapping): site_user_subscription_limit: Optional[int] subscribed_site_user_count: Optional[int] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -228,9 +213,6 @@ def from_dict(cls, d: Any): sites: Optional[List[Sites]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -242,9 +224,6 @@ def from_dict(cls, d: Any): warning_code: str salto_ks_metadata: Optional[SaltoKsMetadata] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -277,9 +256,6 @@ def from_dict(cls, d: Any): user_identifier: Optional[UserIdentifier] warnings: List[Warnings] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/customer_portal.py b/seam/resources/customer_portal.py index fa9a7f82..d49625df 100644 --- a/seam/resources/customer_portal.py +++ b/seam/resources/customer_portal.py @@ -28,9 +28,6 @@ class CustomerPortal: url: str workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/device.py b/seam/resources/device.py index 65c2a775..20853259 100644 --- a/seam/resources/device.py +++ b/seam/resources/device.py @@ -98,9 +98,6 @@ class DeviceManufacturer(ResourceMapping): image_url: Optional[str] manufacturer: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -127,9 +124,6 @@ class DeviceProvider(ResourceMapping): image_url: Optional[str] provider_category: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -163,9 +157,6 @@ class Errors(ResourceMapping): message: str is_bridge_error: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -192,9 +183,6 @@ class Location(ResourceMapping): time_zone: Optional[str] timezone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -433,9 +421,6 @@ class Battery(ResourceMapping): level: float - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -445,9 +430,6 @@ def from_dict(cls, d: Any): battery: Optional[Battery] is_connected: bool - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -468,9 +450,6 @@ class Appearance(ResourceMapping): name: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -489,9 +468,6 @@ class Battery(ResourceMapping): level: float status: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -526,9 +502,6 @@ class Model(ResourceMapping): offline_access_codes_supported: Optional[bool] online_access_codes_supported: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -569,9 +542,6 @@ class Endpoints(ResourceMapping): endpoint_id: Optional[str] is_active: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -582,9 +552,6 @@ def from_dict(cls, d: Any): endpoints: Optional[List[Endpoints]] has_active_endpoint: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -603,9 +570,6 @@ class SaltoSpaceCredentialServiceMetadata(ResourceMapping): has_active_phone: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -629,9 +593,6 @@ class AkilesMetadata(ResourceMapping): gadget_name: Optional[str] product_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -670,9 +631,6 @@ class AqaraMetadata(ResourceMapping): position_id: Optional[str] time_zone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -694,9 +652,6 @@ class AssaAbloyVostioMetadata(ResourceMapping): encoder_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -729,9 +684,6 @@ class AugustMetadata(ResourceMapping): lock_name: Optional[str] model: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -770,9 +722,6 @@ class AvigilonAltaMetadata(ResourceMapping): zone_id: Optional[float] zone_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -796,9 +745,6 @@ class BrivoMetadata(ResourceMapping): activation_enabled: Optional[bool] device_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -820,9 +766,6 @@ class ControlbywebMetadata(ResourceMapping): device_name: Optional[str] relay_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -855,12 +798,8 @@ class DormakabaOracodeMetadata(ResourceMapping): class DeviceId(ResourceMapping): """Device ID for a dormakaba Oracode device.""" - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): - # This shape documents no properties, so there is nothing to read. # pylint: disable=unused-argument return cls() @@ -899,9 +838,6 @@ class PredefinedTimeSlots(ResourceMapping): name: Optional[str] prefix: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -930,9 +866,6 @@ def from_dict(cls, d: Any): site_id: Optional[float] site_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -964,9 +897,6 @@ class EcobeeMetadata(ResourceMapping): device_name: Optional[str] ecobee_device_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -989,9 +919,6 @@ class FourSuitesMetadata(ResourceMapping): device_name: Optional[str] reclose_delay_in_seconds: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1011,9 +938,6 @@ class GenieMetadata(ResourceMapping): device_name: Optional[str] door_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1033,9 +957,6 @@ class HoneywellResideoMetadata(ResourceMapping): device_name: Optional[str] honeywell_resideo_device_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1059,9 +980,6 @@ class IglooMetadata(ResourceMapping): device_id: Optional[str] model: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1093,9 +1011,6 @@ class IgloohomeMetadata(ResourceMapping): is_accessory_keypad_linked_to_bridge: Optional[bool] keypad_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1177,9 +1092,6 @@ class KeynestMetadata(ResourceMapping): status_type: Optional[str] subscription_plan: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1223,9 +1135,6 @@ class KisiMetadata(ResourceMapping): lock_name: Optional[str] place_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1262,9 +1171,6 @@ class KorelockMetadata(ResourceMapping): serial_number: Optional[str] wifi_signal_strength: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1291,9 +1197,6 @@ class KwiksetMetadata(ResourceMapping): device_name: Optional[str] model_number: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1316,9 +1219,6 @@ class LocklyMetadata(ResourceMapping): device_name: Optional[str] model: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1363,9 +1263,6 @@ class AccelerometerZ(ResourceMapping): time: Optional[str] value: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1384,9 +1281,6 @@ class Humidity(ResourceMapping): time: Optional[str] value: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1405,9 +1299,6 @@ class Pressure(ResourceMapping): time: Optional[str] value: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1426,9 +1317,6 @@ class Sound(ResourceMapping): time: Optional[str] value: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1448,9 +1336,6 @@ class Temperature(ResourceMapping): time: Optional[str] value: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1464,9 +1349,6 @@ def from_dict(cls, d: Any): sound: Optional[Sound] temperature: Optional[Temperature] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1501,9 +1383,6 @@ def from_dict(cls, d: Any): device_name: Optional[str] latest_sensor_values: Optional[LatestSensorValues] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1533,9 +1412,6 @@ class NestMetadata(ResourceMapping): display_name: Optional[str] nest_device_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1566,9 +1442,6 @@ class NoiseawareMetadata(ResourceMapping): noise_level_decibel: Optional[float] noise_level_nrs: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1600,9 +1473,6 @@ class NukiMetadata(ResourceMapping): keypad_battery_critical: Optional[bool] keypad_paired: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1640,9 +1510,6 @@ class OmnitecMetadata(ResourceMapping): time_zone: Optional[str] timezone_raw_offset_ms: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1666,9 +1533,6 @@ class RingMetadata(ResourceMapping): device_id: Optional[str] device_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1709,9 +1573,6 @@ class SaltoKsMetadata(ResourceMapping): site_id: Optional[str] site_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1758,9 +1619,6 @@ class SaltoMetadata(ResourceMapping): site_id: Optional[str] site_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1788,9 +1646,6 @@ class SchlageMetadata(ResourceMapping): device_name: Optional[str] model: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1813,9 +1668,6 @@ class SeamBridgeMetadata(ResourceMapping): name: Optional[str] unlock_method: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1841,9 +1693,6 @@ class SensiMetadata(ResourceMapping): dual_setpoints_not_supported: Optional[bool] product_type: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1872,9 +1721,6 @@ class SmartthingsMetadata(ResourceMapping): location_id: Optional[str] model: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1895,9 +1741,6 @@ class TadoMetadata(ResourceMapping): device_type: Optional[str] serial_no: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1931,9 +1774,6 @@ class TedeeMetadata(ResourceMapping): keypad_id: Optional[float] serial_number: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -1990,9 +1830,6 @@ class Features(ResourceMapping): unlock_via_gateway: Optional[bool] wifi: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2019,9 +1856,6 @@ class WirelessKeypads(ResourceMapping): wireless_keypad_id: Optional[float] wireless_keypad_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2037,9 +1871,6 @@ def from_dict(cls, d: Any): timezone_raw_offset_ms: Optional[float] wireless_keypads: Optional[List[WirelessKeypads]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2070,9 +1901,6 @@ class TwoNMetadata(ResourceMapping): device_id: Optional[float] device_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2097,9 +1925,6 @@ class UltraloqMetadata(ResourceMapping): device_type: Optional[str] time_zone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2117,9 +1942,6 @@ class VisionlineMetadata(ResourceMapping): encoder_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2155,9 +1977,6 @@ class WyzeMetadata(ResourceMapping): product_name: Optional[str] product_type: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2185,9 +2004,6 @@ class CodeConstraints(ResourceMapping): max_length: Optional[float] min_length: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2204,9 +2020,6 @@ class KeypadBattery(ResourceMapping): level: float - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2249,9 +2062,6 @@ class TimePairs(ResourceMapping): end_time: str start_time: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2269,9 +2079,6 @@ def from_dict(cls, d: Any): time_pairs: Optional[List[TimePairs]] time_zone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2325,9 +2132,6 @@ class TimePairs(ResourceMapping): end_time: str start_time: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2345,9 +2149,6 @@ def from_dict(cls, d: Any): time_pairs: Optional[List[TimePairs]] time_zone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2407,9 +2208,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2430,9 +2228,6 @@ def from_dict(cls, d: Any): thermostat_schedule_id: str workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2501,9 +2296,6 @@ class EcobeeMetadata(ResourceMapping): is_optimized: Optional[bool] owner: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2528,9 +2320,6 @@ def from_dict(cls, d: Any): manual_override_allowed: bool name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2611,9 +2400,6 @@ class EcobeeMetadata(ResourceMapping): is_optimized: Optional[bool] owner: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2638,9 +2424,6 @@ def from_dict(cls, d: Any): manual_override_allowed: Optional[bool] name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2721,9 +2504,6 @@ class EcobeeMetadata(ResourceMapping): is_optimized: Optional[bool] owner: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2748,9 +2528,6 @@ def from_dict(cls, d: Any): manual_override_allowed: Optional[bool] name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2799,9 +2576,6 @@ class TemperatureThreshold(ResourceMapping): upper_limit_celsius: Optional[float] upper_limit_fahrenheit: Optional[float] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2840,9 +2614,6 @@ class Periods(ResourceMapping): climate_preset_key: str starts_at_time: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2857,9 +2628,6 @@ def from_dict(cls, d: Any): thermostat_daily_program_id: str workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -2903,9 +2671,6 @@ class ThermostatWeeklyProgram(ResourceMapping): tuesday_program_id: Optional[str] wednesday_program_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -3028,9 +2793,6 @@ def from_dict(cls, d: Any): thermostat_daily_programs: Optional[List[ThermostatDailyPrograms]] thermostat_weekly_program: Optional[ThermostatWeeklyProgram] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -3438,9 +3200,6 @@ class Warnings(ResourceMapping): active_access_code_count: Optional[int] max_active_access_code_count: Optional[int] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -3491,9 +3250,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/device_provider.py b/seam/resources/device_provider.py index 6bd6a2e7..f58fb9d9 100644 --- a/seam/resources/device_provider.py +++ b/seam/resources/device_provider.py @@ -82,9 +82,6 @@ class DeviceProvider: image_url: str provider_categories: List[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/instant_key.py b/seam/resources/instant_key.py index b7c32d1c..775e8a11 100644 --- a/seam/resources/instant_key.py +++ b/seam/resources/instant_key.py @@ -42,9 +42,6 @@ class Customization(ResourceMapping): primary_color: Optional[str] secondary_color: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -63,9 +60,6 @@ def from_dict(cls, d: Any): user_identity_id: str workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/noise_threshold.py b/seam/resources/noise_threshold.py index d8179713..9918d3c3 100644 --- a/seam/resources/noise_threshold.py +++ b/seam/resources/noise_threshold.py @@ -31,9 +31,6 @@ class NoiseThreshold: noise_threshold_nrs: Optional[float] starts_daily_at: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/pagination.py b/seam/resources/pagination.py index 53949210..b1faa8c7 100644 --- a/seam/resources/pagination.py +++ b/seam/resources/pagination.py @@ -18,9 +18,6 @@ class Pagination: next_page_cursor: Optional[str] next_page_url: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/phone.py b/seam/resources/phone.py index 2c69f137..74b1963b 100644 --- a/seam/resources/phone.py +++ b/seam/resources/phone.py @@ -42,9 +42,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -82,9 +79,6 @@ class Endpoints(ResourceMapping): endpoint_id: Optional[str] is_active: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -95,9 +89,6 @@ def from_dict(cls, d: Any): endpoints: Optional[List[Endpoints]] has_active_endpoint: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -116,9 +107,6 @@ class SaltoSpaceCredentialServiceMetadata(ResourceMapping): has_active_phone: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -132,9 +120,6 @@ def from_dict(cls, d: Any): SaltoSpaceCredentialServiceMetadata ] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -168,9 +153,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -190,9 +172,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/seam_event.py b/seam/resources/seam_event.py index 68fe4395..e9592838 100644 --- a/seam/resources/seam_event.py +++ b/seam/resources/seam_event.py @@ -204,9 +204,6 @@ class ChangedProperties(ResourceMapping): property: str to: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -232,9 +229,6 @@ class From(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -261,9 +255,6 @@ class To(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -288,9 +279,6 @@ class RequestedMutations(ResourceMapping): mutation_code: str to: Optional[Dict[str, Any]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -314,9 +302,6 @@ class AccessCodeErrors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -340,9 +325,6 @@ class AccessCodeWarnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -366,9 +348,6 @@ class ConnectedAccountErrors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -392,9 +371,6 @@ class ConnectedAccountWarnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -418,9 +394,6 @@ class DeviceErrors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -444,9 +417,6 @@ class DeviceWarnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -470,9 +440,6 @@ class AcsSystemErrors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -496,9 +463,6 @@ class AcsSystemWarnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -519,9 +483,6 @@ class Reason(ResourceMapping): message: str reason_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -621,9 +582,6 @@ def from_dict(cls, d: Any): space_id: Optional[str] space_key: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/space.py b/seam/resources/space.py index 629fb423..65e68978 100644 --- a/seam/resources/space.py +++ b/seam/resources/space.py @@ -47,9 +47,6 @@ class CustomerData(ResourceMapping): default_checkout_time: Optional[str] time_zone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -70,9 +67,6 @@ class Geolocation(ResourceMapping): latitude: float longitude: float - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -92,9 +86,6 @@ def from_dict(cls, d: Any): space_key: Optional[str] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/thermostat_daily_program.py b/seam/resources/thermostat_daily_program.py index e6710a01..0c1c262d 100644 --- a/seam/resources/thermostat_daily_program.py +++ b/seam/resources/thermostat_daily_program.py @@ -33,9 +33,6 @@ class Periods(ResourceMapping): climate_preset_key: str starts_at_time: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -50,9 +47,6 @@ def from_dict(cls, d: Any): thermostat_daily_program_id: str workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/thermostat_schedule.py b/seam/resources/thermostat_schedule.py index b51d1dd8..2c022801 100644 --- a/seam/resources/thermostat_schedule.py +++ b/seam/resources/thermostat_schedule.py @@ -45,9 +45,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -68,9 +65,6 @@ def from_dict(cls, d: Any): thermostat_schedule_id: str workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/unmanaged_access_code.py b/seam/resources/unmanaged_access_code.py index 0d39e68d..8c1d00ed 100644 --- a/seam/resources/unmanaged_access_code.py +++ b/seam/resources/unmanaged_access_code.py @@ -81,9 +81,6 @@ class DormakabaOracodeMetadata(ResourceMapping): user_level_id: Optional[str] user_level_name: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -138,9 +135,6 @@ class ModifiedFields(ResourceMapping): from_: Optional[str] to: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -161,9 +155,6 @@ def from_dict(cls, d: Any): is_device_error: Optional[bool] is_bridge_error: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -212,9 +203,6 @@ class ModifiedFields(ResourceMapping): from_: Optional[str] to: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -229,9 +217,6 @@ def from_dict(cls, d: Any): change_type: Optional[str] modified_fields: Optional[List[ModifiedFields]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -262,9 +247,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/unmanaged_access_grant.py b/seam/resources/unmanaged_access_grant.py index 354a3d37..71e7a68e 100644 --- a/seam/resources/unmanaged_access_grant.py +++ b/seam/resources/unmanaged_access_grant.py @@ -58,9 +58,6 @@ class Errors(ResourceMapping): message: str missing_device_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -100,9 +97,6 @@ class From(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -128,9 +122,6 @@ class To(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -147,9 +138,6 @@ def from_dict(cls, d: Any): to: Optional[To] access_method_ids: Optional[List[str]] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -189,9 +177,6 @@ class RequestedAccessMethods(ResourceMapping): instant_key_max_use_count: Optional[int] mode: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -240,9 +225,6 @@ class FailedDevices(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -261,9 +243,6 @@ def from_dict(cls, d: Any): original_code: Optional[str] reason: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -298,9 +277,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/unmanaged_access_method.py b/seam/resources/unmanaged_access_method.py index 0183e341..c30e9a4e 100644 --- a/seam/resources/unmanaged_access_method.py +++ b/seam/resources/unmanaged_access_method.py @@ -53,9 +53,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -92,9 +89,6 @@ class From(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -117,9 +111,6 @@ class To(ResourceMapping): ends_at: Optional[str] starts_at: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -134,9 +125,6 @@ def from_dict(cls, d: Any): mutation_code: str to: Optional[To] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -169,9 +157,6 @@ class Warnings(ResourceMapping): warning_code: str original_access_method_id: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -197,9 +182,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/unmanaged_device.py b/seam/resources/unmanaged_device.py index 273abc48..ad56f5a9 100644 --- a/seam/resources/unmanaged_device.py +++ b/seam/resources/unmanaged_device.py @@ -97,9 +97,6 @@ class Errors(ResourceMapping): message: str is_bridge_error: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -126,9 +123,6 @@ class Location(ResourceMapping): time_zone: Optional[str] timezone: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -181,9 +175,6 @@ class Battery(ResourceMapping): level: float - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -193,9 +184,6 @@ def from_dict(cls, d: Any): battery: Optional[Battery] is_connected: bool - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -219,9 +207,6 @@ class Battery(ResourceMapping): level: float status: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -256,9 +241,6 @@ class Model(ResourceMapping): offline_access_codes_supported: Optional[bool] online_access_codes_supported: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -291,9 +273,6 @@ def from_dict(cls, d: Any): online: bool online_access_codes_enabled: Optional[bool] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -345,9 +324,6 @@ class Warnings(ResourceMapping): active_access_code_count: Optional[int] max_active_access_code_count: Optional[int] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -393,9 +369,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/unmanaged_user_identity.py b/seam/resources/unmanaged_user_identity.py index 2831e4c6..6905f5af 100644 --- a/seam/resources/unmanaged_user_identity.py +++ b/seam/resources/unmanaged_user_identity.py @@ -49,9 +49,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -77,9 +74,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -99,9 +93,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/user_identity.py b/seam/resources/user_identity.py index 8d9e5751..141a82a3 100644 --- a/seam/resources/user_identity.py +++ b/seam/resources/user_identity.py @@ -51,9 +51,6 @@ class Errors(ResourceMapping): error_code: str message: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -79,9 +76,6 @@ class Warnings(ResourceMapping): message: str warning_code: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -102,9 +96,6 @@ def from_dict(cls, d: Any): warnings: List[Warnings] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/webhook.py b/seam/resources/webhook.py index 75353745..137e031b 100644 --- a/seam/resources/webhook.py +++ b/seam/resources/webhook.py @@ -21,9 +21,6 @@ class Webhook: url: str webhook_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( diff --git a/seam/resources/workspace.py b/seam/resources/workspace.py index 06c9eb1b..bbc0e01e 100644 --- a/seam/resources/workspace.py +++ b/seam/resources/workspace.py @@ -49,9 +49,6 @@ class ConnectWebviewCustomization(ResourceMapping): primary_button_text_color: Optional[str] success_message: Optional[str] - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( @@ -73,9 +70,6 @@ def from_dict(cls, d: Any): publishable_key: Optional[str] workspace_id: str - # The payload is decoded JSON, so every value read out of it is untyped. - # Typing d as Any keeps that at this boundary instead of casting each - # read, and the dataclass fields carry the real types. @classmethod def from_dict(cls, d: Any): return cls( From 6ac5168616e621298b0fdfe9e56c80b8bea17246 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 22:31:19 -0700 Subject: [PATCH 12/39] Make retry behavior explicit --- README.rst | 10 ++++++++++ seam/client.py | 8 +++++++- test/retry_test.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3c6d2289..a031c5ce 100644 --- a/README.rst +++ b/README.rst @@ -477,6 +477,16 @@ A request that exceeds the timeout raises ``httpx.TimeoutException``. Configuring retries ^^^^^^^^^^^^^^^^^^^ +By default, the SDK makes up to three attempts: the initial request and two +retries. Retries are limited to ``GET``, ``HEAD``, ``OPTIONS``, ``PUT``, and +``DELETE`` requests that fail because of a transport error, timeout, HTTP 429 +response, or HTTP 5xx response. ``POST`` and ``PATCH`` requests are not retried. + +Retries use exponential backoff with jitter: approximately 200–240 ms before +the first retry and 400–480 ms before the second. A ``Retry-After`` header is +honored instead of the calculated backoff. The request timeout is reset for +each attempt. + Pass the ``retries`` option to configure retry behavior. Retries are handled by `httpx-retries `_, and its ``Retry`` class is re-exported from ``seam`` for convenience: diff --git a/seam/client.py b/seam/client.py index f1aace5b..e180e1a8 100644 --- a/seam/client.py +++ b/seam/client.py @@ -18,7 +18,13 @@ "seam-sdk-version": version("seam"), } -DEFAULT_RETRIES = Retry() +DEFAULT_RETRIES = Retry( + total=2, + allowed_methods=["GET", "HEAD", "OPTIONS", "PUT", "DELETE"], + status_forcelist=[429, *range(500, 600)], + backoff_factor=0.12, + backoff_jitter=1 / 6, +) class AbstractSeamHttpClient(abc.ABC): diff --git a/test/retry_test.py b/test/retry_test.py index bdd382bd..e78d1e3a 100644 --- a/test/retry_test.py +++ b/test/retry_test.py @@ -2,11 +2,41 @@ from httpx import HTTPStatusError from seam import Retry, Seam +from seam.client import DEFAULT_RETRIES SERVICE_UNAVAILABLE = (503, "Service Unavailable") DEVICES = (200, {"devices": [{"device_id": "august_device_1"}]}) +def test_default_retry_policy_matches_the_standard_policy(): + assert DEFAULT_RETRIES.total == 2 + assert DEFAULT_RETRIES.allowed_methods == { + "GET", + "HEAD", + "OPTIONS", + "PUT", + "DELETE", + } + assert DEFAULT_RETRIES.status_forcelist == {429, *range(500, 600)} + assert DEFAULT_RETRIES.backoff_factor == 0.12 + assert DEFAULT_RETRIES.backoff_jitter == pytest.approx(1 / 6) + + +def test_default_retry_backoff_ranges(monkeypatch): + monkeypatch.setattr("httpx_retries.retry.random.uniform", lambda low, high: low) + + first_retry = DEFAULT_RETRIES.increment() + second_retry = first_retry.increment() + + assert first_retry.backoff_strategy() == pytest.approx(0.2) + assert second_retry.backoff_strategy() == pytest.approx(0.4) + + monkeypatch.setattr("httpx_retries.retry.random.uniform", lambda low, high: high) + + assert first_retry.backoff_strategy() == pytest.approx(0.24) + assert second_retry.backoff_strategy() == pytest.approx(0.48) + + # The retries option has no effect on API requests because the Seam API # uses POST, which httpx-retries does not treat as retryable. A follow-up # PR will apply the retry policy to API requests without exposing the From 05a26ebaddd42ea5e95408fcfcc426f0a6eac344 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 22:47:12 -0700 Subject: [PATCH 13/39] Fix known failing test --- test/conftest.py | 7 +++++++ test/retry_test.py | 15 ++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 9cdc3ab2..39a47693 100755 --- a/test/conftest.py +++ b/test/conftest.py @@ -70,8 +70,15 @@ def recording_server(responses): class Handler(BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" + # pylint: disable-next=invalid-name + def do_GET(self): + self._handle_request() + # pylint: disable-next=invalid-name def do_POST(self): + self._handle_request() + + def _handle_request(self): content_length = int(self.headers.get("content-length", 0)) raw_body = self.rfile.read(content_length) diff --git a/test/retry_test.py b/test/retry_test.py index e78d1e3a..a9ebdd0c 100644 --- a/test/retry_test.py +++ b/test/retry_test.py @@ -37,11 +37,6 @@ def test_default_retry_backoff_ranges(monkeypatch): assert second_retry.backoff_strategy() == pytest.approx(0.48) -# The retries option has no effect on API requests because the Seam API -# uses POST, which httpx-retries does not treat as retryable. A follow-up -# PR will apply the retry policy to API requests without exposing the -# HTTP method in the SDK's public API, then remove the xfail markers. -@pytest.mark.xfail(reason="TODO: Apply the retry policy to API requests") def test_seam_retries_service_unavailable_responses(recording_server): expected_retry_count = 2 responses = [SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE, DEVICES] @@ -52,13 +47,13 @@ def test_seam_retries_service_unavailable_responses(recording_server): endpoint=endpoint, retries=retry_policy(total=expected_retry_count), ) - devices = seam.devices.list() + # TODO: Use seam.devices.list() once the generated SDK route uses GET. + devices = seam.client.get("/devices/list")["devices"] assert len(devices) == 1 assert len(requests) == expected_retry_count + 1 -@pytest.mark.xfail(reason="TODO: Apply the retry policy to API requests") def test_seam_stops_retrying_once_retries_are_exhausted(recording_server): expected_retry_count = 1 @@ -70,7 +65,8 @@ def test_seam_stops_retrying_once_retries_are_exhausted(recording_server): ) with pytest.raises(HTTPStatusError) as exc_info: - seam.devices.list() + # TODO: Use seam.devices.list() once the generated SDK route uses GET. + seam.client.get("/devices/list") assert exc_info.value.response.status_code == 503 assert len(requests) == expected_retry_count + 1 @@ -83,7 +79,8 @@ def test_seam_does_not_retry_when_retries_are_disabled(recording_server): ) with pytest.raises(HTTPStatusError) as exc_info: - seam.devices.list() + # TODO: Use seam.devices.list() once the generated SDK route uses GET. + seam.client.get("/devices/list") assert exc_info.value.response.status_code == 503 assert len(requests) == 1 From e03e0f9cdb04cf2bfca279f10f97563b13258f84 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 22:58:41 -0700 Subject: [PATCH 14/39] Drop support for python 3.11 --- .devcontainer/devcontainer.json | 2 +- .github/actions/setup/action.yml | 2 +- .github/workflows/_build.yml | 2 +- .github/workflows/check.yml | 11 +++- .python-version | 2 +- pyproject.toml | 2 +- uv.lock | 98 +------------------------------- 7 files changed, 16 insertions(+), 103 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a70bd664..2f9e5640 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "args": { "NODE_VERSION": "24", "UV_VERSION": "0.8.17", - "VARIANT": "3.12" + "VARIANT": "3.14" } }, "remoteEnv": { diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 518622cb..e6a5cbdb 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -6,7 +6,7 @@ inputs: python_version: description: The Python version. required: false - default: '3.12' + default: '3.14' just_version: description: The just version. required: false diff --git a/.github/workflows/_build.yml b/.github/workflows/_build.yml index 8324ef2f..7717e29d 100644 --- a/.github/workflows/_build.yml +++ b/.github/workflows/_build.yml @@ -8,7 +8,7 @@ on: description: The Python version. type: string required: false - default: '3.12' + default: '3.14' runs_on: description: The runner environment. type: string diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f756a8dd..bf7c3fb2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,9 +20,10 @@ jobs: os: - ubuntu-latest python: - - '3.10' - '3.11' - '3.12' + - '3.13' + - '3.14' include: - os: ubuntu-latest os_name: Linux @@ -47,6 +48,8 @@ jobs: python: - '3.11' - '3.12' + - '3.13' + - '3.14' steps: - name: Checkout uses: actions/checkout@v7 @@ -69,9 +72,10 @@ jobs: os: - ubuntu-latest python: - - '3.10' - '3.11' - '3.12' + - '3.13' + - '3.14' include: - os: ubuntu-latest os_name: Linux @@ -86,9 +90,10 @@ jobs: os: - ubuntu-latest python: - - '3.10' - '3.11' - '3.12' + - '3.13' + - '3.14' include: - os: ubuntu-latest os_name: Linux diff --git a/.python-version b/.python-version index 8531a3b7..6324d401 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.12.2 +3.14 diff --git a/pyproject.toml b/pyproject.toml index 30d20fe9..b97ba651 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" license-files = ["LICENSE.txt"] readme = "README.rst" -requires-python = ">=3.10" +requires-python = ">=3.11" dependencies = [ "httpx>=0.23.0,<1", "httpx-retries>=0.6.0,<1", diff --git a/uv.lock b/uv.lock index cbf0a220..1d822959 100644 --- a/uv.lock +++ b/uv.lock @@ -1,11 +1,9 @@ version = 1 revision = 3 -requires-python = ">=3.10" +requires-python = ">=3.11" resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version >= '3.12' and python_full_version < '3.15'", - "python_full_version == '3.11.*'", - "python_full_version < '3.11'", + "python_full_version >= '3.12'", + "python_full_version < '3.12'", ] [[package]] @@ -31,7 +29,6 @@ name = "anyio" version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] @@ -44,9 +41,6 @@ wheels = [ name = "astroid" version = "3.2.4" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576, upload-time = "2024-07-20T12:57:43.26Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348, upload-time = "2024-07-20T12:57:40.886Z" }, @@ -71,15 +65,9 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/47/c9997eb470a7f48f7aaddd3d9a828244a2e4199569e38128715c48059ac1/black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d", size = 642299, upload-time = "2024-04-26T00:32:15.305Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/f6/3adc48c210527a7b651aaed43824a9b8bd04b3fb361a5227bad046e1c876/black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce", size = 1631487, upload-time = "2024-04-26T00:40:28.969Z" }, - { url = "https://files.pythonhosted.org/packages/a2/25/70aa1bec12c841a03e333e312daa0cf2fee50ea6336ac4851c93c0e2b411/black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021", size = 1456317, upload-time = "2024-04-26T00:39:10.333Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/7f8df0fdbbbefc4362d3eca6b69b7a8a4249a8a88dabc00a207d31fddcd7/black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063", size = 1822765, upload-time = "2024-04-26T00:34:56.436Z" }, - { url = "https://files.pythonhosted.org/packages/5c/21/1ee97841c469c1551133cbe47448cdba9628c7d9431f74f114f02e3b233c/black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96", size = 1409336, upload-time = "2024-04-26T00:35:30.392Z" }, { url = "https://files.pythonhosted.org/packages/9b/f7/591d601c3046ceb65b97291dfe87fa25124cffac3d97aaaba89d0f0d7bdf/black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474", size = 1615013, upload-time = "2024-04-26T00:39:49.415Z" }, { url = "https://files.pythonhosted.org/packages/c9/17/5e0036b265bbf6bc44970d93d48febcbc03701b671db3c9603fd43ebc616/black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c", size = 1436163, upload-time = "2024-04-26T00:40:20.267Z" }, { url = "https://files.pythonhosted.org/packages/c5/48/34176b522e8cff4620a5d96c2e323ff2413f574870eb25efa8025885e028/black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb", size = 1803382, upload-time = "2024-04-26T00:34:38.665Z" }, @@ -127,20 +115,6 @@ version = "7.15.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/be/c3/4f2195f512fb172aa425a8803a874b2baa9ba7f80ff7b6080998761fc701/coverage-7.15.4.tar.gz", hash = "sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00", size = 936952, upload-time = "2026-08-06T13:50:24.442Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/70/b052a519a584663a7bd052841a2debe11c8309ec49a7786340003f9c0a02/coverage-7.15.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d0be6daac4cce6b8c8dc65886bae1b082ddbca4da8e5cbb5e15166acf253e264", size = 222245, upload-time = "2026-08-06T13:46:55.253Z" }, - { url = "https://files.pythonhosted.org/packages/67/39/892fa511aba3d1c3c8f49509a0ff5c71eab9f9f88d08e1a38da395821660/coverage-7.15.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b24e078eabcd6a9caa8b0713f9bc1eeb310bcc960a29d45a3b4fcd4b16d5b11d", size = 222762, upload-time = "2026-08-06T13:46:57.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/95/b2c724ce1e64bc23cb5b1d7eeffa9548dc3d811f7a6297b2d01607f4e062/coverage-7.15.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe20cc8cf8821d4fe54f89106cbf06aa27f37b5bbe3535568065a81539b4150", size = 249498, upload-time = "2026-08-06T13:46:59.012Z" }, - { url = "https://files.pythonhosted.org/packages/0b/4f/b1973f67a1382af65b572a31ed692f8e490a6ad707191eab59148376832a/coverage-7.15.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:83cf06cdd687677742caff1a9134833b7a8b75f111519d2cb0e0ba1b9a851e15", size = 251328, upload-time = "2026-08-06T13:47:00.764Z" }, - { url = "https://files.pythonhosted.org/packages/a2/09/03efa6722a132abcac91b32a60b64b240dd707c189c64eee697e48992c96/coverage-7.15.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8fa4de68e2a752468ff14b4e15db7def689a71be759e826a31ccecbef69c5fd0", size = 253194, upload-time = "2026-08-06T13:47:01.976Z" }, - { url = "https://files.pythonhosted.org/packages/45/63/8299201d9c80fb65551ce99c966cab83d706ec4066ac999bef08201346de/coverage-7.15.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4dff9daa47d83120c3ec38ce921214242944a832aa04e903e50b5b7ebac8972d", size = 255106, upload-time = "2026-08-06T13:47:03.281Z" }, - { url = "https://files.pythonhosted.org/packages/ee/16/26fd8a691eb8d9a230128685f6d23309d7402cb030aa553001788c8c50fc/coverage-7.15.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a093fd37229918976f602aa07aa59e0973cde82186f220c8e197f721f5be0ce4", size = 250177, upload-time = "2026-08-06T13:47:04.713Z" }, - { url = "https://files.pythonhosted.org/packages/ad/ef/3c7556f33783a0a566e01443ca62bd8eb2cdfe22d271efdc02e08beb5654/coverage-7.15.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:317db01a2cb02552fd67e2b1cca77a4b528a2a277176c5e0bf2cecbb639d3f54", size = 251234, upload-time = "2026-08-06T13:47:06.104Z" }, - { url = "https://files.pythonhosted.org/packages/29/49/640a34043edac950738f36a3567832db5731d4cb2ed84b59cdb89c6bccbf/coverage-7.15.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8ee3838dcb656602c3b51e16aed9bfb0822f8d8d6d1c5966d32ec8c104be8e20", size = 249237, upload-time = "2026-08-06T13:47:07.467Z" }, - { url = "https://files.pythonhosted.org/packages/48/f5/e80f212669dd1be954ff844f883ef11a437ef4fd0089c6e0effc7b66b15d/coverage-7.15.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:425920379052ff1fe465268f3361d35804a241bbdd5a1b592c8cb60df4c52325", size = 253050, upload-time = "2026-08-06T13:47:08.748Z" }, - { url = "https://files.pythonhosted.org/packages/c7/e9/e5da0fe39f7fde1bca9edc09c60921bb5fdba4cec7db5bbad41ddfd8c230/coverage-7.15.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:69bb2400abef928e365ea7d4d9925169ada78ed2295546780002d4b65de3df88", size = 249508, upload-time = "2026-08-06T13:47:10.072Z" }, - { url = "https://files.pythonhosted.org/packages/7d/38/41bf25774a0c8bba6b467f917cb1c9a0a2605e02dc93aad489fc7050ed59/coverage-7.15.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:81661f82d302484e3119e7c80c519c02fa9bcc2a6b339baf67d67bc89c580f04", size = 250110, upload-time = "2026-08-06T13:47:11.35Z" }, - { url = "https://files.pythonhosted.org/packages/89/6e/26f2e54b79acc29d179ee4272922625aedb69198c4eb61f7ff4f098f3c78/coverage-7.15.4-cp310-cp310-win32.whl", hash = "sha256:cb476b2e828ecb71cb6b6a928d23fd20a7ddb501188022dae1c37499149cc338", size = 224294, upload-time = "2026-08-06T13:47:12.753Z" }, - { url = "https://files.pythonhosted.org/packages/7b/06/9a318fc3ae040d4d6cb2d86101c6aa963fab20899a5c58666adf52cde0ca/coverage-7.15.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fc2130bf37df31852a8384f12601563a45a0024bccc6624f38355cba7a8d360", size = 224919, upload-time = "2026-08-06T13:47:14.17Z" }, { url = "https://files.pythonhosted.org/packages/2a/66/edcec7d7a0b524aa8923e22925fde6fe50ce005a113dca13ae1581455c4c/coverage-7.15.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbac5abad70df71019988f83f26ac7092ff2642975def4429e98dc7585ef3490", size = 222367, upload-time = "2026-08-06T13:47:15.578Z" }, { url = "https://files.pythonhosted.org/packages/e6/c6/ab8de429e2e8548faf58ec7e1674a4ce00414b4113942d3fe87109cf0f68/coverage-7.15.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:357a173465c7ce028d07a95cc2b63b5bf59f50ecdd5ad75c5cbb78ada984048e", size = 222874, upload-time = "2026-08-06T13:47:16.961Z" }, { url = "https://files.pythonhosted.org/packages/be/c4/3b7b49587e8a6b9af79b3eb468d443d6042b6d65b47aa26586846a0d6566/coverage-7.15.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:21b803935e2efc3acebe9697197a294fccf5dc4e5382bd6369542ff7a7d2a1d7", size = 253287, upload-time = "2026-08-06T13:47:18.291Z" }, @@ -290,18 +264,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - [[package]] name = "h11" version = "0.16.0" @@ -384,20 +346,6 @@ version = "0.15.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/12/e2e9ca532cf5a0e08c9489826c4a35c6958c92ba0313fda70e8c6c3912be/librt-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1a49adf16a7c9d9646816c2946135527197b6fcf4347c7b8b761cf1bfbf4489", size = 148673, upload-time = "2026-08-07T10:46:22.569Z" }, - { url = "https://files.pythonhosted.org/packages/6d/7c/02005e23478bd5950618d9712e0fd2b4c511657857f3efd8ba6a5feabcdd/librt-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81a398f45b45a59200e13cd5ad1ae1d3f44334de98b148331afe2cdfee701c52", size = 153547, upload-time = "2026-08-07T10:46:23.931Z" }, - { url = "https://files.pythonhosted.org/packages/a0/90/d8848a735f5642077fc4b3b4bebcdb08edf10178e3add45597f5201a368f/librt-0.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4eafbaff06b9563f8b1c850621ce51605de05208e09d4d71ce490bc972b7b9e8", size = 494355, upload-time = "2026-08-07T10:46:25.122Z" }, - { url = "https://files.pythonhosted.org/packages/e1/0b/8604f41ea02feace490e9e405a338a15f9905369f55b239a9ce31c946f24/librt-0.15.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b0411b4066db926b80258c60dcb0e6db4c9cee312eab45b7e8866b17ddf9ada1", size = 485459, upload-time = "2026-08-07T10:46:26.447Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ac/84153bda1ce0da609182527ab92b40d961809e544eefdc5a1c2422971416/librt-0.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d", size = 498398, upload-time = "2026-08-07T10:46:27.701Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3a/5ca6cd282b2c244bec8ec84102e09773264e9c02891d56ab3a8f0e4d7083/librt-0.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b230acc1c3bfe2d6f2627ba2b95dc92e58aa494600e9722d0e6ccbc931e59702", size = 515474, upload-time = "2026-08-07T10:46:28.9Z" }, - { url = "https://files.pythonhosted.org/packages/73/d3/bd34110234779eb843c6ed66aba7c9b2091d3dd85989f1fb9922f564cb7a/librt-0.15.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6da110e5f314c19ab8478464d02ae18808ae73d522c15260fa4918acdcd64da9", size = 509484, upload-time = "2026-08-07T10:46:30.124Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/43c3f7f071d71631a7daa3b835ef2168ea39f20692d81464d4e47fbaa6d6/librt-0.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eab9208b00ca55bf75983ec99f7bf13acc746a36102e98953addaad7f7ea1e1b", size = 532534, upload-time = "2026-08-07T10:46:31.511Z" }, - { url = "https://files.pythonhosted.org/packages/c5/1c/b854adf036ea817c40408873a5b794d65a91d9f0f39826f2ad2a2d5d7f48/librt-0.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c013cd3a1721e69e14380ada97eaa4b7b0cdf1c6b96fa765d4ea47c875088db", size = 537087, upload-time = "2026-08-07T10:46:32.734Z" }, - { url = "https://files.pythonhosted.org/packages/25/5c/c9a890e244e7dd725d3bd8b560e41f0aec787eaf343b46956a290ab7b841/librt-0.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:567b1c430f8bd560e689421468278ac5941bab4a05303b5d95b6ae10db03f451", size = 536575, upload-time = "2026-08-07T10:46:33.965Z" }, - { url = "https://files.pythonhosted.org/packages/5f/c5/c8e70b60b704299555f55db468eb46b1c81bfc60201ffbfe20407d89870c/librt-0.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:29c4cab9df457b19672c39be7f384ebb2bc925c4e2684b8780c222b43eb36389", size = 517142, upload-time = "2026-08-07T10:46:35.577Z" }, - { url = "https://files.pythonhosted.org/packages/56/d1/767a90c41f5d381b3195bc88ac0ec4afda35777c9c781e1f9848fedd965e/librt-0.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bccbd8e5b0bffb7106cf18eb1baa3d7194b1cebb3b4b1cdbd4bdb19382a6ee6c", size = 558714, upload-time = "2026-08-07T10:46:36.829Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b4/3c0624b8dc8301ab808f2b3a910995bcabe28df070fb9a0e5505ae997dae/librt-0.15.0-cp310-cp310-win32.whl", hash = "sha256:8ae493ed5f659a7761c43d42f183db514536073ded9bcf671d2d1df47e29a07e", size = 104426, upload-time = "2026-08-07T10:46:38.594Z" }, - { url = "https://files.pythonhosted.org/packages/31/98/e91c0382304bedb2db9c6801897319a9dcb68daac5e975819b562362f20d/librt-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc25fb356d0c7810bb49ff3df908ad1fda6995d660ab099ded69244ed7ab6053", size = 125057, upload-time = "2026-08-07T10:46:40.052Z" }, { url = "https://files.pythonhosted.org/packages/59/52/06790ced2ac7117f890c21bda43c39c958ec82aa665c0718e821d33ff939/librt-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823b92cf3c18ecd08afc70c42473888b41b6e8ef5046f3b82c05c154a2fa3d22", size = 148039, upload-time = "2026-08-07T10:46:41.165Z" }, { url = "https://files.pythonhosted.org/packages/e7/1d/8e150b7fc449a1f33c8a760965cc1f43b14fc1577d9d0b50ab2701420e74/librt-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70bc1b602cf59917e8f0c7a2cbc8bcc6fbc14d5486136b00707a79619121d63", size = 153067, upload-time = "2026-08-07T10:46:42.418Z" }, { url = "https://files.pythonhosted.org/packages/51/87/a162bc5a66a35599dc619ecb215145f4de7d68e886b479b6d12593139f7c/librt-0.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:814ff83a25b5fce8b9c80c4dd803153fb5c5599fc74db9e022466938368957ef", size = 493087, upload-time = "2026-08-07T10:46:43.657Z" }, @@ -546,17 +494,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" }, @@ -653,20 +594,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, - { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, - { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, - { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, - { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, - { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, - { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, - { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, - { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, - { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, - { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, @@ -780,7 +707,6 @@ dependencies = [ { name = "isort" }, { name = "mccabe" }, { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomlkit" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0c/4c/b561478a1ccb91e9b02965cb999d2281894d43e68c0bf3777d023af15f11/pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8", size = 1505895, upload-time = "2024-05-20T07:22:43.252Z" } @@ -794,12 +720,10 @@ version = "8.4.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/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ @@ -1081,9 +1005,6 @@ 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/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, - { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, - { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, @@ -1093,8 +1014,6 @@ wheels = [ { 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/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, { 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" }, @@ -1113,17 +1032,6 @@ version = "2.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/2b/b0/c1f5a970721f06b85c0cd5142e0ff8fe067708abd779b0c4f4be7d61d09f/wrapt-2.3.0.tar.gz", hash = "sha256:681a2d0eefd721998f90642762b8e75c2159ec531b20ad5e437245ea7b06a107", size = 131509, upload-time = "2026-07-28T06:06:14.895Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/31/5822ce37ca8820c2ed35a498c67c8b37960b9cee2ba437fd32849d0a234c/wrapt-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0bb2797048db0956348cb3058c33bc4184614f13231389cfbccc16a5d32780a7", size = 81191, upload-time = "2026-07-28T06:04:04.858Z" }, - { url = "https://files.pythonhosted.org/packages/7a/5a/3c6117938be98754578ab83f5a40d7d0ea2cd2c487dc5cd6027ee7228229/wrapt-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce9f398f868d2b3b27aa2ea4de79645ef9077aeeac8dfc2814b0d542c6a2b87f", size = 82255, upload-time = "2026-07-28T06:04:07.151Z" }, - { url = "https://files.pythonhosted.org/packages/a5/0f/94ae724c5087eb6054c0d63febd7094947dcf302fe058e2e0488102a872b/wrapt-2.3.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad71df7a04dd3497e9302e81f4a7c91bd401ea0e15a9df9029527900f94bee43", size = 155228, upload-time = "2026-07-28T06:04:08.272Z" }, - { url = "https://files.pythonhosted.org/packages/6c/21/1f780bba935dcf697c0c59de9be3a559bbb8e31a53ca3f25422023738432/wrapt-2.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc82c2ccc8e234c844f5303d9f2984b346dcdd53e94823ce8420d2c75b4b9023", size = 157073, upload-time = "2026-07-28T06:04:09.459Z" }, - { url = "https://files.pythonhosted.org/packages/73/31/6c7799d7b6431fcd7e1b83245fb45258a2d2c3a2187fbaecb83572a72d7a/wrapt-2.3.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6e19531ae33c508cea7d84a7edfda01fa86e51b8d1a93a77712c55e6e469152", size = 151594, upload-time = "2026-07-28T06:04:10.784Z" }, - { url = "https://files.pythonhosted.org/packages/ce/17/42d670dbfafd49076c6eb2b7d67633d7e1c968e39bfb11a135acb6fac67b/wrapt-2.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:df4ce31150bcd5d9f36f816aac3010ab4f4bf8672ac1d3b0ac7d539ec61c7c02", size = 156069, upload-time = "2026-07-28T06:04:12.316Z" }, - { url = "https://files.pythonhosted.org/packages/1f/d6/c66b4ba4eda49257c84d5c2df26118280f09ca7905aee20d0064db778d13/wrapt-2.3.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e2e692bc0d63f881cf7006730a56bd4e0c2fab5dc318466942805d692b166276", size = 150930, upload-time = "2026-07-28T06:04:13.482Z" }, - { url = "https://files.pythonhosted.org/packages/c0/f2/1a3b949c0322fb27396eafd1044328c1cb0400e0b32105d75a3cd03096e7/wrapt-2.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c8388ba7faf5dbf9ee106bb70d66f257629b1bd98091123e19e8a4553a319199", size = 154525, upload-time = "2026-07-28T06:04:14.698Z" }, - { url = "https://files.pythonhosted.org/packages/12/65/147563a3dfa6e830c857b93b530ebd8c0cd9d540e5914aec8f9b12880c02/wrapt-2.3.0-cp310-cp310-win32.whl", hash = "sha256:e045ff75d7d94900fc32896ed93c45ce2d2cac28c9dead582ff9a5a49d446e35", size = 77879, upload-time = "2026-07-28T06:04:16.102Z" }, - { url = "https://files.pythonhosted.org/packages/c4/eb/921405b4dc55d4f8be4c700ef120539fdd75d5fdb50d83bd257171ee18e0/wrapt-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4fc96b159af0a3e0faa72475a69d66292bea72a5bed1e1aca1bffbddc3cb2b0", size = 80733, upload-time = "2026-07-28T06:04:17.43Z" }, - { url = "https://files.pythonhosted.org/packages/b6/13/75947450c5bb57795fa86384721cd52c5c4deb0879022f309501a8a85d44/wrapt-2.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:1236fa25173ca964c97422470482e9011b9e3c7ed0d75798b40b3da3b0e0e760", size = 80199, upload-time = "2026-07-28T06:04:18.761Z" }, { url = "https://files.pythonhosted.org/packages/00/b8/9182e4c618a847be0baccb68e4602b070d0fa22c782cf058f4bc66b32709/wrapt-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ab559e1b2551d23d54db2a0001c6d73bad022a254639561c5f6c382a9d6c2fe", size = 81427, upload-time = "2026-07-28T06:04:20.106Z" }, { url = "https://files.pythonhosted.org/packages/84/ca/613cefd9c5977366b1587e61c0b428176d382e6d75b454084c5e58503042/wrapt-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bff9a671bc00709cab5a7f745c592b5671873449db0ee2a569af994f16b29a4d", size = 82360, upload-time = "2026-07-28T06:04:21.613Z" }, { url = "https://files.pythonhosted.org/packages/71/71/4cd2151a236f44a6e2dd4ed8011838d7ba0be3d656c8bafdfc65a2ed1917/wrapt-2.3.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fc648a335d7e01adb3640b25f02fd0ea05886cf04d0af7f4ee902bc7b5e466e8", size = 161700, upload-time = "2026-07-28T06:04:22.723Z" }, From 51c57552749c3d348b679be72e5ed98ddb7fecbb Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:00:25 -0700 Subject: [PATCH 15/39] Update types and blueprint --- package-lock.json | 420 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 212 insertions(+), 212 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10183c3d..3e6a6f45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,10 @@ "": { "name": "@seamapi/python", "devDependencies": { - "@seamapi/blueprint": "^1.1.0", + "@seamapi/blueprint": "^1.5.1", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.983.0", + "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", "prettier": "^3.2.5" }, @@ -19,9 +19,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -37,9 +37,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -55,9 +55,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -73,9 +73,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -91,9 +91,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -109,9 +109,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -127,9 +127,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -145,9 +145,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -163,9 +163,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -199,9 +199,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -217,9 +217,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -235,9 +235,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -253,9 +253,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -271,9 +271,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -289,9 +289,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -307,9 +307,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -325,9 +325,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -343,9 +343,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -361,9 +361,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -379,9 +379,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -397,9 +397,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -415,9 +415,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -433,9 +433,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -451,9 +451,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -469,9 +469,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -787,9 +787,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.1.0.tgz", - "integrity": "sha512-wX1HZkA/IK9hDQ6Qdxw5Mo+Ysfh82p9IEXQJafakO9VMbszW6n1U02eEhZHVY3CfzN/duk6t9h1veX0zRlhWBQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.5.1.tgz", + "integrity": "sha512-rXHMNDGmaE7OK+tg3e64DnpMHQypN4BuSS+PkVqGUoYc9f+xBMONqPscsHAGi9iU/iyZOJZKEOKOXvMmWaLoLA==", "dev": true, "license": "MIT", "dependencies": { @@ -797,8 +797,8 @@ "zod": "^3.23.8" }, "engines": { - "node": ">=22.11.0", - "npm": ">=10.9.4" + "node": ">=22.12.0", + "npm": ">=10.0.0" } }, "node_modules/@seamapi/fake-devicedb": { @@ -871,14 +871,14 @@ } }, "node_modules/@seamapi/types": { - "version": "1.983.0", - "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.983.0.tgz", - "integrity": "sha512-SMkfn1SVC70x67mtRAvLMJtpFh/0zaStLatb6LA+kz9n/rV1gBS/UlH8SzBFg7iStE22f/VPjkdltHTIY1paoA==", + "version": "1.1001.0", + "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.1001.0.tgz", + "integrity": "sha512-pwIEqMYCdOLlIHUzzLlMq/4K6QwAM3kWXooSxNWOlPyCeWk21SvrbTN/joXwZtky504g3unLPKMbFad1mlQyfQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=22.11.0", - "npm": ">=10.9.4" + "node": ">=22.12.0", + "npm": ">=10.0.0" }, "peerDependencies": { "zod": "^3.24.0" @@ -965,17 +965,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", - "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/type-utils": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -988,7 +988,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.65.0", + "@typescript-eslint/parser": "^8.67.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -1004,16 +1004,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", - "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3" }, "engines": { @@ -1029,14 +1029,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", - "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.65.0", - "@typescript-eslint/types": "^8.65.0", + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", "debug": "^4.4.3" }, "engines": { @@ -1051,14 +1051,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", - "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0" + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1069,9 +1069,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", - "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", "dev": true, "license": "MIT", "engines": { @@ -1086,15 +1086,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", - "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1111,9 +1111,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", - "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", "dev": true, "license": "MIT", "engines": { @@ -1125,16 +1125,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", - "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.65.0", - "@typescript-eslint/tsconfig-utils": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1163,9 +1163,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { @@ -1205,16 +1205,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", - "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0" + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1229,13 +1229,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/types": "8.67.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1527,9 +1527,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1886,9 +1886,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.24.4", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.4.tgz", - "integrity": "sha512-GVoi+ICHocoOIU7qVVM48wOJziRsqrsyqlI0Ce0LdowRn6v3bcH2zUa9kp85ncx0nwIb9/HOCOLS3fdThDG/XQ==", + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", "dev": true, "license": "MIT", "dependencies": { @@ -2099,9 +2099,9 @@ } }, "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2113,32 +2113,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escape-string-regexp": { @@ -2747,9 +2747,9 @@ } }, "node_modules/flatted": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz", - "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC", "peer": true @@ -2898,9 +2898,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", - "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.2.tgz", + "integrity": "sha512-XpwZALwwl/BaKTAyC6+c5T8y6kCg2jk+XGqOVrKIQmW49pNypYLMRjCUXqa28tQgJlhS2RlzP7sc+Rx7W6qsfw==", "dev": true, "license": "MIT", "dependencies": { @@ -2953,9 +2953,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { @@ -3101,9 +3101,9 @@ } }, "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", - "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", "dev": true, "license": "MIT", "peer": true, @@ -3817,9 +3817,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -4190,9 +4190,9 @@ } }, "node_modules/neostandard/node_modules/globals": { - "version": "17.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.8.0.tgz", - "integrity": "sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", "dev": true, "license": "MIT", "engines": { @@ -5453,9 +5453,9 @@ } }, "node_modules/tsx": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", - "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", "dev": true, "license": "MIT", "peer": true, @@ -5580,16 +5580,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", - "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.65.0", - "@typescript-eslint/parser": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0" + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 1a816343..83d09c74 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,10 @@ } }, "devDependencies": { - "@seamapi/blueprint": "^1.1.0", + "@seamapi/blueprint": "^1.5.1", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.983.0", + "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", "prettier": "^3.2.5" } From 975e57ff158b97384b7bd69fffc73d515663f4d0 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:03:58 +0000 Subject: [PATCH 16/39] ci: Generate code --- seam/resources/acs_credential.py | 30 +++++++++++ seam/resources/action_attempt.py | 69 +++++++++++++++++++++++++- seam/resources/device.py | 74 ++++++++++++++++++++------- seam/resources/seam_event.py | 12 +---- seam/resources/unmanaged_device.py | 4 ++ seam/routes/access_codes.py | 56 --------------------- seam/routes/devices_unmanaged.py | 32 ------------ seam/routes/events.py | 4 +- seam/routes/locks.py | 80 ------------------------------ seam/routes/noise_sensors.py | 80 ------------------------------ seam/routes/thermostats.py | 80 ------------------------------ 11 files changed, 163 insertions(+), 358 deletions(-) diff --git a/seam/resources/acs_credential.py b/seam/resources/acs_credential.py index 99f92b44..b8f09b54 100644 --- a/seam/resources/acs_credential.py +++ b/seam/resources/acs_credential.py @@ -24,6 +24,8 @@ class AcsCredential: :ivar acs_user_id: ID of the `ACS user `_ to whom the `credential `_ belongs. + :ivar akiles_metadata: Akiles-specific metadata for the `credential `_. + :ivar assa_abloy_vostio_metadata: Vostio-specific metadata for the `credential `_. :ivar card_number: Number of the card associated with the `credential `_. @@ -71,6 +73,20 @@ class AcsCredential: :ivar workspace_id: ID of the workspace that contains the `credential `_. """ + @dataclass + class AkilesMetadata(ResourceMapping): + """Akiles-specific metadata for the `credential `_. + + :ivar member_pin_id: ID of the Akiles member PIN.""" + + member_pin_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + member_pin_id=d.get("member_pin_id", None), + ) + @dataclass class AssaAbloyVostioMetadata(ResourceMapping): """Vostio-specific metadata for the `credential `_. @@ -182,11 +198,17 @@ class Warnings(ResourceMapping): :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + + :ivar new_code: The PIN code that was assigned instead. + + :ivar original_code: The originally requested PIN code that could not be used. """ created_at: str message: str warning_code: str + new_code: Optional[str] + original_code: Optional[str] @classmethod def from_dict(cls, d: Any): @@ -194,6 +216,8 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), message=d.get("message", None), warning_code=d.get("warning_code", None), + new_code=d.get("new_code", None), + original_code=d.get("original_code", None), ) access_method: str @@ -201,6 +225,7 @@ def from_dict(cls, d: Any): acs_credential_pool_id: Optional[str] acs_system_id: str acs_user_id: Optional[str] + akiles_metadata: Optional[AkilesMetadata] assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] card_number: Optional[str] code: Optional[str] @@ -233,6 +258,11 @@ def from_dict(cls, d: Any): acs_credential_pool_id=d.get("acs_credential_pool_id", None), acs_system_id=d.get("acs_system_id", None), acs_user_id=d.get("acs_user_id", None), + akiles_metadata=( + cls.AkilesMetadata.from_dict(d.get("akiles_metadata")) + if d.get("akiles_metadata") is not None + else None + ), assa_abloy_vostio_metadata=( cls.AssaAbloyVostioMetadata.from_dict( d.get("assa_abloy_vostio_metadata") diff --git a/seam/resources/action_attempt.py b/seam/resources/action_attempt.py index b371cad6..0c0ff07e 100644 --- a/seam/resources/action_attempt.py +++ b/seam/resources/action_attempt.py @@ -58,6 +58,8 @@ class Result(ResourceMapping): :ivar acs_user_id: ID of the `ACS user `_ to whom the `credential `_ belongs. + :ivar akiles_metadata: Akiles-specific metadata for the `credential `_. + :ivar assa_abloy_vostio_metadata: Vostio-specific metadata for the `credential `_. :ivar card_number: Number of the card associated with the `credential `_. @@ -121,7 +123,10 @@ class Result(ResourceMapping): :ivar mode: Access method mode. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. :ivar pending_mutations: Pending mutations for the `access method `_. Indicates operations that are in progress. - """ + + :ivar access_code: + + :ivar noise_threshold:""" @dataclass class AcsCredentialOnEncoder(ResourceMapping): @@ -235,6 +240,8 @@ class AcsCredentialOnSeam(ResourceMapping): :ivar acs_user_id: ID of the `ACS user `_ to whom the `credential `_ belongs. + :ivar akiles_metadata: Akiles-specific metadata for the `credential `_. + :ivar assa_abloy_vostio_metadata: Vostio-specific metadata for the `credential `_. :ivar card_number: Number of the card associated with the `credential `_. @@ -282,6 +289,20 @@ class AcsCredentialOnSeam(ResourceMapping): :ivar workspace_id: ID of the workspace that contains the `credential `_. """ + @dataclass + class AkilesMetadata(ResourceMapping): + """Akiles-specific metadata for the `credential `_. + + :ivar member_pin_id: ID of the Akiles member PIN.""" + + member_pin_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + member_pin_id=d.get("member_pin_id", None), + ) + @dataclass class AssaAbloyVostioMetadata(ResourceMapping): """Vostio-specific metadata for the `credential `_. @@ -395,11 +416,17 @@ class Warnings(ResourceMapping): :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + + :ivar new_code: The PIN code that was assigned instead. + + :ivar original_code: The originally requested PIN code that could not be used. """ created_at: str message: str warning_code: str + new_code: Optional[str] + original_code: Optional[str] @classmethod def from_dict(cls, d: Any): @@ -407,6 +434,8 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), message=d.get("message", None), warning_code=d.get("warning_code", None), + new_code=d.get("new_code", None), + original_code=d.get("original_code", None), ) access_method: str @@ -414,6 +443,7 @@ def from_dict(cls, d: Any): acs_credential_pool_id: Optional[str] acs_system_id: str acs_user_id: Optional[str] + akiles_metadata: Optional[AkilesMetadata] assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] card_number: Optional[str] code: Optional[str] @@ -446,6 +476,11 @@ def from_dict(cls, d: Any): acs_credential_pool_id=d.get("acs_credential_pool_id", None), acs_system_id=d.get("acs_system_id", None), acs_user_id=d.get("acs_user_id", None), + akiles_metadata=( + cls.AkilesMetadata.from_dict(d.get("akiles_metadata")) + if d.get("akiles_metadata") is not None + else None + ), assa_abloy_vostio_metadata=( cls.AssaAbloyVostioMetadata.from_dict( d.get("assa_abloy_vostio_metadata") @@ -503,6 +538,10 @@ class Warnings(ResourceMapping): :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar new_code: The PIN code that was assigned instead. + + :ivar original_code: The originally requested PIN code that could not be used. + :ivar original_access_method_id: ID of the original access method from which this backup access method was split, if applicable. """ @@ -510,6 +549,8 @@ class Warnings(ResourceMapping): warning_message: Optional[str] created_at: Optional[str] message: Optional[str] + new_code: Optional[str] + original_code: Optional[str] original_access_method_id: Optional[str] @classmethod @@ -519,9 +560,25 @@ def from_dict(cls, d: Any): warning_message=d.get("warning_message", None), created_at=d.get("created_at", None), message=d.get("message", None), + new_code=d.get("new_code", None), + original_code=d.get("original_code", None), original_access_method_id=d.get("original_access_method_id", None), ) + @dataclass + class AkilesMetadata(ResourceMapping): + """Akiles-specific metadata for the `credential `_. + + :ivar member_pin_id: ID of the Akiles member PIN.""" + + member_pin_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + member_pin_id=d.get("member_pin_id", None), + ) + @dataclass class AssaAbloyVostioMetadata(ResourceMapping): """Vostio-specific metadata for the `credential `_. @@ -708,6 +765,7 @@ def from_dict(cls, d: Any): acs_credential_pool_id: Optional[str] acs_system_id: Optional[str] acs_user_id: Optional[str] + akiles_metadata: Optional[AkilesMetadata] assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] card_number: Optional[str] code: Optional[str] @@ -740,6 +798,8 @@ def from_dict(cls, d: Any): is_ready_for_encoding: Optional[bool] mode: Optional[str] pending_mutations: Optional[List[PendingMutations]] + access_code: Optional[Dict[str, Any]] + noise_threshold: Optional[Dict[str, Any]] @classmethod def from_dict(cls, d: Any): @@ -763,6 +823,11 @@ def from_dict(cls, d: Any): acs_credential_pool_id=d.get("acs_credential_pool_id", None), acs_system_id=d.get("acs_system_id", None), acs_user_id=d.get("acs_user_id", None), + akiles_metadata=( + cls.AkilesMetadata.from_dict(d.get("akiles_metadata")) + if d.get("akiles_metadata") is not None + else None + ), assa_abloy_vostio_metadata=( cls.AssaAbloyVostioMetadata.from_dict( d.get("assa_abloy_vostio_metadata") @@ -814,6 +879,8 @@ def from_dict(cls, d: Any): cls.PendingMutations.from_dict(i) for i in d.get("pending_mutations") or [] ], + access_code=DeepAttrDict(d.get("access_code", None)), + noise_threshold=DeepAttrDict(d.get("noise_threshold", None)), ) action_attempt_id: str diff --git a/seam/resources/device.py b/seam/resources/device.py index 20853259..827983a7 100644 --- a/seam/resources/device.py +++ b/seam/resources/device.py @@ -174,12 +174,15 @@ class Location(ResourceMapping): :ivar location_name: Name of the device location. + :ivar room_name: Name of the room within the device location, when the provider reports one. + :ivar time_zone: Time zone of the device location. :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. """ location_name: Optional[str] + room_name: Optional[str] time_zone: Optional[str] timezone: Optional[str] @@ -187,6 +190,7 @@ class Location(ResourceMapping): def from_dict(cls, d: Any): return cls( location_name=d.get("location_name", None), + room_name=d.get("room_name", None), time_zone=d.get("time_zone", None), timezone=d.get("timezone", None), ) @@ -287,7 +291,7 @@ class Properties(ResourceMapping): :ivar salto_ks_metadata: Metadata for a Salto KS device. - :ivar salto_metadata: Deprecated: Use ``salto_ks_metadata `` instead. Metada for a Salto device. + :ivar salto_metadata: Deprecated: Use ``salto_ks_metadata`` instead. Metada for a Salto device. :ivar schlage_metadata: Metadata for a Schlage device. @@ -311,6 +315,8 @@ class Properties(ResourceMapping): :ivar wyze_metadata: Metadata for a Wyze device. + :ivar yacan_metadata: Metadata for a Yacan device. + :ivar auto_lock_delay_seconds: The delay in seconds before the lock automatically locks after being unlocked. :ivar auto_lock_enabled: Indicates whether automatic locking is enabled. @@ -794,15 +800,6 @@ class DormakabaOracodeMetadata(ResourceMapping): :ivar site_name: Site name for a dormakaba Oracode device.""" - @dataclass - class DeviceId(ResourceMapping): - """Device ID for a dormakaba Oracode device.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - @dataclass class PredefinedTimeSlots(ResourceMapping): """Predefined time slots for a dormakaba Oracode device. @@ -857,7 +854,7 @@ def from_dict(cls, d: Any): prefix=d.get("prefix", None), ) - device_id: Optional[DeviceId] + device_id: Optional[str] door_id: Optional[float] door_is_wireless: Optional[bool] door_name: Optional[str] @@ -869,11 +866,7 @@ def from_dict(cls, d: Any): @classmethod def from_dict(cls, d: Any): return cls( - device_id=( - cls.DeviceId.from_dict(d.get("device_id")) - if d.get("device_id") is not None - else None - ), + device_id=d.get("device_id", None), door_id=d.get("door_id", None), door_is_wireless=d.get("door_is_wireless", None), door_name=d.get("door_name", None), @@ -1405,12 +1398,19 @@ class NestMetadata(ResourceMapping): :ivar display_name: Display name for a Google Nest device. - :ivar nest_device_id: Device ID for a Google Nest device.""" + :ivar nest_device_id: Device ID for a Google Nest device. + + :ivar nest_structure_id: ID of the Google Nest structure containing the device. + + :ivar structure_name: Name of the Google Nest structure containing the device. The device owner sets this value. + """ device_custom_name: Optional[str] device_name: Optional[str] display_name: Optional[str] nest_device_id: Optional[str] + nest_structure_id: Optional[str] + structure_name: Optional[str] @classmethod def from_dict(cls, d: Any): @@ -1419,6 +1419,8 @@ def from_dict(cls, d: Any): device_name=d.get("device_name", None), display_name=d.get("display_name", None), nest_device_id=d.get("nest_device_id", None), + nest_structure_id=d.get("nest_structure_id", None), + structure_name=d.get("structure_name", None), ) @dataclass @@ -1686,11 +1688,14 @@ class SensiMetadata(ResourceMapping): :ivar dual_setpoints_not_supported: Set to true when the device does not support the /dual-setpoints API endpoint. + :ivar enforced_setpoint_range_celsius: Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. + :ivar product_type: Product type for a Sensi device.""" device_id: Optional[str] device_name: Optional[str] dual_setpoints_not_supported: Optional[bool] + enforced_setpoint_range_celsius: Optional[List[float]] product_type: Optional[str] @classmethod @@ -1701,6 +1706,9 @@ def from_dict(cls, d: Any): dual_setpoints_not_supported=d.get( "dual_setpoints_not_supported", None ), + enforced_setpoint_range_celsius=d.get( + "enforced_setpoint_range_celsius", None + ), product_type=d.get("product_type", None), ) @@ -1990,6 +1998,32 @@ def from_dict(cls, d: Any): product_type=d.get("product_type", None), ) + @dataclass + class YacanMetadata(ResourceMapping): + """Metadata for a Yacan device. + + :ivar device_id: Device ID for a Yacan device. + + :ivar device_name: Device name for a Yacan device. + + :ivar device_type: Device type for a Yacan device. + + :ivar serial_number: Serial number for a Yacan device.""" + + device_id: Optional[str] + device_name: Optional[str] + device_type: Optional[str] + serial_number: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_id=d.get("device_id", None), + device_name=d.get("device_name", None), + device_type=d.get("device_type", None), + serial_number=d.get("serial_number", None), + ) + @dataclass class CodeConstraints(ResourceMapping): """Constraints on access codes for the device. Seam represents each constraint as an object with a ``constraint_type`` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. @@ -2746,6 +2780,7 @@ def from_dict(cls, d: Any): ultraloq_metadata: Optional[UltraloqMetadata] visionline_metadata: Optional[VisionlineMetadata] wyze_metadata: Optional[WyzeMetadata] + yacan_metadata: Optional[YacanMetadata] auto_lock_delay_seconds: Optional[float] auto_lock_enabled: Optional[bool] backup_access_code_pool_enabled: Optional[bool] @@ -3046,6 +3081,11 @@ def from_dict(cls, d: Any): if d.get("wyze_metadata") is not None else None ), + yacan_metadata=( + cls.YacanMetadata.from_dict(d.get("yacan_metadata")) + if d.get("yacan_metadata") is not None + else None + ), auto_lock_delay_seconds=d.get("auto_lock_delay_seconds", None), auto_lock_enabled=d.get("auto_lock_enabled", None), backup_access_code_pool_enabled=d.get( diff --git a/seam/resources/seam_event.py b/seam/resources/seam_event.py index e9592838..323115b6 100644 --- a/seam/resources/seam_event.py +++ b/seam/resources/seam_event.py @@ -80,7 +80,7 @@ class SeamEvent: :ivar is_backup_code: Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). - :ivar acs_system_id: + :ivar acs_system_id: ID of the access system. :ivar acs_system_errors: Errors associated with the access control system. @@ -88,7 +88,7 @@ class SeamEvent: :ivar acs_credential_id: ID of the affected credential. - :ivar acs_user_id: + :ivar acs_user_id: ID of the affected access system user. :ivar acs_encoder_id: ID of the affected encoder. @@ -100,8 +100,6 @@ class SeamEvent: :ivar customer_key: - :ivar connected_account_type: undocumented: Unreleased. - :ivar action_attempt_id: :ivar action_type: Type of the action. @@ -136,8 +134,6 @@ class SeamEvent: :ivar method: - :ivar user_identity_id: - :ivar reason: Why access was denied, when the provider reports a determinable cause. Omitted when unknown. :ivar climate_preset_key: Key of the climate preset that was activated. @@ -536,7 +532,6 @@ def from_dict(cls, d: Any): client_session_id: Optional[str] connect_webview_id: Optional[str] customer_key: Optional[str] - connected_account_type: Optional[str] action_attempt_id: Optional[str] action_type: Optional[str] status: Optional[str] @@ -554,7 +549,6 @@ def from_dict(cls, d: Any): is_via_bluetooth: Optional[bool] is_via_nfc: Optional[bool] method: Optional[str] - user_identity_id: Optional[str] reason: Optional[Reason] climate_preset_key: Optional[str] is_fallback_climate_preset: Optional[bool] @@ -663,7 +657,6 @@ def from_dict(cls, d: Any): client_session_id=d.get("client_session_id", None), connect_webview_id=d.get("connect_webview_id", None), customer_key=d.get("customer_key", None), - connected_account_type=d.get("connected_account_type", None), action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), status=d.get("status", None), @@ -681,7 +674,6 @@ def from_dict(cls, d: Any): is_via_bluetooth=d.get("is_via_bluetooth", None), is_via_nfc=d.get("is_via_nfc", None), method=d.get("method", None), - user_identity_id=d.get("user_identity_id", None), reason=( cls.Reason.from_dict(d.get("reason")) if d.get("reason") is not None diff --git a/seam/resources/unmanaged_device.py b/seam/resources/unmanaged_device.py index ad56f5a9..15ba2dcc 100644 --- a/seam/resources/unmanaged_device.py +++ b/seam/resources/unmanaged_device.py @@ -114,12 +114,15 @@ class Location(ResourceMapping): :ivar location_name: Name of the device location. + :ivar room_name: Name of the room within the device location, when the provider reports one. + :ivar time_zone: Time zone of the device location. :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. """ location_name: Optional[str] + room_name: Optional[str] time_zone: Optional[str] timezone: Optional[str] @@ -127,6 +130,7 @@ class Location(ResourceMapping): def from_dict(cls, d: Any): return cls( location_name=d.get("location_name", None), + room_name=d.get("room_name", None), time_zone=d.get("time_zone", None), timezone=d.get("timezone", None), ) diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index ddccd477..b954c899 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -278,16 +278,9 @@ def update( ends_at: Optional[str] = None, is_external_modification_allowed: Optional[bool] = None, is_managed: Optional[bool] = None, - is_offline_access_code: Optional[bool] = None, - is_one_time_use: Optional[bool] = None, - max_time_rounding: Optional[str] = None, name: Optional[str] = None, - prefer_native_scheduling: Optional[bool] = None, - preferred_code_length: Optional[float] = None, starts_at: Optional[str] = None, type: Optional[str] = None, - use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None, ) -> None: """Updates a specified active or upcoming `access code `_. @@ -309,12 +302,6 @@ def update( :param is_managed: Indicates whether the access code is managed through Seam. Note that to convert an unmanaged access code into a managed access code, use ``/access_codes/unmanaged/convert_to_managed``. - :param is_offline_access_code: Indicates whether the access code is an `offline access code `_. - - :param is_one_time_use: Indicates whether the `offline access code `_ is a single-use access code. - - :param max_time_rounding: Maximum rounding adjustment. To create a daily-bound `offline access code `_ for devices that support this feature, set this parameter to ``1d``. - :param name: Name of the new access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as ``first_name`` and ``last_name``. @@ -323,17 +310,9 @@ def update( To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called ``appearance``. This is an object with a ``name`` property and, optionally, ``first_name`` and ``last_name`` properties (for providers that break down a name into components). - :param prefer_native_scheduling: Indicates whether `native scheduling `_ should be used for time-bound codes when supported by the provider. Default: ``true``. - - :param preferred_code_length: Preferred code length. Only applicable if you do not specify a ``code``. If the affected device does not support the preferred code length, Seam reverts to using the shortest supported code length. - :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. :param type: Type to which you want to convert the access code. To convert a time-bound access code to an ongoing access code, set ``type`` to ``ongoing``. See also `Changing a time-bound access code to permanent access `_. - - :param use_backup_access_code_pool: Indicates whether to use a `backup access code pool `_ provided by Seam. If ``true``, you can use ```/access_codes/pull_backup_access_code`` `_. - - :param use_offline_access_code: Deprecated: Use ``is_offline_access_code`` instead. """ raise NotImplementedError() @@ -781,16 +760,9 @@ def update( ends_at: Optional[str] = None, is_external_modification_allowed: Optional[bool] = None, is_managed: Optional[bool] = None, - is_offline_access_code: Optional[bool] = None, - is_one_time_use: Optional[bool] = None, - max_time_rounding: Optional[str] = None, name: Optional[str] = None, - prefer_native_scheduling: Optional[bool] = None, - preferred_code_length: Optional[float] = None, starts_at: Optional[str] = None, type: Optional[str] = None, - use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None, ) -> None: """Updates a specified active or upcoming `access code `_. @@ -812,12 +784,6 @@ def update( :param is_managed: Indicates whether the access code is managed through Seam. Note that to convert an unmanaged access code into a managed access code, use ``/access_codes/unmanaged/convert_to_managed``. - :param is_offline_access_code: Indicates whether the access code is an `offline access code `_. - - :param is_one_time_use: Indicates whether the `offline access code `_ is a single-use access code. - - :param max_time_rounding: Maximum rounding adjustment. To create a daily-bound `offline access code `_ for devices that support this feature, set this parameter to ``1d``. - :param name: Name of the new access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as ``first_name`` and ``last_name``. @@ -826,17 +792,9 @@ def update( To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called ``appearance``. This is an object with a ``name`` property and, optionally, ``first_name`` and ``last_name`` properties (for providers that break down a name into components). - :param prefer_native_scheduling: Indicates whether `native scheduling `_ should be used for time-bound codes when supported by the provider. Default: ``true``. - - :param preferred_code_length: Preferred code length. Only applicable if you do not specify a ``code``. If the affected device does not support the preferred code length, Seam reverts to using the shortest supported code length. - :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. :param type: Type to which you want to convert the access code. To convert a time-bound access code to an ongoing access code, set ``type`` to ``ongoing``. See also `Changing a time-bound access code to permanent access `_. - - :param use_backup_access_code_pool: Indicates whether to use a `backup access code pool `_ provided by Seam. If ``true``, you can use ```/access_codes/pull_backup_access_code`` `_. - - :param use_offline_access_code: Deprecated: Use ``is_offline_access_code`` instead. """ json_payload: Dict[str, Any] = {} @@ -858,26 +816,12 @@ def update( ) if is_managed is not None: json_payload["is_managed"] = is_managed - if is_offline_access_code is not None: - json_payload["is_offline_access_code"] = is_offline_access_code - if is_one_time_use is not None: - json_payload["is_one_time_use"] = is_one_time_use - if max_time_rounding is not None: - json_payload["max_time_rounding"] = max_time_rounding if name is not None: json_payload["name"] = name - if prefer_native_scheduling is not None: - json_payload["prefer_native_scheduling"] = prefer_native_scheduling - if preferred_code_length is not None: - json_payload["preferred_code_length"] = preferred_code_length if starts_at is not None: json_payload["starts_at"] = starts_at if type is not None: json_payload["type"] = type - if use_backup_access_code_pool is not None: - json_payload["use_backup_access_code_pool"] = use_backup_access_code_pool - if use_offline_access_code is not None: - json_payload["use_offline_access_code"] = use_offline_access_code self.client.post("/access_codes/update", json=json_payload) diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index d7c6e9c1..5089cd05 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -31,7 +31,6 @@ def list( connected_account_id: Optional[str] = None, connected_account_ids: Optional[List[str]] = None, created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, @@ -40,9 +39,6 @@ def list( manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[UnmanagedDevice]: """Returns a list of all `unmanaged devices `_. @@ -56,8 +52,6 @@ def list( :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. :param device_ids: Array of device IDs for which you want to list devices. @@ -74,12 +68,6 @@ def list( :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" raise NotImplementedError() @@ -141,7 +129,6 @@ def list( connected_account_id: Optional[str] = None, connected_account_ids: Optional[List[str]] = None, created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, @@ -150,9 +137,6 @@ def list( manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[UnmanagedDevice]: """Returns a list of all `unmanaged devices `_. @@ -166,8 +150,6 @@ def list( :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. :param device_ids: Array of device IDs for which you want to list devices. @@ -184,12 +166,6 @@ def list( :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" json_payload: Dict[str, Any] = {} @@ -201,8 +177,6 @@ def list( json_payload["connected_account_ids"] = connected_account_ids if created_before is not None: json_payload["created_before"] = created_before - if custom_metadata_has is not None: - json_payload["custom_metadata_has"] = custom_metadata_has if customer_key is not None: json_payload["customer_key"] = customer_key if device_ids is not None: @@ -219,12 +193,6 @@ def list( json_payload["page_cursor"] = page_cursor if search is not None: json_payload["search"] = search - if space_id is not None: - json_payload["space_id"] = space_id - if unstable_location_id is not None: - json_payload["unstable_location_id"] = unstable_location_id - if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key res = self.client.post("/devices/unmanaged/list", json=json_payload) diff --git a/seam/routes/events.py b/seam/routes/events.py index 639fbb7b..4e452b10 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -42,7 +42,7 @@ def list( acs_system_id: Optional[str] = None, acs_system_ids: Optional[List[str]] = None, acs_user_id: Optional[str] = None, - between: Optional[List[Dict[str, Any]]] = None, + between: Optional[List[str]] = None, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, @@ -170,7 +170,7 @@ def list( acs_system_id: Optional[str] = None, acs_system_ids: Optional[List[str]] = None, acs_user_id: Optional[str] = None, - between: Optional[List[Dict[str, Any]]] = None, + between: Optional[List[str]] = None, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, diff --git a/seam/routes/locks.py b/seam/routes/locks.py index effc53d1..0147ed82 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -57,20 +57,10 @@ def list( *, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, - connected_account_ids: Optional[List[str]] = None, - created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, device_types: Optional[List[str]] = None, - limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, - search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -78,34 +68,14 @@ def list( :param connected_account_id: ID of the connected account for which you want to list devices. - :param connected_account_ids: Array of IDs of the connected accounts for which you want to list devices. - - :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. - :param device_ids: Array of device IDs for which you want to list devices. - :param device_type: Device type of the locks that you want to list. :param device_types: Device types of the locks that you want to list. - :param limit: Numerical limit on the number of devices to return. - :param manufacturer: Manufacturer of the locks that you want to list. - :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. - - :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" raise NotImplementedError() @@ -223,20 +193,10 @@ def list( *, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, - connected_account_ids: Optional[List[str]] = None, - created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, device_types: Optional[List[str]] = None, - limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, - search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -244,34 +204,14 @@ def list( :param connected_account_id: ID of the connected account for which you want to list devices. - :param connected_account_ids: Array of IDs of the connected accounts for which you want to list devices. - - :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. - :param device_ids: Array of device IDs for which you want to list devices. - :param device_type: Device type of the locks that you want to list. :param device_types: Device types of the locks that you want to list. - :param limit: Numerical limit on the number of devices to return. - :param manufacturer: Manufacturer of the locks that you want to list. - :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. - - :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" json_payload: Dict[str, Any] = {} @@ -279,34 +219,14 @@ def list( json_payload["connect_webview_id"] = connect_webview_id if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id - if connected_account_ids is not None: - json_payload["connected_account_ids"] = connected_account_ids - if created_before is not None: - json_payload["created_before"] = created_before - if custom_metadata_has is not None: - json_payload["custom_metadata_has"] = custom_metadata_has if customer_key is not None: json_payload["customer_key"] = customer_key - if device_ids is not None: - json_payload["device_ids"] = device_ids if device_type is not None: json_payload["device_type"] = device_type if device_types is not None: json_payload["device_types"] = device_types - if limit is not None: - json_payload["limit"] = limit if manufacturer is not None: json_payload["manufacturer"] = manufacturer - if page_cursor is not None: - json_payload["page_cursor"] = page_cursor - if search is not None: - json_payload["search"] = search - if space_id is not None: - json_payload["space_id"] = space_id - if unstable_location_id is not None: - json_payload["unstable_location_id"] = unstable_location_id - if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key res = self.client.post("/locks/list", json=json_payload) diff --git a/seam/routes/noise_sensors.py b/seam/routes/noise_sensors.py index 598ce75e..ab99a7cb 100644 --- a/seam/routes/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -27,20 +27,10 @@ def list( *, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, - connected_account_ids: Optional[List[str]] = None, - created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, device_types: Optional[List[str]] = None, - limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, - search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. @@ -48,34 +38,14 @@ def list( :param connected_account_id: ID of the connected account for which you want to list devices. - :param connected_account_ids: Array of IDs of the connected accounts for which you want to list devices. - - :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. - :param device_ids: Array of device IDs for which you want to list devices. - :param device_type: Device type of the noise sensors that you want to list. :param device_types: Device types of the noise sensors that you want to list. - :param limit: Numerical limit on the number of devices to return. - :param manufacturer: Manufacturers of the noise sensors that you want to list. - :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. - - :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" raise NotImplementedError() @@ -102,20 +72,10 @@ def list( *, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, - connected_account_ids: Optional[List[str]] = None, - created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, device_types: Optional[List[str]] = None, - limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, - search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. @@ -123,34 +83,14 @@ def list( :param connected_account_id: ID of the connected account for which you want to list devices. - :param connected_account_ids: Array of IDs of the connected accounts for which you want to list devices. - - :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. - :param device_ids: Array of device IDs for which you want to list devices. - :param device_type: Device type of the noise sensors that you want to list. :param device_types: Device types of the noise sensors that you want to list. - :param limit: Numerical limit on the number of devices to return. - :param manufacturer: Manufacturers of the noise sensors that you want to list. - :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. - - :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" json_payload: Dict[str, Any] = {} @@ -158,34 +98,14 @@ def list( json_payload["connect_webview_id"] = connect_webview_id if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id - if connected_account_ids is not None: - json_payload["connected_account_ids"] = connected_account_ids - if created_before is not None: - json_payload["created_before"] = created_before - if custom_metadata_has is not None: - json_payload["custom_metadata_has"] = custom_metadata_has if customer_key is not None: json_payload["customer_key"] = customer_key - if device_ids is not None: - json_payload["device_ids"] = device_ids if device_type is not None: json_payload["device_type"] = device_type if device_types is not None: json_payload["device_types"] = device_types - if limit is not None: - json_payload["limit"] = limit if manufacturer is not None: json_payload["manufacturer"] = manufacturer - if page_cursor is not None: - json_payload["page_cursor"] = page_cursor - if search is not None: - json_payload["search"] = search - if space_id is not None: - json_payload["space_id"] = space_id - if unstable_location_id is not None: - json_payload["unstable_location_id"] = unstable_location_id - if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key res = self.client.post("/noise_sensors/list", json=json_payload) diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index 338fcfd0..cac0bf14 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -180,20 +180,10 @@ def list( *, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, - connected_account_ids: Optional[List[str]] = None, - created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, device_types: Optional[List[str]] = None, - limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, - search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -201,34 +191,14 @@ def list( :param connected_account_id: ID of the connected account for which you want to list devices. - :param connected_account_ids: Array of IDs of the connected accounts for which you want to list devices. - - :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. - :param device_ids: Array of device IDs for which you want to list devices. - :param device_type: Device type by which you want to filter thermostat devices. :param device_types: Array of device types by which you want to filter thermostat devices. - :param limit: Numerical limit on the number of devices to return. - :param manufacturer: Manufacturer by which you want to filter thermostat devices. - :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. - - :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" raise NotImplementedError() @@ -713,20 +683,10 @@ def list( *, connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, - connected_account_ids: Optional[List[str]] = None, - created_before: Optional[str] = None, - custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, - device_ids: Optional[List[str]] = None, device_type: Optional[str] = None, device_types: Optional[List[str]] = None, - limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, - search: Optional[str] = None, - space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -734,34 +694,14 @@ def list( :param connected_account_id: ID of the connected account for which you want to list devices. - :param connected_account_ids: Array of IDs of the connected accounts for which you want to list devices. - - :param created_before: Timestamp by which to limit returned devices. Returns devices created before this timestamp. - - :param custom_metadata_has: Set of key:value `custom metadata `_ pairs for which you want to list devices. - :param customer_key: Customer key for which you want to list devices. - :param device_ids: Array of device IDs for which you want to list devices. - :param device_type: Device type by which you want to filter thermostat devices. :param device_types: Array of device types by which you want to filter thermostat devices. - :param limit: Numerical limit on the number of devices to return. - :param manufacturer: Manufacturer by which you want to filter thermostat devices. - :param page_cursor: Identifies the specific page of results to return, obtained from the previous page's ``next_page_cursor``. - - :param search: String for which to search. Filters returned devices to include all records that satisfy a partial match using ``device_id`` (full or partial UUID prefix, minimum 4 characters), ``connected_account_id``, ``display_name``, ``custom_metadata`` or ``location.location_name``. - - :param space_id: ID of the space for which you want to list devices. - - :param unstable_location_id: Deprecated: Use ``space_id``. - - :param user_identifier_key: Your own internal user ID for the user for which you want to list devices. - :returns: OK""" json_payload: Dict[str, Any] = {} @@ -769,34 +709,14 @@ def list( json_payload["connect_webview_id"] = connect_webview_id if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id - if connected_account_ids is not None: - json_payload["connected_account_ids"] = connected_account_ids - if created_before is not None: - json_payload["created_before"] = created_before - if custom_metadata_has is not None: - json_payload["custom_metadata_has"] = custom_metadata_has if customer_key is not None: json_payload["customer_key"] = customer_key - if device_ids is not None: - json_payload["device_ids"] = device_ids if device_type is not None: json_payload["device_type"] = device_type if device_types is not None: json_payload["device_types"] = device_types - if limit is not None: - json_payload["limit"] = limit if manufacturer is not None: json_payload["manufacturer"] = manufacturer - if page_cursor is not None: - json_payload["page_cursor"] = page_cursor - if search is not None: - json_payload["search"] = search - if space_id is not None: - json_payload["space_id"] = space_id - if unstable_location_id is not None: - json_payload["unstable_location_id"] = unstable_location_id - if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key res = self.client.post("/thermostats/list", json=json_payload) From 566beea8aa3604b5c7823b7a915c1dfc9d630e79 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:08:28 -0700 Subject: [PATCH 17/39] feat: use preferred HTTP methods in generated routes --- codegen/layouts/partials/route-method.hbs | 6 +++--- codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 16 ++++++++++++++++ codegen/lib/routes.ts | 1 + seam/client.py | 3 +++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index d5549441..4af50ca2 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,13 +1,13 @@ def {{> method-signature}}: """{{> method-docstring}}""" - json_payload: Dict[str, Any] = {} + {{payloadVar}}: Dict[str, Any] = {} {{#each params}} if {{name}} is not None: - json_payload["{{name}}"] = {{name}} + {{../payloadVar}}["{{name}}"] = {{name}} {{/each}} - {{#unless (eq returnType "None")}}res = {{/unless}}self.client.post("{{path}}", json=json_payload) + {{#unless (eq returnType "None")}}res = {{/unless}}self.client.{{httpVerb}}("{{path}}", {{payloadArg}}={{payloadVar}}) {{#if (eq returnType "ActionAttempt")}} wait_for_action_attempt = ( diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index 9937221a..50ee1874 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -14,6 +14,7 @@ export interface ClassMethodParameter { export interface ClassMethod { methodName: string path: string + preferredMethod: string description: string responseDescription: string isDeprecated: boolean diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 8e57326c..e7a27041 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -11,6 +11,9 @@ import { export interface MethodLayoutContext { name: string path: string + httpVerb: string + payloadVar: string + payloadArg: string description: string responseDescription: string isDeprecated: boolean @@ -51,11 +54,24 @@ export interface RouteLayoutContext { methods: MethodLayoutContext[] } +const getRequestLayoutContext = ( + preferredMethod: string, +): Pick => { + const httpVerb = preferredMethod.toLowerCase() + + if (preferredMethod === 'GET' || preferredMethod === 'DELETE') { + return { httpVerb, payloadVar: 'params', payloadArg: 'params' } + } + + return { httpVerb, payloadVar: 'json_payload', payloadArg: 'json' } +} + export const getMethodLayoutContext = ( method: ClassMethod, ): MethodLayoutContext => ({ name: method.methodName, path: method.path, + ...getRequestLayoutContext(method.preferredMethod), description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 0cd065d2..7c874bcc 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -89,6 +89,7 @@ export const routes = ( cls.methods.push({ methodName: endpoint.name, path: endpoint.path, + preferredMethod: endpoint.request.preferredMethod, description: endpoint.description, responseDescription: endpoint.response.description, isDeprecated: endpoint.isDeprecated, diff --git a/seam/client.py b/seam/client.py index e180e1a8..600f6a2b 100644 --- a/seam/client.py +++ b/seam/client.py @@ -92,6 +92,9 @@ def get(self, url, **kwargs) -> Any: def post(self, url, data=None, json=None, **kwargs) -> Any: return self.request("POST", url, data=data, json=json, **kwargs) + def delete(self, url, json=None, **kwargs) -> Any: + return self.request("DELETE", url, json=json, **kwargs) + def request(self, method, url, *args, **kwargs) -> Any: response = super().request(method, url, *args, **kwargs) From b0de5642de527831b0ceda1f94e4799bf88d7643 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:10:42 +0000 Subject: [PATCH 18/39] ci: Generate code --- seam/routes/access_codes.py | 30 ++++----- seam/routes/access_codes_unmanaged.py | 34 +++++----- seam/routes/access_grants.py | 16 ++--- seam/routes/access_grants_unmanaged.py | 24 +++---- seam/routes/access_methods.py | 36 +++++----- seam/routes/access_methods_unmanaged.py | 18 ++--- seam/routes/acs_access_groups.py | 50 +++++++------- seam/routes/acs_credentials.py | 48 +++++++------- seam/routes/acs_encoders.py | 6 +- seam/routes/acs_entrances.py | 6 +- seam/routes/acs_systems.py | 25 ++++--- seam/routes/acs_users.py | 66 +++++++++---------- seam/routes/action_attempts.py | 6 +- seam/routes/client_sessions.py | 32 ++++----- seam/routes/connect_webviews.py | 12 ++-- seam/routes/connected_accounts.py | 16 ++--- seam/routes/devices.py | 16 ++--- seam/routes/devices_unmanaged.py | 10 +-- seam/routes/events.py | 10 +-- seam/routes/instant_keys.py | 20 +++--- seam/routes/locks.py | 8 +-- seam/routes/noise_sensors_noise_thresholds.py | 24 ++++--- seam/routes/phones.py | 20 +++--- seam/routes/spaces.py | 44 ++++++------- seam/routes/thermostats.py | 12 ++-- seam/routes/thermostats_daily_programs.py | 8 +-- seam/routes/thermostats_schedules.py | 22 +++---- seam/routes/user_identities.py | 64 +++++++++--------- seam/routes/user_identities_unmanaged.py | 20 +++--- seam/routes/webhooks.py | 18 ++--- seam/routes/workspaces.py | 10 +-- 31 files changed, 361 insertions(+), 370 deletions(-) diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index b954c899..f79ab444 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -555,7 +555,7 @@ def create_multiple( if use_backup_access_code_pool is not None: json_payload["use_backup_access_code_pool"] = use_backup_access_code_pool - res = self.client.post("/access_codes/create_multiple", json=json_payload) + res = self.client.put("/access_codes/create_multiple", json=json_payload) return [AccessCode.from_dict(item) for item in res["access_codes"]] @@ -566,14 +566,14 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non :param device_id: ID of the device for which you want to delete the access code. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_code_id is not None: - json_payload["access_code_id"] = access_code_id + params["access_code_id"] = access_code_id if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - self.client.post("/access_codes/delete", json=json_payload) + self.client.delete("/access_codes/delete", params=params) return None @@ -583,12 +583,12 @@ def generate_code(self, *, device_id: str) -> AccessCode: :param device_id: ID of the device for which you want to generate a code. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - res = self.client.post("/access_codes/generate_code", json=json_payload) + res = self.client.get("/access_codes/generate_code", params=params) return AccessCode.from_dict(res["generated_code"]) @@ -610,16 +610,16 @@ def get( :param device_id: ID of the device containing the access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_code_id is not None: - json_payload["access_code_id"] = access_code_id + params["access_code_id"] = access_code_id if code is not None: - json_payload["code"] = code + params["code"] = code if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - res = self.client.post("/access_codes/get", json=json_payload) + res = self.client.get("/access_codes/get", params=params) return AccessCode.from_dict(res["access_code"]) @@ -823,7 +823,7 @@ def update( if type is not None: json_payload["type"] = type - self.client.post("/access_codes/update", json=json_payload) + self.client.put("/access_codes/update", json=json_payload) return None @@ -866,6 +866,6 @@ def update_multiple( if starts_at is not None: json_payload["starts_at"] = starts_at - self.client.post("/access_codes/update_multiple", json=json_payload) + self.client.patch("/access_codes/update_multiple", json=json_payload) return None diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index e0dd0612..176752b2 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -150,7 +150,7 @@ def convert_to_managed( is_external_modification_allowed ) - self.client.post( + self.client.patch( "/access_codes/unmanaged/convert_to_managed", json=json_payload ) @@ -161,12 +161,12 @@ def delete(self, *, access_code_id: str) -> None: :param access_code_id: ID of the unmanaged access code that you want to delete. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_code_id is not None: - json_payload["access_code_id"] = access_code_id + params["access_code_id"] = access_code_id - self.client.post("/access_codes/unmanaged/delete", json=json_payload) + self.client.delete("/access_codes/unmanaged/delete", params=params) return None @@ -188,16 +188,16 @@ def get( :param device_id: ID of the device containing the unmanaged access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_code_id is not None: - json_payload["access_code_id"] = access_code_id + params["access_code_id"] = access_code_id if code is not None: - json_payload["code"] = code + params["code"] = code if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - res = self.client.post("/access_codes/unmanaged/get", json=json_payload) + res = self.client.get("/access_codes/unmanaged/get", params=params) return UnmanagedAccessCode.from_dict(res["access_code"]) @@ -223,20 +223,20 @@ def list( :param user_identifier_key: Your user ID for the user by which to filter unmanaged access codes. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if limit is not None: - json_payload["limit"] = limit + params["limit"] = limit if page_cursor is not None: - json_payload["page_cursor"] = page_cursor + params["page_cursor"] = page_cursor if search is not None: - json_payload["search"] = search + params["search"] = search if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key + params["user_identifier_key"] = user_identifier_key - res = self.client.post("/access_codes/unmanaged/list", json=json_payload) + res = self.client.get("/access_codes/unmanaged/list", params=params) return [UnmanagedAccessCode.from_dict(item) for item in res["access_codes"]] @@ -276,6 +276,6 @@ def update( is_external_modification_allowed ) - self.client.post("/access_codes/unmanaged/update", json=json_payload) + self.client.patch("/access_codes/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index 8865a7fa..dae8efa8 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -305,12 +305,12 @@ def delete(self, *, access_grant_id: str) -> None: """Delete an Access Grant. :param access_grant_id: ID of Access Grant to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_grant_id is not None: - json_payload["access_grant_id"] = access_grant_id + params["access_grant_id"] = access_grant_id - self.client.post("/access_grants/delete", json=json_payload) + self.client.delete("/access_grants/delete", params=params) return None @@ -327,14 +327,14 @@ def get( :param access_grant_key: Unique key of Access Grant to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_grant_id is not None: - json_payload["access_grant_id"] = access_grant_id + params["access_grant_id"] = access_grant_id if access_grant_key is not None: - json_payload["access_grant_key"] = access_grant_key + params["access_grant_key"] = access_grant_key - res = self.client.post("/access_grants/get", json=json_payload) + res = self.client.get("/access_grants/get", params=params) return AccessGrant.from_dict(res["access_grant"]) @@ -508,6 +508,6 @@ def update( if starts_at is not None: json_payload["starts_at"] = starts_at - self.client.post("/access_grants/update", json=json_payload) + self.client.patch("/access_grants/update", json=json_payload) return None diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index ffa6796b..e98c844d 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -77,12 +77,12 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: :param access_grant_id: ID of unmanaged Access Grant to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_grant_id is not None: - json_payload["access_grant_id"] = access_grant_id + params["access_grant_id"] = access_grant_id - res = self.client.post("/access_grants/unmanaged/get", json=json_payload) + res = self.client.get("/access_grants/unmanaged/get", params=params) return UnmanagedAccessGrant.from_dict(res["access_grant"]) @@ -111,22 +111,22 @@ def list( :param user_identity_id: ID of user identity by which you want to filter the list of unmanaged Access Grants. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_entrance_id is not None: - json_payload["acs_entrance_id"] = acs_entrance_id + params["acs_entrance_id"] = acs_entrance_id if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id if limit is not None: - json_payload["limit"] = limit + params["limit"] = limit if page_cursor is not None: - json_payload["page_cursor"] = page_cursor + params["page_cursor"] = page_cursor if reservation_key is not None: - json_payload["reservation_key"] = reservation_key + params["reservation_key"] = reservation_key if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post("/access_grants/unmanaged/list", json=json_payload) + res = self.client.get("/access_grants/unmanaged/list", params=params) return [UnmanagedAccessGrant.from_dict(item) for item in res["access_grants"]] @@ -158,6 +158,6 @@ def update( if access_grant_key is not None: json_payload["access_grant_key"] = access_grant_key - self.client.post("/access_grants/unmanaged/update", json=json_payload) + self.client.patch("/access_grants/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index 87c94a57..fe674b8e 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -216,16 +216,16 @@ def delete( :param reservation_key: Reservation key of the access grant whose access methods should be deleted. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_method_id is not None: - json_payload["access_method_id"] = access_method_id + params["access_method_id"] = access_method_id if access_grant_id is not None: - json_payload["access_grant_id"] = access_grant_id + params["access_grant_id"] = access_grant_id if reservation_key is not None: - json_payload["reservation_key"] = reservation_key + params["reservation_key"] = reservation_key - self.client.post("/access_methods/delete", json=json_payload) + self.client.delete("/access_methods/delete", params=params) return None @@ -272,12 +272,12 @@ def get(self, *, access_method_id: str) -> AccessMethod: :param access_method_id: ID of access method to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_method_id is not None: - json_payload["access_method_id"] = access_method_id + params["access_method_id"] = access_method_id - res = self.client.post("/access_methods/get", json=json_payload) + res = self.client.get("/access_methods/get", params=params) return AccessMethod.from_dict(res["access_method"]) @@ -341,26 +341,26 @@ def list( :param space_id: ID of the space by which to filter the returned access methods. Must be combined with ``access_grant_id``, ``access_grant_key``, or ``acs_entrance_id``. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_code_id is not None: - json_payload["access_code_id"] = access_code_id + params["access_code_id"] = access_code_id if access_grant_id is not None: - json_payload["access_grant_id"] = access_grant_id + params["access_grant_id"] = access_grant_id if access_grant_key is not None: - json_payload["access_grant_key"] = access_grant_key + params["access_grant_key"] = access_grant_key if acs_entrance_id is not None: - json_payload["acs_entrance_id"] = acs_entrance_id + params["acs_entrance_id"] = acs_entrance_id if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if limit is not None: - json_payload["limit"] = limit + params["limit"] = limit if page_cursor is not None: - json_payload["page_cursor"] = page_cursor + params["page_cursor"] = page_cursor if space_id is not None: - json_payload["space_id"] = space_id + params["space_id"] = space_id - res = self.client.post("/access_methods/list", json=json_payload) + res = self.client.get("/access_methods/list", params=params) return [AccessMethod.from_dict(item) for item in res["access_methods"]] diff --git a/seam/routes/access_methods_unmanaged.py b/seam/routes/access_methods_unmanaged.py index bd1e1584..bea7b4b6 100644 --- a/seam/routes/access_methods_unmanaged.py +++ b/seam/routes/access_methods_unmanaged.py @@ -49,12 +49,12 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: :param access_method_id: ID of unmanaged access method to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_method_id is not None: - json_payload["access_method_id"] = access_method_id + params["access_method_id"] = access_method_id - res = self.client.post("/access_methods/unmanaged/get", json=json_payload) + res = self.client.get("/access_methods/unmanaged/get", params=params) return UnmanagedAccessMethod.from_dict(res["access_method"]) @@ -77,17 +77,17 @@ def list( :param space_id: ID of the space for which you want to retrieve all unmanaged access methods. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if access_grant_id is not None: - json_payload["access_grant_id"] = access_grant_id + params["access_grant_id"] = access_grant_id if acs_entrance_id is not None: - json_payload["acs_entrance_id"] = acs_entrance_id + params["acs_entrance_id"] = acs_entrance_id if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if space_id is not None: - json_payload["space_id"] = space_id + params["space_id"] = space_id - res = self.client.post("/access_methods/unmanaged/list", json=json_payload) + res = self.client.get("/access_methods/unmanaged/list", params=params) return [UnmanagedAccessMethod.from_dict(item) for item in res["access_methods"]] diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index 743ae2a3..0a1c65a1 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -130,7 +130,7 @@ def add_user( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.client.post("/acs/access_groups/add_user", json=json_payload) + self.client.put("/acs/access_groups/add_user", json=json_payload) return None @@ -138,12 +138,12 @@ def delete(self, *, acs_access_group_id: str) -> None: """Deletes a specified `access group `_. :param acs_access_group_id: ID of the access group that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_access_group_id is not None: - json_payload["acs_access_group_id"] = acs_access_group_id + params["acs_access_group_id"] = acs_access_group_id - self.client.post("/acs/access_groups/delete", json=json_payload) + self.client.delete("/acs/access_groups/delete", params=params) return None @@ -153,12 +153,12 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: :param acs_access_group_id: ID of the access group that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_access_group_id is not None: - json_payload["acs_access_group_id"] = acs_access_group_id + params["acs_access_group_id"] = acs_access_group_id - res = self.client.post("/acs/access_groups/get", json=json_payload) + res = self.client.get("/acs/access_groups/get", params=params) return AcsAccessGroup.from_dict(res["acs_access_group"]) @@ -181,18 +181,18 @@ def list( :param user_identity_id: ID of the user identity for which you want to retrieve all access groups. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if search is not None: - json_payload["search"] = search + params["search"] = search if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post("/acs/access_groups/list", json=json_payload) + res = self.client.get("/acs/access_groups/list", params=params) return [AcsAccessGroup.from_dict(item) for item in res["acs_access_groups"]] @@ -204,13 +204,13 @@ def list_accessible_entrances( :param acs_access_group_id: ID of the access group for which you want to retrieve all accessible entrances. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_access_group_id is not None: - json_payload["acs_access_group_id"] = acs_access_group_id + params["acs_access_group_id"] = acs_access_group_id - res = self.client.post( - "/acs/access_groups/list_accessible_entrances", json=json_payload + res = self.client.get( + "/acs/access_groups/list_accessible_entrances", params=params ) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -221,12 +221,12 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: :param acs_access_group_id: ID of the access group for which you want to retrieve all access system users. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_access_group_id is not None: - json_payload["acs_access_group_id"] = acs_access_group_id + params["acs_access_group_id"] = acs_access_group_id - res = self.client.post("/acs/access_groups/list_users", json=json_payload) + res = self.client.get("/acs/access_groups/list_users", params=params) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -245,15 +245,15 @@ def remove_user( :param user_identity_id: ID of the user identity associated with the user that you want to remove from an access group. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_access_group_id is not None: - json_payload["acs_access_group_id"] = acs_access_group_id + params["acs_access_group_id"] = acs_access_group_id if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - self.client.post("/acs/access_groups/remove_user", json=json_payload) + self.client.delete("/acs/access_groups/remove_user", params=params) return None diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index 2ad19891..9223aa4d 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -198,7 +198,7 @@ def assign( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.client.post("/acs/credentials/assign", json=json_payload) + self.client.patch("/acs/credentials/assign", json=json_payload) return None @@ -289,12 +289,12 @@ def delete(self, *, acs_credential_id: str) -> None: """Deletes a specified `credential `_. :param acs_credential_id: ID of the credential that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_credential_id is not None: - json_payload["acs_credential_id"] = acs_credential_id + params["acs_credential_id"] = acs_credential_id - self.client.post("/acs/credentials/delete", json=json_payload) + self.client.delete("/acs/credentials/delete", params=params) return None @@ -304,12 +304,12 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: :param acs_credential_id: ID of the credential that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_credential_id is not None: - json_payload["acs_credential_id"] = acs_credential_id + params["acs_credential_id"] = acs_credential_id - res = self.client.post("/acs/credentials/get", json=json_payload) + res = self.client.get("/acs/credentials/get", params=params) return AcsCredential.from_dict(res["acs_credential"]) @@ -344,28 +344,26 @@ def list( :param search: String for which to search. Filters returned credentials to include all records that satisfy a partial match using ``display_name``, ``code``, ``card_number``, ``acs_user_id`` or ``acs_credential_id``. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id if created_before is not None: - json_payload["created_before"] = created_before + params["created_before"] = created_before if is_multi_phone_sync_credential is not None: - json_payload["is_multi_phone_sync_credential"] = ( - is_multi_phone_sync_credential - ) + params["is_multi_phone_sync_credential"] = is_multi_phone_sync_credential if limit is not None: - json_payload["limit"] = limit + params["limit"] = limit if page_cursor is not None: - json_payload["page_cursor"] = page_cursor + params["page_cursor"] = page_cursor if search is not None: - json_payload["search"] = search + params["search"] = search - res = self.client.post("/acs/credentials/list", json=json_payload) + res = self.client.get("/acs/credentials/list", params=params) return [AcsCredential.from_dict(item) for item in res["acs_credentials"]] @@ -375,13 +373,13 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran :param acs_credential_id: ID of the credential for which you want to retrieve all entrances to which the credential grants access. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_credential_id is not None: - json_payload["acs_credential_id"] = acs_credential_id + params["acs_credential_id"] = acs_credential_id - res = self.client.post( - "/acs/credentials/list_accessible_entrances", json=json_payload + res = self.client.get( + "/acs/credentials/list_accessible_entrances", params=params ) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -410,7 +408,7 @@ def unassign( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.client.post("/acs/credentials/unassign", json=json_payload) + self.client.patch("/acs/credentials/unassign", json=json_payload) return None @@ -438,6 +436,6 @@ def update( if ends_at is not None: json_payload["ends_at"] = ends_at - self.client.post("/acs/credentials/update", json=json_payload) + self.client.patch("/acs/credentials/update", json=json_payload) return None diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index 1ecc91ce..7c9c379b 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -172,12 +172,12 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: :param acs_encoder_id: ID of the encoder that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_encoder_id is not None: - json_payload["acs_encoder_id"] = acs_encoder_id + params["acs_encoder_id"] = acs_encoder_id - res = self.client.post("/acs/encoders/get", json=json_payload) + res = self.client.get("/acs/encoders/get", params=params) return AcsEncoder.from_dict(res["acs_encoder"]) diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 71b462ba..01f63b0e 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -121,12 +121,12 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: :param acs_entrance_id: ID of the entrance that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_entrance_id is not None: - json_payload["acs_entrance_id"] = acs_entrance_id + params["acs_entrance_id"] = acs_entrance_id - res = self.client.post("/acs/entrances/get", json=json_payload) + res = self.client.get("/acs/entrances/get", params=params) return AcsEntrance.from_dict(res["acs_entrance"]) diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index 144a0c86..0ee1e440 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -78,12 +78,12 @@ def get(self, *, acs_system_id: str) -> AcsSystem: :param acs_system_id: ID of the access system that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id - res = self.client.post("/acs/systems/get", json=json_payload) + res = self.client.get("/acs/systems/get", params=params) return AcsSystem.from_dict(res["acs_system"]) @@ -105,16 +105,16 @@ def list( :param search: String for which to search. Filters returned access systems to include all records that satisfy a partial match using ``name`` or ``acs_system_id``. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if connected_account_id is not None: - json_payload["connected_account_id"] = connected_account_id + params["connected_account_id"] = connected_account_id if customer_key is not None: - json_payload["customer_key"] = customer_key + params["customer_key"] = customer_key if search is not None: - json_payload["search"] = search + params["search"] = search - res = self.client.post("/acs/systems/list", json=json_payload) + res = self.client.get("/acs/systems/list", params=params) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] @@ -128,14 +128,13 @@ def list_compatible_credential_manager_acs_systems( :param acs_system_id: ID of the access system for which you want to retrieve all compatible credential manager systems. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id - res = self.client.post( - "/acs/systems/list_compatible_credential_manager_acs_systems", - json=json_payload, + res = self.client.get( + "/acs/systems/list_compatible_credential_manager_acs_systems", params=params ) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 7f57044e..8cd454ed 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -272,7 +272,7 @@ def add_to_access_group( if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.client.post("/acs/users/add_to_access_group", json=json_payload) + self.client.put("/acs/users/add_to_access_group", json=json_payload) return None @@ -345,16 +345,16 @@ def delete( :param user_identity_id: ID of the user identity that you want to delete. You must provide either acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - self.client.post("/acs/users/delete", json=json_payload) + self.client.delete("/acs/users/delete", params=params) return None @@ -374,16 +374,16 @@ def get( :param user_identity_id: ID of the user identity that you want to get. You can only provide acs_user_id or user_identity_id. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post("/acs/users/get", json=json_payload) + res = self.client.get("/acs/users/get", params=params) return AcsUser.from_dict(res["acs_user"]) @@ -418,26 +418,26 @@ def list( :param user_identity_phone_number: Phone number of the user identity for which you want to retrieve all access system users, in `E.164 format `_ (for example, ``+15555550100``). :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id if created_before is not None: - json_payload["created_before"] = created_before + params["created_before"] = created_before if limit is not None: - json_payload["limit"] = limit + params["limit"] = limit if page_cursor is not None: - json_payload["page_cursor"] = page_cursor + params["page_cursor"] = page_cursor if search is not None: - json_payload["search"] = search + params["search"] = search if user_identity_email_address is not None: - json_payload["user_identity_email_address"] = user_identity_email_address + params["user_identity_email_address"] = user_identity_email_address if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id if user_identity_phone_number is not None: - json_payload["user_identity_phone_number"] = user_identity_phone_number + params["user_identity_phone_number"] = user_identity_phone_number - res = self.client.post("/acs/users/list", json=json_payload) + res = self.client.get("/acs/users/list", params=params) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -457,18 +457,16 @@ def list_accessible_entrances( :param user_identity_id: ID of the user identity for whom you want to list accessible entrances. You can only provide acs_user_id or user_identity_id. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_system_id is not None: - json_payload["acs_system_id"] = acs_system_id + params["acs_system_id"] = acs_system_id if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post( - "/acs/users/list_accessible_entrances", json=json_payload - ) + res = self.client.get("/acs/users/list_accessible_entrances", params=params) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -487,16 +485,16 @@ def remove_from_access_group( :param user_identity_id: ID of the user identity that you want to remove from an access group. You can only provide acs_user_id or user_identity_id. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_access_group_id is not None: - json_payload["acs_access_group_id"] = acs_access_group_id + params["acs_access_group_id"] = acs_access_group_id if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - self.client.post("/acs/users/remove_from_access_group", json=json_payload) + self.client.delete("/acs/users/remove_from_access_group", params=params) return None @@ -638,6 +636,6 @@ def update( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.client.post("/acs/users/update", json=json_payload) + self.client.patch("/acs/users/update", json=json_payload) return None diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index 310ff7ca..e70e8287 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -64,12 +64,12 @@ def get( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if action_attempt_id is not None: - json_payload["action_attempt_id"] = action_attempt_id + params["action_attempt_id"] = action_attempt_id - res = self.client.post("/action_attempts/get", json=json_payload) + res = self.client.get("/action_attempts/get", params=params) wait_for_action_attempt = ( self.defaults.get("wait_for_action_attempt") diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index 80ab10f6..20415340 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -208,7 +208,7 @@ def create( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - res = self.client.post("/client_sessions/create", json=json_payload) + res = self.client.put("/client_sessions/create", json=json_payload) return ClientSession.from_dict(res["client_session"]) @@ -216,12 +216,12 @@ def delete(self, *, client_session_id: str) -> None: """Deletes a `client session `_. :param client_session_id: ID of the client session that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if client_session_id is not None: - json_payload["client_session_id"] = client_session_id + params["client_session_id"] = client_session_id - self.client.post("/client_sessions/delete", json=json_payload) + self.client.delete("/client_sessions/delete", params=params) return None @@ -238,14 +238,14 @@ def get( :param user_identifier_key: User identifier key associated with the client session that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if client_session_id is not None: - json_payload["client_session_id"] = client_session_id + params["client_session_id"] = client_session_id if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key + params["user_identifier_key"] = user_identifier_key - res = self.client.post("/client_sessions/get", json=json_payload) + res = self.client.get("/client_sessions/get", params=params) return ClientSession.from_dict(res["client_session"]) @@ -332,7 +332,7 @@ def grant_access( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - self.client.post("/client_sessions/grant_access", json=json_payload) + self.client.patch("/client_sessions/grant_access", json=json_payload) return None @@ -358,20 +358,20 @@ def list( :param without_user_identifier_key: Indicates whether to retrieve only client sessions without associated user identifier keys. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if client_session_id is not None: - json_payload["client_session_id"] = client_session_id + params["client_session_id"] = client_session_id if connect_webview_id is not None: - json_payload["connect_webview_id"] = connect_webview_id + params["connect_webview_id"] = connect_webview_id if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key + params["user_identifier_key"] = user_identifier_key if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id if without_user_identifier_key is not None: - json_payload["without_user_identifier_key"] = without_user_identifier_key + params["without_user_identifier_key"] = without_user_identifier_key - res = self.client.post("/client_sessions/list", json=json_payload) + res = self.client.get("/client_sessions/list", params=params) return [ClientSession.from_dict(item) for item in res["client_sessions"]] diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index bb9a7ca6..21707cfc 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -184,12 +184,12 @@ def delete(self, *, connect_webview_id: str) -> None: You do not need to delete a Connect Webview once a user completes it. Instead, you can simply ignore completed Connect Webviews. :param connect_webview_id: ID of the Connect Webview that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if connect_webview_id is not None: - json_payload["connect_webview_id"] = connect_webview_id + params["connect_webview_id"] = connect_webview_id - self.client.post("/connect_webviews/delete", json=json_payload) + self.client.delete("/connect_webviews/delete", params=params) return None @@ -201,12 +201,12 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: :param connect_webview_id: ID of the Connect Webview that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if connect_webview_id is not None: - json_payload["connect_webview_id"] = connect_webview_id + params["connect_webview_id"] = connect_webview_id - res = self.client.post("/connect_webviews/get", json=json_payload) + res = self.client.get("/connect_webviews/get", params=params) return ConnectWebview.from_dict(res["connect_webview"]) diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index 16e9eafc..00ee084f 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -126,12 +126,12 @@ def delete(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account that you want to delete. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if connected_account_id is not None: - json_payload["connected_account_id"] = connected_account_id + params["connected_account_id"] = connected_account_id - self.client.post("/connected_accounts/delete", json=json_payload) + self.client.delete("/connected_accounts/delete", params=params) return None @@ -145,14 +145,14 @@ def get( :param email: Email address associated with the connected account that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if connected_account_id is not None: - json_payload["connected_account_id"] = connected_account_id + params["connected_account_id"] = connected_account_id if email is not None: - json_payload["email"] = email + params["email"] = email - res = self.client.post("/connected_accounts/get", json=json_payload) + res = self.client.get("/connected_accounts/get", params=params) return ConnectedAccount.from_dict(res["connected_account"]) @@ -260,6 +260,6 @@ def update( if display_name is not None: json_payload["display_name"] = display_name - self.client.post("/connected_accounts/update", json=json_payload) + self.client.patch("/connected_accounts/update", json=json_payload) return None diff --git a/seam/routes/devices.py b/seam/routes/devices.py index f11e77f0..7f38c104 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -169,14 +169,14 @@ def get( :param name: Name of the device that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if name is not None: - json_payload["name"] = name + params["name"] = name - res = self.client.post("/devices/get", json=json_payload) + res = self.client.get("/devices/get", params=params) return Device.from_dict(res["device"]) @@ -286,12 +286,12 @@ def list_device_providers( :param provider_category: Category for which you want to list providers. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if provider_category is not None: - json_payload["provider_category"] = provider_category + params["provider_category"] = provider_category - res = self.client.post("/devices/list_device_providers", json=json_payload) + res = self.client.get("/devices/list_device_providers", params=params) return [DeviceProvider.from_dict(item) for item in res["device_providers"]] @@ -350,6 +350,6 @@ def update( if properties is not None: json_payload["properties"] = properties - self.client.post("/devices/update", json=json_payload) + self.client.patch("/devices/update", json=json_payload) return None diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 5089cd05..c6e47509 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -111,14 +111,14 @@ def get( :param name: Name of the unmanaged device that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if name is not None: - json_payload["name"] = name + params["name"] = name - res = self.client.post("/devices/unmanaged/get", json=json_payload) + res = self.client.get("/devices/unmanaged/get", params=params) return UnmanagedDevice.from_dict(res["device"]) @@ -224,6 +224,6 @@ def update( if is_managed is not None: json_payload["is_managed"] = is_managed - self.client.post("/devices/unmanaged/update", json=json_payload) + self.client.patch("/devices/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/events.py b/seam/routes/events.py index 4e452b10..32a28a86 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -141,16 +141,16 @@ def get( :param event_type: Type of the event that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if event_id is not None: - json_payload["event_id"] = event_id + params["event_id"] = event_id if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if event_type is not None: - json_payload["event_type"] = event_type + params["event_type"] = event_type - res = self.client.post("/events/get", json=json_payload) + res = self.client.get("/events/get", params=params) return SeamEvent.from_dict(res["event"]) diff --git a/seam/routes/instant_keys.py b/seam/routes/instant_keys.py index bf3ab433..d5038353 100644 --- a/seam/routes/instant_keys.py +++ b/seam/routes/instant_keys.py @@ -48,12 +48,12 @@ def delete(self, *, instant_key_id: str) -> None: """Deletes a specified `Instant Key `_. :param instant_key_id: ID of the Instant Key that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if instant_key_id is not None: - json_payload["instant_key_id"] = instant_key_id + params["instant_key_id"] = instant_key_id - self.client.post("/instant_keys/delete", json=json_payload) + self.client.delete("/instant_keys/delete", params=params) return None @@ -70,14 +70,14 @@ def get( :param instant_key_url: URL of the instant key to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if instant_key_id is not None: - json_payload["instant_key_id"] = instant_key_id + params["instant_key_id"] = instant_key_id if instant_key_url is not None: - json_payload["instant_key_url"] = instant_key_url + params["instant_key_url"] = instant_key_url - res = self.client.post("/instant_keys/get", json=json_payload) + res = self.client.get("/instant_keys/get", params=params) return InstantKey.from_dict(res["instant_key"]) @@ -87,11 +87,11 @@ def list(self, *, user_identity_id: Optional[str] = None) -> List[InstantKey]: :param user_identity_id: ID of the user identity by which you want to filter the list of Instant Keys. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post("/instant_keys/list", json=json_payload) + res = self.client.get("/instant_keys/list", params=params) return [InstantKey.from_dict(item) for item in res["instant_keys"]] diff --git a/seam/routes/locks.py b/seam/routes/locks.py index 0147ed82..3647830c 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -177,14 +177,14 @@ def get( .. deprecated:: Use ``/devices/get`` instead.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if name is not None: - json_payload["name"] = name + params["name"] = name - res = self.client.post("/locks/get", json=json_payload) + res = self.client.get("/locks/get", params=params) return Device.from_dict(res["device"]) diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index 80d5e996..a382ac7c 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -149,14 +149,14 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: :param device_id: ID of the device that contains the noise threshold that you want to delete. :param noise_threshold_id: ID of the noise threshold that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if noise_threshold_id is not None: - json_payload["noise_threshold_id"] = noise_threshold_id + params["noise_threshold_id"] = noise_threshold_id - self.client.post("/noise_sensors/noise_thresholds/delete", json=json_payload) + self.client.delete("/noise_sensors/noise_thresholds/delete", params=params) return None @@ -166,12 +166,12 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: :param noise_threshold_id: ID of the noise threshold that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if noise_threshold_id is not None: - json_payload["noise_threshold_id"] = noise_threshold_id + params["noise_threshold_id"] = noise_threshold_id - res = self.client.post("/noise_sensors/noise_thresholds/get", json=json_payload) + res = self.client.get("/noise_sensors/noise_thresholds/get", params=params) return NoiseThreshold.from_dict(res["noise_threshold"]) @@ -181,14 +181,12 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: :param device_id: ID of the device for which you want to list noise thresholds. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - res = self.client.post( - "/noise_sensors/noise_thresholds/list", json=json_payload - ) + res = self.client.get("/noise_sensors/noise_thresholds/list", params=params) return [NoiseThreshold.from_dict(item) for item in res["noise_thresholds"]] @@ -236,6 +234,6 @@ def update( if starts_daily_at is not None: json_payload["starts_daily_at"] = starts_daily_at - self.client.post("/noise_sensors/noise_thresholds/update", json=json_payload) + self.client.put("/noise_sensors/noise_thresholds/update", json=json_payload) return None diff --git a/seam/routes/phones.py b/seam/routes/phones.py index cc39e6d5..dc940287 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -59,12 +59,12 @@ def deactivate(self, *, device_id: str) -> None: """Deactivates a phone, which is useful, for example, if a user has lost their phone. For more information, see `App User Lost Phone Process `_. :param device_id: Device ID of the phone that you want to deactivate.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - self.client.post("/phones/deactivate", json=json_payload) + self.client.delete("/phones/deactivate", params=params) return None @@ -74,12 +74,12 @@ def get(self, *, device_id: str) -> Phone: :param device_id: Device ID of the phone that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - res = self.client.post("/phones/get", json=json_payload) + res = self.client.get("/phones/get", params=params) return Phone.from_dict(res["phone"]) @@ -96,13 +96,13 @@ def list( :param owner_user_identity_id: ID of the user identity that represents the owner by which you want to filter the list of returned phones. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_credential_id is not None: - json_payload["acs_credential_id"] = acs_credential_id + params["acs_credential_id"] = acs_credential_id if owner_user_identity_id is not None: - json_payload["owner_user_identity_id"] = owner_user_identity_id + params["owner_user_identity_id"] = owner_user_identity_id - res = self.client.post("/phones/list", json=json_payload) + res = self.client.get("/phones/list", params=params) return [Phone.from_dict(item) for item in res["phones"]] diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index 34e6f995..675b3376 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -213,7 +213,7 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No if space_id is not None: json_payload["space_id"] = space_id - self.client.post("/spaces/add_acs_entrances", json=json_payload) + self.client.put("/spaces/add_acs_entrances", json=json_payload) return None @@ -233,7 +233,7 @@ def add_connected_account( if space_id is not None: json_payload["space_id"] = space_id - self.client.post("/spaces/add_connected_account", json=json_payload) + self.client.put("/spaces/add_connected_account", json=json_payload) return None @@ -250,7 +250,7 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: if space_id is not None: json_payload["space_id"] = space_id - self.client.post("/spaces/add_devices", json=json_payload) + self.client.put("/spaces/add_devices", json=json_payload) return None @@ -307,12 +307,12 @@ def delete(self, *, space_id: str) -> None: """Deletes a space. :param space_id: ID of the space that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if space_id is not None: - json_payload["space_id"] = space_id + params["space_id"] = space_id - self.client.post("/spaces/delete", json=json_payload) + self.client.delete("/spaces/delete", params=params) return None @@ -326,14 +326,14 @@ def get( :param space_key: Unique key of the space that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if space_id is not None: - json_payload["space_id"] = space_id + params["space_id"] = space_id if space_key is not None: - json_payload["space_key"] = space_key + params["space_key"] = space_key - res = self.client.post("/spaces/get", json=json_payload) + res = self.client.get("/spaces/get", params=params) return Space.from_dict(res["space"]) @@ -393,20 +393,20 @@ def list( :param space_key: Filter spaces by space_key. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if customer_key is not None: - json_payload["customer_key"] = customer_key + params["customer_key"] = customer_key if limit is not None: - json_payload["limit"] = limit + params["limit"] = limit if page_cursor is not None: - json_payload["page_cursor"] = page_cursor + params["page_cursor"] = page_cursor if search is not None: - json_payload["search"] = search + params["search"] = search if space_key is not None: - json_payload["space_key"] = space_key + params["space_key"] = space_key - res = self.client.post("/spaces/list", json=json_payload) + res = self.client.get("/spaces/list", params=params) return [Space.from_dict(item) for item in res["spaces"]] @@ -438,14 +438,14 @@ def remove_connected_account( :param space_id: ID of the space from which you want to remove the connected account. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if connected_account_id is not None: - json_payload["connected_account_id"] = connected_account_id + params["connected_account_id"] = connected_account_id if space_id is not None: - json_payload["space_id"] = space_id + params["space_id"] = space_id - self.client.post("/spaces/remove_connected_account", json=json_payload) + self.client.delete("/spaces/remove_connected_account", params=params) return None @@ -506,6 +506,6 @@ def update( if space_key is not None: json_payload["space_key"] = space_key - res = self.client.post("/spaces/update", json=json_payload) + res = self.client.patch("/spaces/update", json=json_payload) return Space.from_dict(res["space"]) diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index cac0bf14..0532b5f3 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -573,14 +573,14 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N :param device_id: ID of the thermostat device for which you want to delete a climate preset. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if climate_preset_key is not None: - json_payload["climate_preset_key"] = climate_preset_key + params["climate_preset_key"] = climate_preset_key if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id - self.client.post("/thermostats/delete_climate_preset", json=json_payload) + self.client.delete("/thermostats/delete_climate_preset", params=params) return None @@ -907,7 +907,7 @@ def set_temperature_threshold( if upper_limit_fahrenheit is not None: json_payload["upper_limit_fahrenheit"] = upper_limit_fahrenheit - self.client.post("/thermostats/set_temperature_threshold", json=json_payload) + self.client.patch("/thermostats/set_temperature_threshold", json=json_payload) return None @@ -980,7 +980,7 @@ def update_climate_preset( if name is not None: json_payload["name"] = name - self.client.post("/thermostats/update_climate_preset", json=json_payload) + self.client.patch("/thermostats/update_climate_preset", json=json_payload) return None diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index d1662efe..6202c7e4 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -88,12 +88,12 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: :param thermostat_daily_program_id: ID of the thermostat daily program that you want to delete. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if thermostat_daily_program_id is not None: - json_payload["thermostat_daily_program_id"] = thermostat_daily_program_id + params["thermostat_daily_program_id"] = thermostat_daily_program_id - self.client.post("/thermostats/daily_programs/delete", json=json_payload) + self.client.delete("/thermostats/daily_programs/delete", params=params) return None @@ -125,7 +125,7 @@ def update( if thermostat_daily_program_id is not None: json_payload["thermostat_daily_program_id"] = thermostat_daily_program_id - res = self.client.post("/thermostats/daily_programs/update", json=json_payload) + res = self.client.patch("/thermostats/daily_programs/update", json=json_payload) wait_for_action_attempt = ( self.defaults.get("wait_for_action_attempt") diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index 3b6d2340..4fddd011 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -157,12 +157,12 @@ def delete(self, *, thermostat_schedule_id: str) -> None: :param thermostat_schedule_id: ID of the thermostat schedule that you want to delete. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if thermostat_schedule_id is not None: - json_payload["thermostat_schedule_id"] = thermostat_schedule_id + params["thermostat_schedule_id"] = thermostat_schedule_id - self.client.post("/thermostats/schedules/delete", json=json_payload) + self.client.delete("/thermostats/schedules/delete", params=params) return None @@ -172,12 +172,12 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: :param thermostat_schedule_id: ID of the thermostat schedule that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if thermostat_schedule_id is not None: - json_payload["thermostat_schedule_id"] = thermostat_schedule_id + params["thermostat_schedule_id"] = thermostat_schedule_id - res = self.client.post("/thermostats/schedules/get", json=json_payload) + res = self.client.get("/thermostats/schedules/get", params=params) return ThermostatSchedule.from_dict(res["thermostat_schedule"]) @@ -191,14 +191,14 @@ def list( :param user_identifier_key: User identifier key by which to filter the list of returned thermostat schedules. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if user_identifier_key is not None: - json_payload["user_identifier_key"] = user_identifier_key + params["user_identifier_key"] = user_identifier_key - res = self.client.post("/thermostats/schedules/list", json=json_payload) + res = self.client.get("/thermostats/schedules/list", params=params) return [ ThermostatSchedule.from_dict(item) for item in res["thermostat_schedules"] @@ -248,6 +248,6 @@ def update( if starts_at is not None: json_payload["starts_at"] = starts_at - self.client.post("/thermostats/schedules/update", json=json_payload) + self.client.patch("/thermostats/schedules/update", json=json_payload) return None diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index 948620fe..82e9c703 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -267,7 +267,7 @@ def add_acs_user( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - self.client.post("/user_identities/add_acs_user", json=json_payload) + self.client.put("/user_identities/add_acs_user", json=json_payload) return None @@ -314,12 +314,12 @@ def delete(self, *, user_identity_id: str) -> None: """Deletes a specified `user identity `_. This deletes the user identity and all associated resources, including any `credentials `_, `acs users `_ and `client sessions `_. :param user_identity_id: ID of the user identity that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - self.client.post("/user_identities/delete", json=json_payload) + self.client.delete("/user_identities/delete", params=params) return None @@ -367,14 +367,14 @@ def get( :param user_identity_key: :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id if user_identity_key is not None: - json_payload["user_identity_key"] = user_identity_key + params["user_identity_key"] = user_identity_key - res = self.client.post("/user_identities/get", json=json_payload) + res = self.client.get("/user_identities/get", params=params) return UserIdentity.from_dict(res["user_identity"]) @@ -392,7 +392,7 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.client.post("/user_identities/grant_access_to_device", json=json_payload) + self.client.put("/user_identities/grant_access_to_device", json=json_payload) return None @@ -448,14 +448,12 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: :param user_identity_id: ID of the user identity for which you want to retrieve all accessible devices. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post( - "/user_identities/list_accessible_devices", json=json_payload - ) + res = self.client.get("/user_identities/list_accessible_devices", params=params) return [Device.from_dict(item) for item in res["devices"]] @@ -465,13 +463,13 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc :param user_identity_id: ID of the user identity for which you want to retrieve all accessible entrances. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post( - "/user_identities/list_accessible_entrances", json=json_payload + res = self.client.get( + "/user_identities/list_accessible_entrances", params=params ) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -482,12 +480,12 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: :param user_identity_id: ID of the user identity for which you want to retrieve all access systems. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post("/user_identities/list_acs_systems", json=json_payload) + res = self.client.get("/user_identities/list_acs_systems", params=params) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] @@ -497,12 +495,12 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: :param user_identity_id: ID of the user identity for which you want to retrieve all access system users. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post("/user_identities/list_acs_users", json=json_payload) + res = self.client.get("/user_identities/list_acs_users", params=params) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -513,14 +511,14 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: :param user_identity_id: ID of the user identity from which you want to remove an access system user. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if acs_user_id is not None: - json_payload["acs_user_id"] = acs_user_id + params["acs_user_id"] = acs_user_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - self.client.post("/user_identities/remove_acs_user", json=json_payload) + self.client.delete("/user_identities/remove_acs_user", params=params) return None @@ -531,14 +529,14 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N :param user_identity_id: ID of the user identity from which you want to revoke access to a device. """ - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if device_id is not None: - json_payload["device_id"] = device_id + params["device_id"] = device_id if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - self.client.post("/user_identities/revoke_access_to_device", json=json_payload) + self.client.delete("/user_identities/revoke_access_to_device", params=params) return None @@ -575,6 +573,6 @@ def update( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - self.client.post("/user_identities/update", json=json_payload) + self.client.patch("/user_identities/update", json=json_payload) return None diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index f4de6364..78e78800 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -69,12 +69,12 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: :param user_identity_id: ID of the unmanaged user identity that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if user_identity_id is not None: - json_payload["user_identity_id"] = user_identity_id + params["user_identity_id"] = user_identity_id - res = self.client.post("/user_identities/unmanaged/get", json=json_payload) + res = self.client.get("/user_identities/unmanaged/get", params=params) return UnmanagedUserIdentity.from_dict(res["user_identity"]) @@ -97,18 +97,18 @@ def list( :param search: String for which to search. Filters returned unmanaged user identities to include all records that satisfy a partial match using ``full_name``, ``phone_number``, ``email_address``, ``user_identity_id`` or ``acs_system_id``. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if created_before is not None: - json_payload["created_before"] = created_before + params["created_before"] = created_before if limit is not None: - json_payload["limit"] = limit + params["limit"] = limit if page_cursor is not None: - json_payload["page_cursor"] = page_cursor + params["page_cursor"] = page_cursor if search is not None: - json_payload["search"] = search + params["search"] = search - res = self.client.post("/user_identities/unmanaged/list", json=json_payload) + res = self.client.get("/user_identities/unmanaged/list", params=params) return [ UnmanagedUserIdentity.from_dict(item) for item in res["user_identities"] @@ -140,6 +140,6 @@ def update( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - self.client.post("/user_identities/unmanaged/update", json=json_payload) + self.client.patch("/user_identities/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/webhooks.py b/seam/routes/webhooks.py index b82cd1fa..4c8d8893 100644 --- a/seam/routes/webhooks.py +++ b/seam/routes/webhooks.py @@ -78,12 +78,12 @@ def delete(self, *, webhook_id: str) -> None: """Deletes a specified `webhook `_. :param webhook_id: ID of the webhook that you want to delete.""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if webhook_id is not None: - json_payload["webhook_id"] = webhook_id + params["webhook_id"] = webhook_id - self.client.post("/webhooks/delete", json=json_payload) + self.client.delete("/webhooks/delete", params=params) return None @@ -93,12 +93,12 @@ def get(self, *, webhook_id: str) -> Webhook: :param webhook_id: ID of the webhook that you want to get. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} if webhook_id is not None: - json_payload["webhook_id"] = webhook_id + params["webhook_id"] = webhook_id - res = self.client.post("/webhooks/get", json=json_payload) + res = self.client.get("/webhooks/get", params=params) return Webhook.from_dict(res["webhook"]) @@ -106,9 +106,9 @@ def list(self) -> List[Webhook]: """Returns a list of all `webhooks `_. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} - res = self.client.post("/webhooks/list", json=json_payload) + res = self.client.get("/webhooks/list", params=params) return [Webhook.from_dict(item) for item in res["webhooks"]] @@ -125,6 +125,6 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: if webhook_id is not None: json_payload["webhook_id"] = webhook_id - self.client.post("/webhooks/update", json=json_payload) + self.client.put("/webhooks/update", json=json_payload) return None diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index 4dd397d2..d8fc3ab5 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -177,9 +177,9 @@ def get(self) -> Workspace: """Returns the `workspace `_ associated with the authentication value. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} - res = self.client.post("/workspaces/get", json=json_payload) + res = self.client.get("/workspaces/get", params=params) return Workspace.from_dict(res["workspace"]) @@ -187,9 +187,9 @@ def list(self) -> List[Workspace]: """Returns a list of `workspaces `_ associated with the authentication value. :returns: OK""" - json_payload: Dict[str, Any] = {} + params: Dict[str, Any] = {} - res = self.client.post("/workspaces/list", json=json_payload) + res = self.client.get("/workspaces/list", params=params) return [Workspace.from_dict(item) for item in res["workspaces"]] @@ -260,6 +260,6 @@ def update( if organization_id is not None: json_payload["organization_id"] = organization_id - self.client.post("/workspaces/update", json=json_payload) + self.client.patch("/workspaces/update", json=json_payload) return None From e87287863d545ad5868093d05cef966c06c196f6 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:13:57 -0700 Subject: [PATCH 19/39] feat: require parameters when endpoints need them --- codegen/layouts/partials/method-docstring.hbs | 4 +++- codegen/layouts/partials/route-method.hbs | 4 ++++ codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 2 ++ codegen/lib/routes.ts | 1 + 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/codegen/layouts/partials/method-docstring.hbs b/codegen/layouts/partials/method-docstring.hbs index 1484936d..04c1f7eb 100644 --- a/codegen/layouts/partials/method-docstring.hbs +++ b/codegen/layouts/partials/method-docstring.hbs @@ -4,7 +4,9 @@ :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish.{{/if}}{{#unless (eq returnType "None")}} - :returns: {{{indent (pythonDoc responseDescription) 8}}}{{/unless}}{{#if isDeprecated}} + :returns: {{{indent (pythonDoc responseDescription) 8}}}{{/unless}}{{#if hasRequiredParameters}} + + :raises ValueError: At least one parameter must be provided.{{/if}}{{#if isDeprecated}} .. deprecated:: {{#if (pythonDoc deprecationMessage)}}{{{indent (pythonDoc deprecationMessage) 8}}}{{else}}This method is deprecated.{{/if}}{{/if}} \ No newline at end of file diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 4af50ca2..43f45ce5 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,5 +1,9 @@ def {{> method-signature}}: """{{> method-docstring}}""" +{{#if hasRequiredParameters}} + if not any({{#each params}}{{name}} is not None{{#unless @last}}, {{/unless}}{{/each}}): + raise ValueError("At least one parameter must be provided") +{{/if}} {{payloadVar}}: Dict[str, Any] = {} {{#each params}} diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index 50ee1874..fbfa2750 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -15,6 +15,7 @@ export interface ClassMethod { methodName: string path: string preferredMethod: string + hasRequiredParameters: boolean description: string responseDescription: string isDeprecated: boolean diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index e7a27041..dc3217ed 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -14,6 +14,7 @@ export interface MethodLayoutContext { httpVerb: string payloadVar: string payloadArg: string + hasRequiredParameters: boolean description: string responseDescription: string isDeprecated: boolean @@ -72,6 +73,7 @@ export const getMethodLayoutContext = ( name: method.methodName, path: method.path, ...getRequestLayoutContext(method.preferredMethod), + hasRequiredParameters: method.hasRequiredParameters, description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 7c874bcc..03490b8f 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -90,6 +90,7 @@ export const routes = ( methodName: endpoint.name, path: endpoint.path, preferredMethod: endpoint.request.preferredMethod, + hasRequiredParameters: endpoint.request.hasRequiredParameters, description: endpoint.description, responseDescription: endpoint.response.description, isDeprecated: endpoint.isDeprecated, From 7d548e2819bad47e0ea70b8384e2ebc0490c98a7 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:19:47 +0000 Subject: [PATCH 20/39] ci: Generate code --- seam/routes/access_codes.py | 155 +++++++++++++-- seam/routes/access_codes_simulate.py | 10 +- seam/routes/access_codes_unmanaged.py | 61 +++++- seam/routes/access_grants.py | 85 ++++++-- seam/routes/access_grants_unmanaged.py | 22 ++- seam/routes/access_methods.py | 83 ++++++-- seam/routes/access_methods_unmanaged.py | 25 ++- seam/routes/acs_access_groups.py | 64 ++++-- seam/routes/acs_credentials.py | 88 +++++++-- seam/routes/acs_encoders.py | 49 ++++- seam/routes/acs_encoders_simulate.py | 50 ++++- seam/routes/acs_entrances.py | 42 +++- seam/routes/acs_systems.py | 34 +++- seam/routes/acs_users.py | 133 +++++++++++-- seam/routes/action_attempts.py | 10 +- seam/routes/client_sessions.py | 35 +++- seam/routes/connect_webviews.py | 20 +- seam/routes/connected_accounts.py | 41 +++- seam/routes/connected_accounts_simulate.py | 8 +- seam/routes/customers.py | 31 ++- seam/routes/devices.py | 37 +++- seam/routes/devices_simulate.py | 54 +++-- seam/routes/devices_unmanaged.py | 20 +- seam/routes/events.py | 49 ++++- seam/routes/instant_keys.py | 20 +- seam/routes/locks.py | 40 +++- seam/routes/locks_simulate.py | 20 +- seam/routes/noise_sensors_noise_thresholds.py | 63 +++++- seam/routes/noise_sensors_simulate.py | 8 +- seam/routes/phones.py | 20 +- seam/routes/phones_simulate.py | 15 +- seam/routes/spaces.py | 109 +++++++++-- seam/routes/thermostats.py | 184 +++++++++++++++--- seam/routes/thermostats_daily_programs.py | 32 ++- seam/routes/thermostats_schedules.py | 62 +++++- seam/routes/thermostats_simulate.py | 27 ++- seam/routes/user_identities.py | 126 +++++++++--- seam/routes/user_identities_unmanaged.py | 22 ++- seam/routes/webhooks.py | 40 +++- seam/routes/workspaces.py | 21 +- 40 files changed, 1687 insertions(+), 328 deletions(-) diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index f79ab444..fe14f8c7 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -79,7 +79,9 @@ def create( :param use_offline_access_code: Deprecated: Use ``is_offline_access_code`` instead. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -141,7 +143,9 @@ def create_multiple( :param use_backup_access_code_pool: Indicates whether to use a `backup access code pool `_ provided by Seam. If ``true``, you can use ```/access_codes/pull_backup_access_code`` `_. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -151,7 +155,8 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non :param access_code_id: ID of the access code that you want to delete. :param device_id: ID of the device for which you want to delete the access code. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -160,7 +165,9 @@ def generate_code(self, *, device_id: str) -> AccessCode: :param device_id: ID of the device for which you want to generate a code. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -181,7 +188,9 @@ def get( :param device_id: ID of the device containing the access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -223,7 +232,9 @@ def list( :param user_identifier_key: Your user ID for the user by which to filter access codes. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -240,7 +251,9 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: :param access_code_id: ID of the access code for which you want to pull a backup access code. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -263,7 +276,8 @@ def report_device_constraints( :param min_code_length: Minimum supported code length as an integer between 4 and 20, inclusive. You can specify either ``min_code_length``/``max_code_length`` or ``supported_code_lengths``. :param supported_code_lengths: Array of supported code lengths as integers between 4 and 20, inclusive. You can specify either ``supported_code_lengths`` or ``min_code_length``/``max_code_length``. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -313,7 +327,8 @@ def update( :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. :param type: Type to which you want to convert the access code. To convert a time-bound access code to an ongoing access code, set ``type`` to ``ongoing``. See also `Changing a time-bound access code to permanent access `_. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -344,7 +359,8 @@ def update_multiple( To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called ``appearance``. This is an object with a ``name`` property and, optionally, ``first_name`` and ``last_name`` properties (for providers that break down a name into components). :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -423,7 +439,28 @@ def create( :param use_offline_access_code: Deprecated: Use ``is_offline_access_code`` instead. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + allow_external_modification is not None, + attempt_for_offline_device is not None, + code is not None, + common_code_key is not None, + ends_at is not None, + is_external_modification_allowed is not None, + is_offline_access_code is not None, + is_one_time_use is not None, + max_time_rounding is not None, + name is not None, + prefer_native_scheduling is not None, + preferred_code_length is not None, + starts_at is not None, + use_backup_access_code_pool is not None, + use_offline_access_code is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -523,7 +560,24 @@ def create_multiple( :param use_backup_access_code_pool: Indicates whether to use a `backup access code pool `_ provided by Seam. If ``true``, you can use ```/access_codes/pull_backup_access_code`` `_. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_ids is not None, + allow_external_modification is not None, + attempt_for_offline_device is not None, + behavior_when_code_cannot_be_shared is not None, + code is not None, + ends_at is not None, + is_external_modification_allowed is not None, + name is not None, + prefer_native_scheduling is not None, + preferred_code_length is not None, + starts_at is not None, + use_backup_access_code_pool is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -565,7 +619,10 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non :param access_code_id: ID of the access code that you want to delete. :param device_id: ID of the device for which you want to delete the access code. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_code_id is not None, device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_code_id is not None: @@ -582,7 +639,11 @@ def generate_code(self, *, device_id: str) -> AccessCode: :param device_id: ID of the device for which you want to generate a code. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -609,7 +670,11 @@ def get( :param device_id: ID of the device containing the access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_code_id is not None, code is not None, device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_code_id is not None: @@ -661,7 +726,22 @@ def list( :param user_identifier_key: Your user ID for the user by which to filter access codes. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_code_ids is not None, + access_grant_id is not None, + access_grant_key is not None, + access_method_id is not None, + customer_key is not None, + device_id is not None, + limit is not None, + page_cursor is not None, + search is not None, + user_identifier_key is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_code_ids is not None: @@ -702,7 +782,11 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: :param access_code_id: ID of the access code for which you want to pull a backup access code. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_code_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -733,7 +817,15 @@ def report_device_constraints( :param min_code_length: Minimum supported code length as an integer between 4 and 20, inclusive. You can specify either ``min_code_length``/``max_code_length`` or ``supported_code_lengths``. :param supported_code_lengths: Array of supported code lengths as integers between 4 and 20, inclusive. You can specify either ``supported_code_lengths`` or ``min_code_length``/``max_code_length``. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + max_code_length is not None, + min_code_length is not None, + supported_code_lengths is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -795,7 +887,22 @@ def update( :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. :param type: Type to which you want to convert the access code. To convert a time-bound access code to an ongoing access code, set ``type`` to ``ongoing``. See also `Changing a time-bound access code to permanent access `_. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_code_id is not None, + allow_external_modification is not None, + attempt_for_offline_device is not None, + code is not None, + device_id is not None, + ends_at is not None, + is_external_modification_allowed is not None, + is_managed is not None, + name is not None, + starts_at is not None, + type is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -854,7 +961,15 @@ def update_multiple( To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called ``appearance``. This is an object with a ``name`` property and, optionally, ``first_name`` and ``last_name`` properties (for providers that break down a name into components). :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + common_code_key is not None, + ends_at is not None, + name is not None, + starts_at is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if common_code_key is not None: diff --git a/seam/routes/access_codes_simulate.py b/seam/routes/access_codes_simulate.py index 3b0b0064..9c4b72b1 100644 --- a/seam/routes/access_codes_simulate.py +++ b/seam/routes/access_codes_simulate.py @@ -18,7 +18,9 @@ def create_unmanaged_access_code( :param name: Name of the simulated unmanaged access code. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -38,7 +40,11 @@ def create_unmanaged_access_code( :param name: Name of the simulated unmanaged access code. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(code is not None, device_id is not None, name is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if code is not None: diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index 176752b2..0631f38f 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -28,7 +28,8 @@ def convert_to_managed( :param force: Indicates whether to force the access code conversion. To switch management of an access code from one Seam workspace to another, set ``force`` to ``true``. :param is_external_modification_allowed: Indicates whether `external modification `_ of the access code is allowed. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -36,7 +37,8 @@ def delete(self, *, access_code_id: str) -> None: """Deletes an `unmanaged access code `_. :param access_code_id: ID of the unmanaged access code that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -57,7 +59,9 @@ def get( :param device_id: ID of the device containing the unmanaged access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -82,7 +86,9 @@ def list( :param user_identifier_key: Your user ID for the user by which to filter unmanaged access codes. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -106,7 +112,8 @@ def update( :param force: Indicates whether to force the unmanaged access code update. :param is_external_modification_allowed: Indicates whether `external modification `_ of the code is allowed. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -136,7 +143,15 @@ def convert_to_managed( :param force: Indicates whether to force the access code conversion. To switch management of an access code from one Seam workspace to another, set ``force`` to ``true``. :param is_external_modification_allowed: Indicates whether `external modification `_ of the access code is allowed. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_code_id is not None, + allow_external_modification is not None, + force is not None, + is_external_modification_allowed is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -160,7 +175,10 @@ def delete(self, *, access_code_id: str) -> None: """Deletes an `unmanaged access code `_. :param access_code_id: ID of the unmanaged access code that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_code_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_code_id is not None: @@ -187,7 +205,11 @@ def get( :param device_id: ID of the device containing the unmanaged access code that you want to get. You must specify either ``access_code_id`` or both ``device_id`` and ``code``. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_code_id is not None, code is not None, device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_code_id is not None: @@ -222,7 +244,17 @@ def list( :param user_identifier_key: Your user ID for the user by which to filter unmanaged access codes. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + limit is not None, + page_cursor is not None, + search is not None, + user_identifier_key is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -260,7 +292,16 @@ def update( :param force: Indicates whether to force the unmanaged access code update. :param is_external_modification_allowed: Indicates whether `external modification `_ of the code is allowed. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_code_id is not None, + is_managed is not None, + allow_external_modification is not None, + force is not None, + is_external_modification_allowed is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_code_id is not None: diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index dae8efa8..f28e953c 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -67,14 +67,18 @@ def create( :param starts_at: Date and time at which the validity of the new grant starts, in `ISO 8601 `_ format. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod def delete(self, *, access_grant_id: str) -> None: """Delete an Access Grant. - :param access_grant_id: ID of Access Grant to delete.""" + :param access_grant_id: ID of Access Grant to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -90,7 +94,9 @@ def get( :param access_grant_key: Unique key of Access Grant to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -112,7 +118,9 @@ def get_related( :param include: - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -174,7 +182,9 @@ def request_access_methods( :param requested_access_methods: Array of requested access methods to add to the access grant. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -198,7 +208,8 @@ def update( :param name: Display name for the access grant. :param starts_at: Date and time at which the validity of the grant starts, in `ISO 8601 `_ format. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -263,7 +274,27 @@ def create( :param starts_at: Date and time at which the validity of the new grant starts, in `ISO 8601 `_ format. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + requested_access_methods is not None, + user_identity_id is not None, + user_identity is not None, + access_grant_key is not None, + acs_entrance_ids is not None, + customization_profile_id is not None, + device_ids is not None, + ends_at is not None, + location is not None, + location_ids is not None, + name is not None, + reservation_key is not None, + space_ids is not None, + space_keys is not None, + starts_at is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if requested_access_methods is not None: @@ -304,7 +335,11 @@ def create( def delete(self, *, access_grant_id: str) -> None: """Delete an Access Grant. - :param access_grant_id: ID of Access Grant to delete.""" + :param access_grant_id: ID of Access Grant to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_grant_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_grant_id is not None: @@ -326,7 +361,11 @@ def get( :param access_grant_key: Unique key of Access Grant to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_grant_id is not None, access_grant_key is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_grant_id is not None: @@ -356,7 +395,16 @@ def get_related( :param include: - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_grant_ids is not None, + access_grant_keys is not None, + exclude is not None, + include is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_grant_ids is not None: @@ -460,7 +508,11 @@ def request_access_methods( :param requested_access_methods: Array of requested access methods to add to the access grant. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_grant_id is not None, requested_access_methods is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_grant_id is not None: @@ -494,7 +546,16 @@ def update( :param name: Display name for the access grant. :param starts_at: Date and time at which the validity of the grant starts, in `ISO 8601 `_ format. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_grant_id is not None, + access_grant_key is not None, + ends_at is not None, + name is not None, + starts_at is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_grant_id is not None: diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index e98c844d..7dd410db 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -12,7 +12,9 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: :param access_grant_id: ID of unmanaged Access Grant to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -62,7 +64,8 @@ def update( :param is_managed: Must be set to true to convert the unmanaged access grant to managed. :param access_grant_key: Unique key for the access grant. If not provided, the existing key will be preserved. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -76,7 +79,11 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: :param access_grant_id: ID of unmanaged Access Grant to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_grant_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_grant_id is not None: @@ -148,7 +155,14 @@ def update( :param is_managed: Must be set to true to convert the unmanaged access grant to managed. :param access_grant_key: Unique key for the access grant. If not provided, the existing key will be preserved. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_grant_id is not None, + is_managed is not None, + access_grant_key is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_grant_id is not None: diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index fe674b8e..39ac4088 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -32,7 +32,9 @@ def assign_card( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -50,7 +52,8 @@ def delete( :param access_grant_id: ID of access grant whose access methods should be deleted. :param reservation_key: Reservation key of the access grant whose access methods should be deleted. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -69,7 +72,9 @@ def encode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -78,7 +83,9 @@ def get(self, *, access_method_id: str) -> AccessMethod: :param access_method_id: ID of access method to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -97,7 +104,9 @@ def get_related( :param include: - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -131,7 +140,9 @@ def list( :param space_id: ID of the space by which to filter the returned access methods. Must be combined with ``access_grant_id``, ``access_grant_key``, or ``acs_entrance_id``. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -150,7 +161,9 @@ def unlock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -179,7 +192,11 @@ def assign_card( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_method_id is not None, card_number is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_method_id is not None: @@ -215,7 +232,14 @@ def delete( :param access_grant_id: ID of access grant whose access methods should be deleted. :param reservation_key: Reservation key of the access grant whose access methods should be deleted. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_method_id is not None, + access_grant_id is not None, + reservation_key is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_method_id is not None: @@ -244,7 +268,11 @@ def encode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_method_id is not None, acs_encoder_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_method_id is not None: @@ -271,7 +299,11 @@ def get(self, *, access_method_id: str) -> AccessMethod: :param access_method_id: ID of access method to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_method_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_method_id is not None: @@ -296,7 +328,13 @@ def get_related( :param include: - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_method_ids is not None, exclude is not None, include is not None + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_method_ids is not None: @@ -340,7 +378,20 @@ def list( :param space_id: ID of the space by which to filter the returned access methods. Must be combined with ``access_grant_id``, ``access_grant_key``, or ``acs_entrance_id``. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_code_id is not None, + access_grant_id is not None, + access_grant_key is not None, + acs_entrance_id is not None, + device_id is not None, + limit is not None, + page_cursor is not None, + space_id is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_code_id is not None: @@ -379,7 +430,11 @@ def unlock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_method_id is not None, acs_entrance_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_method_id is not None: diff --git a/seam/routes/access_methods_unmanaged.py b/seam/routes/access_methods_unmanaged.py index bea7b4b6..bb9218f6 100644 --- a/seam/routes/access_methods_unmanaged.py +++ b/seam/routes/access_methods_unmanaged.py @@ -12,7 +12,9 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: :param access_method_id: ID of unmanaged access method to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -34,7 +36,9 @@ def list( :param space_id: ID of the space for which you want to retrieve all unmanaged access methods. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -48,7 +52,11 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: :param access_method_id: ID of unmanaged access method to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(access_method_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_method_id is not None: @@ -76,7 +84,16 @@ def list( :param space_id: ID of the space for which you want to retrieve all unmanaged access methods. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_grant_id is not None, + acs_entrance_id is not None, + device_id is not None, + space_id is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if access_grant_id is not None: diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index 0a1c65a1..32735d65 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -21,14 +21,17 @@ def add_user( :param acs_user_id: ID of the access system user that you want to add to an access group. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the desired user identity that you want to add to an access group. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod def delete(self, *, acs_access_group_id: str) -> None: """Deletes a specified `access group `_. - :param acs_access_group_id: ID of the access group that you want to delete.""" + :param acs_access_group_id: ID of the access group that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -37,7 +40,9 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: :param acs_access_group_id: ID of the access group that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -70,7 +75,9 @@ def list_accessible_entrances( :param acs_access_group_id: ID of the access group for which you want to retrieve all accessible entrances. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -79,7 +86,9 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: :param acs_access_group_id: ID of the access group for which you want to retrieve all access system users. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -97,7 +106,8 @@ def remove_user( :param acs_user_id: ID of the access system user that you want to remove from an access group. :param user_identity_id: ID of the user identity associated with the user that you want to remove from an access group. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -120,7 +130,14 @@ def add_user( :param acs_user_id: ID of the access system user that you want to add to an access group. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the desired user identity that you want to add to an access group. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_access_group_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -137,7 +154,11 @@ def add_user( def delete(self, *, acs_access_group_id: str) -> None: """Deletes a specified `access group `_. - :param acs_access_group_id: ID of the access group that you want to delete.""" + :param acs_access_group_id: ID of the access group that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_access_group_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -152,7 +173,11 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: :param acs_access_group_id: ID of the access group that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_access_group_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -203,7 +228,11 @@ def list_accessible_entrances( :param acs_access_group_id: ID of the access group for which you want to retrieve all accessible entrances. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_access_group_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -220,7 +249,11 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: :param acs_access_group_id: ID of the access group for which you want to retrieve all access system users. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_access_group_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -244,7 +277,14 @@ def remove_user( :param acs_user_id: ID of the access system user that you want to remove from an access group. :param user_identity_id: ID of the user identity associated with the user that you want to remove from an access group. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_access_group_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_access_group_id is not None: diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index 9223aa4d..e2aa19e0 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -21,7 +21,8 @@ def assign( :param acs_user_id: ID of the access system user to whom you want to assign a credential. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity to whom you want to assign a credential. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the credential belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -70,14 +71,18 @@ def create( :param visionline_metadata: Visionline-specific metadata for the new credential. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod def delete(self, *, acs_credential_id: str) -> None: """Deletes a specified `credential `_. - :param acs_credential_id: ID of the credential that you want to delete.""" + :param acs_credential_id: ID of the credential that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -86,7 +91,9 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: :param acs_credential_id: ID of the credential that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -129,7 +136,9 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran :param acs_credential_id: ID of the credential for which you want to retrieve all entrances to which the credential grants access. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -147,7 +156,8 @@ def unassign( :param acs_user_id: ID of the access system user from which you want to unassign a credential. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity from which you want to unassign a credential. You can only provide one of acs_user_id or user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -165,7 +175,8 @@ def update( :param code: Replacement access (PIN) code for the credential that you want to update. :param ends_at: Replacement date and time at which the validity of the credential ends, in `ISO 8601 `_ format. Must be a time in the future and after the ``starts_at`` value that you set when creating the credential. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -188,7 +199,14 @@ def assign( :param acs_user_id: ID of the access system user to whom you want to assign a credential. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity to whom you want to assign a credential. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the credential belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_credential_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -247,7 +265,25 @@ def create( :param visionline_metadata: Visionline-specific metadata for the new credential. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_method is not None, + acs_system_id is not None, + acs_user_id is not None, + allowed_acs_entrance_ids is not None, + assa_abloy_vostio_metadata is not None, + code is not None, + credential_manager_acs_system_id is not None, + ends_at is not None, + is_multi_phone_sync_credential is not None, + salto_space_metadata is not None, + starts_at is not None, + user_identity_id is not None, + visionline_metadata is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_method is not None: @@ -288,7 +324,11 @@ def create( def delete(self, *, acs_credential_id: str) -> None: """Deletes a specified `credential `_. - :param acs_credential_id: ID of the credential that you want to delete.""" + :param acs_credential_id: ID of the credential that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_credential_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_credential_id is not None: @@ -303,7 +343,11 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: :param acs_credential_id: ID of the credential that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_credential_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_credential_id is not None: @@ -372,7 +416,11 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran :param acs_credential_id: ID of the credential for which you want to retrieve all entrances to which the credential grants access. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_credential_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_credential_id is not None: @@ -398,7 +446,14 @@ def unassign( :param acs_user_id: ID of the access system user from which you want to unassign a credential. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity from which you want to unassign a credential. You can only provide one of acs_user_id or user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_credential_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -426,7 +481,12 @@ def update( :param code: Replacement access (PIN) code for the credential that you want to update. :param ends_at: Replacement date and time at which the validity of the credential ends, in `ISO 8601 `_ format. Must be a time in the future and after the ``starts_at`` value that you set when creating the credential. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_credential_id is not None, code is not None, ends_at is not None + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_credential_id is not None: diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index 7c9c379b..52bb90b8 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -32,7 +32,9 @@ def encode_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -41,7 +43,9 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: :param acs_encoder_id: ID of the encoder that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -85,7 +89,9 @@ def scan_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -110,7 +116,9 @@ def scan_to_assign_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -142,7 +150,15 @@ def encode_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_encoder_id is not None, + access_method_id is not None, + acs_credential_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -171,7 +187,11 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: :param acs_encoder_id: ID of the encoder that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_encoder_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -235,7 +255,11 @@ def scan_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_encoder_id is not None, salto_ks_metadata is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -278,7 +302,16 @@ def scan_to_assign_credential( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_encoder_id is not None, + acs_user_id is not None, + salto_ks_metadata is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: diff --git a/seam/routes/acs_encoders_simulate.py b/seam/routes/acs_encoders_simulate.py index 02553872..a9d48b43 100644 --- a/seam/routes/acs_encoders_simulate.py +++ b/seam/routes/acs_encoders_simulate.py @@ -20,7 +20,8 @@ def next_credential_encode_will_fail( :param error_code: Code of the error to simulate. :param acs_credential_id: ID of the ``acs_credential`` that will fail to be encoded onto a card in the next request. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -31,7 +32,9 @@ def next_credential_encode_will_succeed( :param acs_encoder_id: ID of the ``acs_encoder`` that will be used in the next request to encode the ``acs_credential``. - :param scenario: Scenario to simulate.""" + :param scenario: Scenario to simulate. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -48,7 +51,9 @@ def next_credential_scan_will_fail( :param error_code: - :param acs_credential_id_on_seam:""" + :param acs_credential_id_on_seam: + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -65,7 +70,9 @@ def next_credential_scan_will_succeed( :param acs_credential_id_on_seam: ID of the Seam ``acs_credential`` that matches the ``acs_credential`` on the encoder in this simulation. - :param scenario: Scenario to simulate.""" + :param scenario: Scenario to simulate. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -88,7 +95,14 @@ def next_credential_encode_will_fail( :param error_code: Code of the error to simulate. :param acs_credential_id: ID of the ``acs_credential`` that will fail to be encoded onto a card in the next request. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_encoder_id is not None, + error_code is not None, + acs_credential_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -111,7 +125,11 @@ def next_credential_encode_will_succeed( :param acs_encoder_id: ID of the ``acs_encoder`` that will be used in the next request to encode the ``acs_credential``. - :param scenario: Scenario to simulate.""" + :param scenario: Scenario to simulate. + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_encoder_id is not None, scenario is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -139,7 +157,15 @@ def next_credential_scan_will_fail( :param error_code: - :param acs_credential_id_on_seam:""" + :param acs_credential_id_on_seam: + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_encoder_id is not None, + error_code is not None, + acs_credential_id_on_seam is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -168,7 +194,15 @@ def next_credential_scan_will_succeed( :param acs_credential_id_on_seam: ID of the Seam ``acs_credential`` that matches the ``acs_credential`` on the encoder in this simulation. - :param scenario: Scenario to simulate.""" + :param scenario: Scenario to simulate. + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_encoder_id is not None, + acs_credential_id_on_seam is not None, + scenario is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 01f63b0e..4be3bc85 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -13,7 +13,9 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: :param acs_entrance_id: ID of the entrance that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -31,7 +33,8 @@ def grant_access( :param acs_user_id: ID of the access system user to whom you want to grant access to an entrance. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity to whom you want to grant access to an entrance. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -87,7 +90,9 @@ def list_credentials_with_access( :param include_if: Conditions that credentials must meet to be included in the returned list. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -106,7 +111,9 @@ def unlock( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -120,7 +127,11 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: :param acs_entrance_id: ID of the entrance that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_entrance_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -144,7 +155,14 @@ def grant_access( :param acs_user_id: ID of the access system user to whom you want to grant access to an entrance. You can only provide one of acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity to whom you want to grant access to an entrance. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_entrance_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -236,7 +254,11 @@ def list_credentials_with_access( :param include_if: Conditions that credentials must meet to be included in the returned list. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_entrance_id is not None, include_if is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -265,7 +287,11 @@ def unlock( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_credential_id is not None, acs_entrance_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_credential_id is not None: diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index 0ee1e440..4b0c8f1f 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -12,7 +12,9 @@ def get(self, *, acs_system_id: str) -> AcsSystem: :param acs_system_id: ID of the access system that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -46,7 +48,9 @@ def list_compatible_credential_manager_acs_systems( :param acs_system_id: ID of the access system for which you want to retrieve all compatible credential manager systems. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -63,7 +67,9 @@ def report_devices( :param acs_encoders: Array of ACS encoders to report - :param acs_entrances: Array of ACS entrances to report""" + :param acs_entrances: Array of ACS entrances to report + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -77,7 +83,11 @@ def get(self, *, acs_system_id: str) -> AcsSystem: :param acs_system_id: ID of the access system that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_system_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_system_id is not None: @@ -127,7 +137,11 @@ def list_compatible_credential_manager_acs_systems( :param acs_system_id: ID of the access system for which you want to retrieve all compatible credential manager systems. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_system_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_system_id is not None: @@ -152,7 +166,15 @@ def report_devices( :param acs_encoders: Array of ACS encoders to report - :param acs_entrances: Array of ACS entrances to report""" + :param acs_entrances: Array of ACS entrances to report + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_system_id is not None, + acs_encoders is not None, + acs_entrances is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_system_id is not None: diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 8cd454ed..59699c10 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -15,7 +15,8 @@ def add_to_access_group( :param acs_access_group_id: ID of the access group to which you want to add an access system user. :param acs_user_id: ID of the access system user that you want to add to an access group. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -49,7 +50,9 @@ def create( :param user_identity_id: ID of the user identity with which you want to associate the new access system user. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -67,7 +70,8 @@ def delete( :param acs_user_id: ID of the access system user that you want to delete. You must provide either acs_user_id or user_identity_id :param user_identity_id: ID of the user identity that you want to delete. You must provide either acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -86,7 +90,9 @@ def get( :param user_identity_id: ID of the user identity that you want to get. You can only provide acs_user_id or user_identity_id. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -139,7 +145,9 @@ def list_accessible_entrances( :param user_identity_id: ID of the user identity for whom you want to list accessible entrances. You can only provide acs_user_id or user_identity_id. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -157,7 +165,8 @@ def remove_from_access_group( :param acs_user_id: ID of the access system user that you want to remove from an access group. You can only provide acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity that you want to remove from an access group. You can only provide acs_user_id or user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -175,7 +184,8 @@ def revoke_access_to_all_entrances( :param acs_user_id: ID of the access system user for whom you want to revoke access. You can only provide acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity for whom you want to revoke access. You can only provide acs_user_id or user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -193,7 +203,8 @@ def suspend( :param acs_user_id: ID of the access system user that you want to suspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. :param user_identity_id: ID of the user identity that you want to suspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -211,7 +222,8 @@ def unsuspend( :param acs_user_id: ID of the access system user that you want to unsuspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. :param user_identity_id: ID of the user identity that you want to unsuspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -247,7 +259,8 @@ def update( :param phone_number: Phone number of the `access system user `_ in E.164 format (for example, ``+15555550100``). :param user_identity_id: ID of the user identity that you want to update. You can only provide acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -264,7 +277,10 @@ def add_to_access_group( :param acs_access_group_id: ID of the access group to which you want to add an access system user. :param acs_user_id: ID of the access system user that you want to add to an access group. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_access_group_id is not None, acs_user_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -306,7 +322,20 @@ def create( :param user_identity_id: ID of the user identity with which you want to associate the new access system user. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_system_id is not None, + full_name is not None, + access_schedule is not None, + acs_access_group_ids is not None, + email is not None, + email_address is not None, + phone_number is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -344,7 +373,14 @@ def delete( :param acs_user_id: ID of the access system user that you want to delete. You must provide either acs_user_id or user_identity_id :param user_identity_id: ID of the user identity that you want to delete. You must provide either acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_system_id is not None: @@ -373,7 +409,15 @@ def get( :param user_identity_id: ID of the user identity that you want to get. You can only provide acs_user_id or user_identity_id. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_user_id is not None, + acs_system_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_user_id is not None: @@ -456,7 +500,15 @@ def list_accessible_entrances( :param user_identity_id: ID of the user identity for whom you want to list accessible entrances. You can only provide acs_user_id or user_identity_id. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_system_id is not None: @@ -484,7 +536,14 @@ def remove_from_access_group( :param acs_user_id: ID of the access system user that you want to remove from an access group. You can only provide acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity that you want to remove from an access group. You can only provide acs_user_id or user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_access_group_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -512,7 +571,14 @@ def revoke_access_to_all_entrances( :param acs_user_id: ID of the access system user for whom you want to revoke access. You can only provide acs_user_id or user_identity_id. :param user_identity_id: ID of the user identity for whom you want to revoke access. You can only provide acs_user_id or user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -540,7 +606,14 @@ def suspend( :param acs_user_id: ID of the access system user that you want to suspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. :param user_identity_id: ID of the user identity that you want to suspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -568,7 +641,14 @@ def unsuspend( :param acs_user_id: ID of the access system user that you want to unsuspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. :param user_identity_id: ID of the user identity that you want to unsuspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -614,7 +694,20 @@ def update( :param phone_number: Phone number of the `access system user `_ in E.164 format (for example, ``+15555550100``). :param user_identity_id: ID of the user identity that you want to update. You can only provide acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_schedule is not None, + acs_system_id is not None, + acs_user_id is not None, + email is not None, + email_address is not None, + full_name is not None, + hid_acs_system_id is not None, + phone_number is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_schedule is not None: diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index e70e8287..e293a2b5 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -20,7 +20,9 @@ def get( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -63,7 +65,11 @@ def get( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(action_attempt_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if action_attempt_id is not None: diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index 20415340..89d9d2af 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -44,7 +44,9 @@ def create( def delete(self, *, client_session_id: str) -> None: """Deletes a `client session `_. - :param client_session_id: ID of the client session that you want to delete.""" + :param client_session_id: ID of the client session that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -115,7 +117,8 @@ def grant_access( :param user_identity_id: ID of the `user identity `_ that you want to associate with the client session. :param user_identity_ids: Deprecated: Use ``user_identity_id``. IDs of the `user identities `_ that you want to associate with the client session. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -149,7 +152,9 @@ def revoke(self, *, client_session_id: str) -> None: Note that `deleting a client session `_ is a separate action. - :param client_session_id: ID of the client session that you want to revoke.""" + :param client_session_id: ID of the client session that you want to revoke. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -215,7 +220,11 @@ def create( def delete(self, *, client_session_id: str) -> None: """Deletes a `client session `_. - :param client_session_id: ID of the client session that you want to delete.""" + :param client_session_id: ID of the client session that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(client_session_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if client_session_id is not None: @@ -316,7 +325,17 @@ def grant_access( :param user_identity_id: ID of the `user identity `_ that you want to associate with the client session. :param user_identity_ids: Deprecated: Use ``user_identity_id``. IDs of the `user identities `_ that you want to associate with the client session. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + client_session_id is not None, + connect_webview_ids is not None, + connected_account_ids is not None, + user_identifier_key is not None, + user_identity_id is not None, + user_identity_ids is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if client_session_id is not None: @@ -380,7 +399,11 @@ def revoke(self, *, client_session_id: str) -> None: Note that `deleting a client session `_ is a separate action. - :param client_session_id: ID of the client session that you want to revoke.""" + :param client_session_id: ID of the client session that you want to revoke. + + :raises ValueError: At least one parameter must be provided.""" + if not any(client_session_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if client_session_id is not None: diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index 21707cfc..b2f6f383 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -58,7 +58,9 @@ def delete(self, *, connect_webview_id: str) -> None: You do not need to delete a Connect Webview once a user completes it. Instead, you can simply ignore completed Connect Webviews. - :param connect_webview_id: ID of the Connect Webview that you want to delete.""" + :param connect_webview_id: ID of the Connect Webview that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -69,7 +71,9 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: :param connect_webview_id: ID of the Connect Webview that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -183,7 +187,11 @@ def delete(self, *, connect_webview_id: str) -> None: You do not need to delete a Connect Webview once a user completes it. Instead, you can simply ignore completed Connect Webviews. - :param connect_webview_id: ID of the Connect Webview that you want to delete.""" + :param connect_webview_id: ID of the Connect Webview that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(connect_webview_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if connect_webview_id is not None: @@ -200,7 +208,11 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: :param connect_webview_id: ID of the Connect Webview that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(connect_webview_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if connect_webview_id is not None: diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index 00ee084f..aba881ff 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -24,7 +24,8 @@ def delete(self, *, connected_account_id: str) -> None: For example, if you delete a connected account with a device that has an access code, Seam sends a ``connected_account.deleted`` event, a ``device.deleted`` event, and an ``access_code.deleted`` event, but Seam does not remove the access code from the device. :param connected_account_id: ID of the connected account that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -37,7 +38,9 @@ def get( :param email: Email address associated with the connected account that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -76,7 +79,8 @@ def sync(self, *, connected_account_id: str) -> None: """Request a `connected account `_ sync attempt for the specified ``connected_account_id``. :param connected_account_id: ID of the connected account that you want to sync. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -103,7 +107,8 @@ def update( :param customer_key: The customer key to associate with this connected account. If provided, the connected account and all resources under the connected account will be moved to this customer. May only be provided if the connected account is not already associated with a customer. :param display_name: Human-readable name for the connected account, shown in the dashboard. For example, ``Booking from Airbnb House 1``. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -125,7 +130,10 @@ def delete(self, *, connected_account_id: str) -> None: For example, if you delete a connected account with a device that has an access code, Seam sends a ``connected_account.deleted`` event, a ``device.deleted`` event, and an ``access_code.deleted`` event, but Seam does not remove the access code from the device. :param connected_account_id: ID of the connected account that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(connected_account_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if connected_account_id is not None: @@ -144,7 +152,11 @@ def get( :param email: Email address associated with the connected account that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(connected_account_id is not None, email is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if connected_account_id is not None: @@ -209,7 +221,10 @@ def sync(self, *, connected_account_id: str) -> None: """Request a `connected account `_ sync attempt for the specified ``connected_account_id``. :param connected_account_id: ID of the connected account that you want to sync. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(connected_account_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if connected_account_id is not None: @@ -242,7 +257,17 @@ def update( :param customer_key: The customer key to associate with this connected account. If provided, the connected account and all resources under the connected account will be moved to this customer. May only be provided if the connected account is not already associated with a customer. :param display_name: Human-readable name for the connected account, shown in the dashboard. For example, ``Booking from Airbnb House 1``. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + connected_account_id is not None, + accepted_capabilities is not None, + automatically_manage_new_devices is not None, + custom_metadata is not None, + customer_key is not None, + display_name is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if connected_account_id is not None: diff --git a/seam/routes/connected_accounts_simulate.py b/seam/routes/connected_accounts_simulate.py index a53f14d6..af66950b 100644 --- a/seam/routes/connected_accounts_simulate.py +++ b/seam/routes/connected_accounts_simulate.py @@ -10,7 +10,8 @@ def disconnect(self, *, connected_account_id: str) -> None: """Simulates a connected account becoming disconnected from Seam. Only applicable for `sandbox workspaces `_. :param connected_account_id: ID of the connected account you want to simulate as disconnected. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -23,7 +24,10 @@ def disconnect(self, *, connected_account_id: str) -> None: """Simulates a connected account becoming disconnected from Seam. Only applicable for `sandbox workspaces `_. :param connected_account_id: ID of the connected account you want to simulate as disconnected. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(connected_account_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if connected_account_id is not None: diff --git a/seam/routes/customers.py b/seam/routes/customers.py index dc465914..9ed18ec9 100644 --- a/seam/routes/customers.py +++ b/seam/routes/customers.py @@ -180,7 +180,9 @@ def push_data( :param user_identities: List of user identities. - :param users: List of users.""" + :param users: List of users. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -430,7 +432,32 @@ def push_data( :param user_identities: List of user identities. - :param users: List of users.""" + :param users: List of users. + + :raises ValueError: At least one parameter must be provided.""" + if not any( + customer_key is not None, + access_grants is not None, + bookings is not None, + buildings is not None, + common_areas is not None, + facilities is not None, + guests is not None, + listings is not None, + properties is not None, + property_listings is not None, + reservations is not None, + residents is not None, + rooms is not None, + sites is not None, + spaces is not None, + staff_members is not None, + tenants is not None, + units is not None, + user_identities is not None, + users is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if customer_key is not None: diff --git a/seam/routes/devices.py b/seam/routes/devices.py index 7f38c104..441e9e4c 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -30,7 +30,9 @@ def get( :param name: Name of the device that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -110,7 +112,9 @@ def list_device_providers( def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: """Updates provider-specific metadata for devices. - :param devices: Array of devices with provider metadata to update""" + :param devices: Array of devices with provider metadata to update + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -138,7 +142,9 @@ def update( :param name: Name for the device. - :param properties:""" + :param properties: + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -168,7 +174,11 @@ def get( :param name: Name of the device that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, name is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -298,7 +308,11 @@ def list_device_providers( def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: """Updates provider-specific metadata for devices. - :param devices: Array of devices with provider metadata to update""" + :param devices: Array of devices with provider metadata to update + + :raises ValueError: At least one parameter must be provided.""" + if not any(devices is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if devices is not None: @@ -332,7 +346,18 @@ def update( :param name: Name for the device. - :param properties:""" + :param properties: + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + backup_access_code_pool_enabled is not None, + custom_metadata is not None, + is_managed is not None, + name is not None, + properties is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/devices_simulate.py b/seam/routes/devices_simulate.py index 29a49bc3..12b294d7 100644 --- a/seam/routes/devices_simulate.py +++ b/seam/routes/devices_simulate.py @@ -10,7 +10,8 @@ def connect(self, *, device_id: str) -> None: """Simulates connecting a device to Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. :param device_id: ID of the device that you want to simulate connecting to Seam. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -20,7 +21,9 @@ def connect_to_hub(self, *, device_id: str) -> None: implemented for August and TTLock locks. This will clear the ``hub_disconnected`` error on the device. - :param device_id: ID of the device whose hub you want to reconnect.""" + :param device_id: ID of the device whose hub you want to reconnect. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -28,7 +31,8 @@ def disconnect(self, *, device_id: str) -> None: """Simulates disconnecting a device from Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. :param device_id: ID of the device that you want to simulate disconnecting from Seam. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -39,7 +43,9 @@ def disconnect_from_hub(self, *, device_id: str) -> None: This will set the ``hub_disconnected`` error on the device, or mark the IglooHome bridge offline in sandbox. - :param device_id: ID of the device whose hub you want to disconnect.""" + :param device_id: ID of the device whose hub you want to disconnect. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -50,7 +56,9 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: :param device_id: - :param is_expired:""" + :param is_expired: + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -58,7 +66,8 @@ def remove(self, *, device_id: str) -> None: """Simulates removing a device from Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. :param device_id: ID of the device that you want to simulate removing from Seam. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -71,7 +80,10 @@ def connect(self, *, device_id: str) -> None: """Simulates connecting a device to Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. :param device_id: ID of the device that you want to simulate connecting to Seam. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -87,7 +99,11 @@ def connect_to_hub(self, *, device_id: str) -> None: implemented for August and TTLock locks. This will clear the ``hub_disconnected`` error on the device. - :param device_id: ID of the device whose hub you want to reconnect.""" + :param device_id: ID of the device whose hub you want to reconnect. + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -101,7 +117,10 @@ def disconnect(self, *, device_id: str) -> None: """Simulates disconnecting a device from Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. :param device_id: ID of the device that you want to simulate disconnecting from Seam. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -118,7 +137,11 @@ def disconnect_from_hub(self, *, device_id: str) -> None: This will set the ``hub_disconnected`` error on the device, or mark the IglooHome bridge offline in sandbox. - :param device_id: ID of the device whose hub you want to disconnect.""" + :param device_id: ID of the device whose hub you want to disconnect. + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -135,7 +158,11 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: :param device_id: - :param is_expired:""" + :param is_expired: + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, is_expired is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -151,7 +178,10 @@ def remove(self, *, device_id: str) -> None: """Simulates removing a device from Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. :param device_id: ID of the device that you want to simulate removing from Seam. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index c6e47509..03f2230e 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -20,7 +20,9 @@ def get( :param name: Name of the unmanaged device that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -88,7 +90,8 @@ def update( :param custom_metadata: Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. :param is_managed: Indicates whether the device is managed. Set this parameter to ``true`` to convert an unmanaged device to managed. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -110,7 +113,11 @@ def get( :param name: Name of the unmanaged device that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, name is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -214,7 +221,12 @@ def update( :param custom_metadata: Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. :param is_managed: Indicates whether the device is managed. Set this parameter to ``true`` to convert an unmanaged device to managed. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, custom_metadata is not None, is_managed is not None + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/events.py b/seam/routes/events.py index 32a28a86..54138841 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -22,7 +22,9 @@ def get( :param event_type: Type of the event that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -116,7 +118,9 @@ def list( :param user_identity_id: ID of the user identity for which you want to list events. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -140,7 +144,11 @@ def get( :param event_type: Type of the event that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(event_id is not None, device_id is not None, event_type is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if event_id is not None: @@ -244,7 +252,40 @@ def list( :param user_identity_id: ID of the user identity for which you want to list events. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + access_code_id is not None, + access_code_ids is not None, + access_grant_id is not None, + access_grant_ids is not None, + access_method_id is not None, + access_method_ids is not None, + acs_access_group_id is not None, + acs_credential_id is not None, + acs_encoder_id is not None, + acs_entrance_id is not None, + acs_system_id is not None, + acs_system_ids is not None, + acs_user_id is not None, + between is not None, + connect_webview_id is not None, + connected_account_id is not None, + customer_key is not None, + device_id is not None, + device_ids is not None, + event_ids is not None, + event_type is not None, + event_types is not None, + limit is not None, + since is not None, + space_id is not None, + space_ids is not None, + unstable_offset is not None, + user_identity_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if access_code_id is not None: diff --git a/seam/routes/instant_keys.py b/seam/routes/instant_keys.py index d5038353..af0020ca 100644 --- a/seam/routes/instant_keys.py +++ b/seam/routes/instant_keys.py @@ -10,7 +10,9 @@ class AbstractInstantKeys(abc.ABC): def delete(self, *, instant_key_id: str) -> None: """Deletes a specified `Instant Key `_. - :param instant_key_id: ID of the Instant Key that you want to delete.""" + :param instant_key_id: ID of the Instant Key that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -26,7 +28,9 @@ def get( :param instant_key_url: URL of the instant key to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -47,7 +51,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def delete(self, *, instant_key_id: str) -> None: """Deletes a specified `Instant Key `_. - :param instant_key_id: ID of the Instant Key that you want to delete.""" + :param instant_key_id: ID of the Instant Key that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(instant_key_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if instant_key_id is not None: @@ -69,7 +77,11 @@ def get( :param instant_key_url: URL of the instant key to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(instant_key_id is not None, instant_key_url is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if instant_key_id is not None: diff --git a/seam/routes/locks.py b/seam/routes/locks.py index 3647830c..5cd5ba08 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -32,7 +32,9 @@ def configure_auto_lock( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -47,6 +49,8 @@ def get( :returns: OK + :raises ValueError: At least one parameter must be provided. + .. deprecated:: Use ``/devices/get`` instead.""" raise NotImplementedError() @@ -92,7 +96,9 @@ def lock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -108,7 +114,9 @@ def unlock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -140,7 +148,15 @@ def configure_auto_lock( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + auto_lock_enabled is not None, + device_id is not None, + auto_lock_delay_seconds is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if auto_lock_enabled is not None: @@ -175,8 +191,12 @@ def get( :returns: OK + :raises ValueError: At least one parameter must be provided. + .. deprecated:: Use ``/devices/get`` instead.""" + if not any(device_id is not None, name is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -244,7 +264,11 @@ def lock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -276,7 +300,11 @@ def unlock_door( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/locks_simulate.py b/seam/routes/locks_simulate.py index d53fe3e0..562ca18b 100644 --- a/seam/routes/locks_simulate.py +++ b/seam/routes/locks_simulate.py @@ -23,7 +23,9 @@ def keypad_code_entry( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -39,7 +41,9 @@ def manual_lock_via_keypad( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -63,7 +67,11 @@ def keypad_code_entry( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(code is not None, device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if code is not None: @@ -97,7 +105,11 @@ def manual_lock_via_keypad( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index a382ac7c..128f53ff 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -31,7 +31,9 @@ def create( :param noise_threshold_nrs: Noise level in Noiseaware Noise Risk Score (NRS) for the new noise threshold. This parameter is only relevant for `Noiseaware sensors `_. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -40,7 +42,9 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: :param device_id: ID of the device that contains the noise threshold that you want to delete. - :param noise_threshold_id: ID of the noise threshold that you want to delete.""" + :param noise_threshold_id: ID of the noise threshold that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -49,7 +53,9 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: :param noise_threshold_id: ID of the noise threshold that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -58,7 +64,9 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: :param device_id: ID of the device for which you want to list noise thresholds. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -88,7 +96,8 @@ def update( :param noise_threshold_nrs: Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for `Noiseaware sensors `_. :param starts_daily_at: Time at which the noise threshold should become active daily. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -121,7 +130,18 @@ def create( :param noise_threshold_nrs: Noise level in Noiseaware Noise Risk Score (NRS) for the new noise threshold. This parameter is only relevant for `Noiseaware sensors `_. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + ends_daily_at is not None, + starts_daily_at is not None, + name is not None, + noise_threshold_decibels is not None, + noise_threshold_nrs is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -148,7 +168,11 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: :param device_id: ID of the device that contains the noise threshold that you want to delete. - :param noise_threshold_id: ID of the noise threshold that you want to delete.""" + :param noise_threshold_id: ID of the noise threshold that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, noise_threshold_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -165,7 +189,11 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: :param noise_threshold_id: ID of the noise threshold that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(noise_threshold_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if noise_threshold_id is not None: @@ -180,7 +208,11 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: :param device_id: ID of the device for which you want to list noise thresholds. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -216,7 +248,18 @@ def update( :param noise_threshold_nrs: Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for `Noiseaware sensors `_. :param starts_daily_at: Time at which the noise threshold should become active daily. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + noise_threshold_id is not None, + ends_daily_at is not None, + name is not None, + noise_threshold_decibels is not None, + noise_threshold_nrs is not None, + starts_daily_at is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/noise_sensors_simulate.py b/seam/routes/noise_sensors_simulate.py index 445ae5ca..44a10c68 100644 --- a/seam/routes/noise_sensors_simulate.py +++ b/seam/routes/noise_sensors_simulate.py @@ -10,7 +10,8 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: """Simulates the triggering of a `noise threshold `_ for a `noise sensor `_ in a `sandbox workspace `_. :param device_id: ID of the device for which you want to simulate the triggering of a noise threshold. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -23,7 +24,10 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: """Simulates the triggering of a `noise threshold `_ for a `noise sensor `_ in a `sandbox workspace `_. :param device_id: ID of the device for which you want to simulate the triggering of a noise threshold. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/phones.py b/seam/routes/phones.py index dc940287..837ac363 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -16,7 +16,9 @@ def simulate(self) -> AbstractPhonesSimulate: def deactivate(self, *, device_id: str) -> None: """Deactivates a phone, which is useful, for example, if a user has lost their phone. For more information, see `App User Lost Phone Process `_. - :param device_id: Device ID of the phone that you want to deactivate.""" + :param device_id: Device ID of the phone that you want to deactivate. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -25,7 +27,9 @@ def get(self, *, device_id: str) -> Phone: :param device_id: Device ID of the phone that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -58,7 +62,11 @@ def simulate(self) -> PhonesSimulate: def deactivate(self, *, device_id: str) -> None: """Deactivates a phone, which is useful, for example, if a user has lost their phone. For more information, see `App User Lost Phone Process `_. - :param device_id: Device ID of the phone that you want to deactivate.""" + :param device_id: Device ID of the phone that you want to deactivate. + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -73,7 +81,11 @@ def get(self, *, device_id: str) -> Phone: :param device_id: Device ID of the phone that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/phones_simulate.py b/seam/routes/phones_simulate.py index a1b7af2f..7d011e63 100644 --- a/seam/routes/phones_simulate.py +++ b/seam/routes/phones_simulate.py @@ -25,7 +25,9 @@ def create_sandbox_phone( :param phone_metadata: Metadata that you want to associate with the simulated phone. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -52,7 +54,16 @@ def create_sandbox_phone( :param phone_metadata: Metadata that you want to associate with the simulated phone. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + user_identity_id is not None, + assa_abloy_metadata is not None, + custom_sdk_installation_id is not None, + phone_metadata is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if user_identity_id is not None: diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index 675b3376..c4e87467 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -12,7 +12,9 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No :param acs_entrance_ids: IDs of the entrances that you want to add to the space. - :param space_id: ID of the space to which you want to add entrances.""" + :param space_id: ID of the space to which you want to add entrances. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -24,7 +26,8 @@ def add_connected_account( :param connected_account_id: ID of the connected account that you want to add to the space. :param space_id: ID of the space to which you want to add the connected account. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -33,7 +36,9 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: :param device_ids: IDs of the devices that you want to add to the space. - :param space_id: ID of the space to which you want to add devices.""" + :param space_id: ID of the space to which you want to add devices. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -64,14 +69,18 @@ def create( :param space_key: Unique key for the space within the workspace. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod def delete(self, *, space_id: str) -> None: """Deletes a space. - :param space_id: ID of the space that you want to delete.""" + :param space_id: ID of the space that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -84,7 +93,9 @@ def get( :param space_key: Unique key of the space that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -106,7 +117,9 @@ def get_related( :param space_keys: Keys of the spaces that you want to get along with their related resources. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -142,7 +155,9 @@ def remove_acs_entrances( :param acs_entrance_ids: IDs of the entrances that you want to remove from the space. - :param space_id: ID of the space from which you want to remove entrances.""" + :param space_id: ID of the space from which you want to remove entrances. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -154,7 +169,8 @@ def remove_connected_account( :param connected_account_id: ID of the connected account that you want to remove from the space. :param space_id: ID of the space from which you want to remove the connected account. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -163,7 +179,9 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: :param device_ids: IDs of the devices that you want to remove from the space. - :param space_id: ID of the space from which you want to remove devices.""" + :param space_id: ID of the space from which you want to remove devices. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -205,7 +223,11 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No :param acs_entrance_ids: IDs of the entrances that you want to add to the space. - :param space_id: ID of the space to which you want to add entrances.""" + :param space_id: ID of the space to which you want to add entrances. + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_entrance_ids is not None, space_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: @@ -225,7 +247,10 @@ def add_connected_account( :param connected_account_id: ID of the connected account that you want to add to the space. :param space_id: ID of the space to which you want to add the connected account. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(connected_account_id is not None, space_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if connected_account_id is not None: @@ -242,7 +267,11 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: :param device_ids: IDs of the devices that you want to add to the space. - :param space_id: ID of the space to which you want to add devices.""" + :param space_id: ID of the space to which you want to add devices. + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_ids is not None, space_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -281,7 +310,19 @@ def create( :param space_key: Unique key for the space within the workspace. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + name is not None, + acs_entrance_ids is not None, + connected_account_ids is not None, + customer_data is not None, + customer_key is not None, + device_ids is not None, + space_key is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if name is not None: @@ -306,7 +347,11 @@ def create( def delete(self, *, space_id: str) -> None: """Deletes a space. - :param space_id: ID of the space that you want to delete.""" + :param space_id: ID of the space that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(space_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if space_id is not None: @@ -325,7 +370,11 @@ def get( :param space_key: Unique key of the space that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(space_id is not None, space_key is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if space_id is not None: @@ -355,7 +404,16 @@ def get_related( :param space_keys: Keys of the spaces that you want to get along with their related resources. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + exclude is not None, + include is not None, + space_ids is not None, + space_keys is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if exclude is not None: @@ -417,7 +475,11 @@ def remove_acs_entrances( :param acs_entrance_ids: IDs of the entrances that you want to remove from the space. - :param space_id: ID of the space from which you want to remove entrances.""" + :param space_id: ID of the space from which you want to remove entrances. + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_entrance_ids is not None, space_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: @@ -437,7 +499,10 @@ def remove_connected_account( :param connected_account_id: ID of the connected account that you want to remove from the space. :param space_id: ID of the space from which you want to remove the connected account. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(connected_account_id is not None, space_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if connected_account_id is not None: @@ -454,7 +519,11 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: :param device_ids: IDs of the devices that you want to remove from the space. - :param space_id: ID of the space from which you want to remove devices.""" + :param space_id: ID of the space from which you want to remove devices. + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_ids is not None, space_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_ids is not None: diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index 0532b5f3..02e0f405 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -44,7 +44,9 @@ def activate_climate_preset( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -66,7 +68,9 @@ def cool( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -111,7 +115,8 @@ def create_climate_preset( :param manual_override_allowed: Deprecated: Use 'thermostat_schedule.is_override_allowed' Indicates whether a person at the thermostat or using the API can change the thermostat's settings. :param name: User-friendly name to identify the `climate preset `_. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -121,7 +126,8 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N :param climate_preset_key: Climate preset key of the climate preset that you want to delete. :param device_id: ID of the thermostat device for which you want to delete a climate preset. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -143,7 +149,9 @@ def heat( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -171,7 +179,9 @@ def heat_cool( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -215,7 +225,9 @@ def off( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -227,7 +239,8 @@ def set_fallback_climate_preset( :param climate_preset_key: Climate preset key of the climate preset that you want to set as the fallback climate preset. :param device_id: ID of the thermostat device for which you want to set the fallback climate preset. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -249,7 +262,9 @@ def set_fan_mode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -280,7 +295,9 @@ def set_hvac_mode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -304,7 +321,8 @@ def set_temperature_threshold( :param upper_limit_celsius: Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either ``upper_limit`` but not both. :param upper_limit_fahrenheit: Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either ``upper_limit`` but not both. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -349,7 +367,8 @@ def update_climate_preset( :param manual_override_allowed: Deprecated: Use 'thermostat_schedule.is_override_allowed' Indicates whether a person at the thermostat can change the thermostat's settings. See `Specifying Manual Override Permissions `_. :param name: User-friendly name to identify the `climate preset `_. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -386,7 +405,9 @@ def update_weekly_program( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -427,7 +448,11 @@ def activate_climate_preset( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(climate_preset_key is not None, device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -469,7 +494,15 @@ def cool( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -534,7 +567,23 @@ def create_climate_preset( :param manual_override_allowed: Deprecated: Use 'thermostat_schedule.is_override_allowed' Indicates whether a person at the thermostat or using the API can change the thermostat's settings. :param name: User-friendly name to identify the `climate preset `_. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + climate_preset_key is not None, + device_id is not None, + climate_preset_mode is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + ecobee_metadata is not None, + fan_mode_setting is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + hvac_mode_setting is not None, + manual_override_allowed is not None, + name is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -572,7 +621,10 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N :param climate_preset_key: Climate preset key of the climate preset that you want to delete. :param device_id: ID of the thermostat device for which you want to delete a climate preset. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(climate_preset_key is not None, device_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if climate_preset_key is not None: @@ -602,7 +654,15 @@ def heat( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -650,7 +710,17 @@ def heat_cool( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -734,7 +804,11 @@ def off( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -762,7 +836,10 @@ def set_fallback_climate_preset( :param climate_preset_key: Climate preset key of the climate preset that you want to set as the fallback climate preset. :param device_id: ID of the thermostat device for which you want to set the fallback climate preset. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(climate_preset_key is not None, device_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -792,7 +869,13 @@ def set_fan_mode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, fan_mode is not None, fan_mode_setting is not None + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -843,7 +926,18 @@ def set_hvac_mode( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + hvac_mode_setting is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -893,7 +987,16 @@ def set_temperature_threshold( :param upper_limit_celsius: Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either ``upper_limit`` but not both. :param upper_limit_fahrenheit: Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either ``upper_limit`` but not both. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + lower_limit_celsius is not None, + lower_limit_fahrenheit is not None, + upper_limit_celsius is not None, + upper_limit_fahrenheit is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -952,7 +1055,23 @@ def update_climate_preset( :param manual_override_allowed: Deprecated: Use 'thermostat_schedule.is_override_allowed' Indicates whether a person at the thermostat can change the thermostat's settings. See `Specifying Manual Override Permissions `_. :param name: User-friendly name to identify the `climate preset `_. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + climate_preset_key is not None, + device_id is not None, + climate_preset_mode is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + ecobee_metadata is not None, + fan_mode_setting is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + hvac_mode_setting is not None, + manual_override_allowed is not None, + name is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -1017,7 +1136,20 @@ def update_weekly_program( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + friday_program_id is not None, + monday_program_id is not None, + saturday_program_id is not None, + sunday_program_id is not None, + thursday_program_id is not None, + tuesday_program_id is not None, + wednesday_program_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index 6202c7e4..4b54bac1 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -19,7 +19,9 @@ def create( :param periods: Array of thermostat daily program periods. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -27,7 +29,8 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: """Deletes a thermostat daily program. :param thermostat_daily_program_id: ID of the thermostat daily program that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -49,7 +52,9 @@ def update( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -69,7 +74,11 @@ def create( :param periods: Array of thermostat daily program periods. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, name is not None, periods is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -87,7 +96,10 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: """Deletes a thermostat daily program. :param thermostat_daily_program_id: ID of the thermostat daily program that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(thermostat_daily_program_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if thermostat_daily_program_id is not None: @@ -115,7 +127,15 @@ def update( :param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + name is not None, + periods is not None, + thermostat_daily_program_id is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if name is not None: diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index 4fddd011..32662695 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -34,7 +34,9 @@ def create( :param name: Name of the thermostat schedule. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -42,7 +44,8 @@ def delete(self, *, thermostat_schedule_id: str) -> None: """Deletes a `thermostat schedule `_ for a specified `thermostat `_. :param thermostat_schedule_id: ID of the thermostat schedule that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -51,7 +54,9 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: :param thermostat_schedule_id: ID of the thermostat schedule that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -64,7 +69,9 @@ def list( :param user_identifier_key: User identifier key by which to filter the list of returned thermostat schedules. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -94,7 +101,8 @@ def update( :param name: Name of the thermostat schedule. :param starts_at: Date and time at which the thermostat schedule starts, in `ISO 8601 `_ format. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -130,7 +138,19 @@ def create( :param name: Name of the thermostat schedule. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + climate_preset_key is not None, + device_id is not None, + ends_at is not None, + starts_at is not None, + is_override_allowed is not None, + max_override_period_minutes is not None, + name is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -156,7 +176,10 @@ def delete(self, *, thermostat_schedule_id: str) -> None: """Deletes a `thermostat schedule `_ for a specified `thermostat `_. :param thermostat_schedule_id: ID of the thermostat schedule that you want to delete. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(thermostat_schedule_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if thermostat_schedule_id is not None: @@ -171,7 +194,11 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: :param thermostat_schedule_id: ID of the thermostat schedule that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(thermostat_schedule_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if thermostat_schedule_id is not None: @@ -190,7 +217,11 @@ def list( :param user_identifier_key: User identifier key by which to filter the list of returned thermostat schedules. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, user_identifier_key is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -230,7 +261,18 @@ def update( :param name: Name of the thermostat schedule. :param starts_at: Date and time at which the thermostat schedule starts, in `ISO 8601 `_ format. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + thermostat_schedule_id is not None, + climate_preset_key is not None, + ends_at is not None, + is_override_allowed is not None, + max_override_period_minutes is not None, + name is not None, + starts_at is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if thermostat_schedule_id is not None: diff --git a/seam/routes/thermostats_simulate.py b/seam/routes/thermostats_simulate.py index 8695b23f..da8c17ed 100644 --- a/seam/routes/thermostats_simulate.py +++ b/seam/routes/thermostats_simulate.py @@ -29,7 +29,8 @@ def hvac_mode_adjusted( :param heating_set_point_celsius: Heating `set point `_ in °C that you want to simulate. You must set ``heating_set_point_celsius`` or ``heating_set_point_fahrenheit``. :param heating_set_point_fahrenheit: Heating `set point `_ in °F that you want to simulate. You must set ``heating_set_point_fahrenheit`` or ``heating_set_point_celsius``. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -47,7 +48,8 @@ def temperature_reached( :param temperature_celsius: Temperature in °C that you want simulate the thermostat reaching. You must set ``temperature_celsius`` or ``temperature_fahrenheit``. :param temperature_fahrenheit: Temperature in °F that you want simulate the thermostat reaching. You must set ``temperature_fahrenheit`` or ``temperature_celsius``. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -79,7 +81,17 @@ def hvac_mode_adjusted( :param heating_set_point_celsius: Heating `set point `_ in °C that you want to simulate. You must set ``heating_set_point_celsius`` or ``heating_set_point_fahrenheit``. :param heating_set_point_fahrenheit: Heating `set point `_ in °F that you want to simulate. You must set ``heating_set_point_fahrenheit`` or ``heating_set_point_celsius``. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + hvac_mode is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -113,7 +125,14 @@ def temperature_reached( :param temperature_celsius: Temperature in °C that you want simulate the thermostat reaching. You must set ``temperature_celsius`` or ``temperature_fahrenheit``. :param temperature_fahrenheit: Temperature in °F that you want simulate the thermostat reaching. You must set ``temperature_fahrenheit`` or ``temperature_celsius``. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + device_id is not None, + temperature_celsius is not None, + temperature_fahrenheit is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index 82e9c703..f550975e 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -41,7 +41,8 @@ def add_acs_user( :param user_identity_id: ID of the user identity to which you want to add an access system user. :param user_identity_key: Key of the user identity to which you want to add an access system user. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -73,7 +74,9 @@ def create( def delete(self, *, user_identity_id: str) -> None: """Deletes a specified `user identity `_. This deletes the user identity and all associated resources, including any `credentials `_, `acs users `_ and `client sessions `_. - :param user_identity_id: ID of the user identity that you want to delete.""" + :param user_identity_id: ID of the user identity that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -92,7 +95,9 @@ def generate_instant_key( :param max_use_count: Maximum number of times the instant key can be used. Default: 1. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -108,7 +113,9 @@ def get( :param user_identity_key: - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -118,7 +125,8 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No :param device_id: ID of the managed device to which you want to grant access to the user identity. :param user_identity_id: ID of the user identity that you want to grant access to a device. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -155,7 +163,9 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: :param user_identity_id: ID of the user identity for which you want to retrieve all accessible devices. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -164,7 +174,9 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc :param user_identity_id: ID of the user identity for which you want to retrieve all accessible entrances. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -173,7 +185,9 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: :param user_identity_id: ID of the user identity for which you want to retrieve all access systems. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -182,7 +196,9 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: :param user_identity_id: ID of the user identity for which you want to retrieve all access system users. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -192,7 +208,8 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: :param acs_user_id: ID of the access system user that you want to remove from the user identity.. :param user_identity_id: ID of the user identity from which you want to remove an access system user. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -202,7 +219,8 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N :param device_id: ID of the managed device to which you want to revoke access from the user identity. :param user_identity_id: ID of the user identity from which you want to revoke access to a device. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -225,7 +243,9 @@ def update( :param phone_number: Unique phone number for the user identity. - :param user_identity_key: Unique key for the user identity.""" + :param user_identity_key: Unique key for the user identity. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -257,7 +277,14 @@ def add_acs_user( :param user_identity_id: ID of the user identity to which you want to add an access system user. :param user_identity_key: Key of the user identity to which you want to add an access system user. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + acs_user_id is not None, + user_identity_id is not None, + user_identity_key is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if acs_user_id is not None: @@ -313,7 +340,11 @@ def create( def delete(self, *, user_identity_id: str) -> None: """Deletes a specified `user identity `_. This deletes the user identity and all associated resources, including any `credentials `_, `acs users `_ and `client sessions `_. - :param user_identity_id: ID of the user identity that you want to delete.""" + :param user_identity_id: ID of the user identity that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if user_identity_id is not None: @@ -338,7 +369,15 @@ def generate_instant_key( :param max_use_count: Maximum number of times the instant key can be used. Default: 1. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + user_identity_id is not None, + customization_profile_id is not None, + max_use_count is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if user_identity_id is not None: @@ -366,7 +405,11 @@ def get( :param user_identity_key: - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(user_identity_id is not None, user_identity_key is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if user_identity_id is not None: @@ -384,7 +427,10 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No :param device_id: ID of the managed device to which you want to grant access to the user identity. :param user_identity_id: ID of the user identity that you want to grant access to a device. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, user_identity_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -447,7 +493,11 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: :param user_identity_id: ID of the user identity for which you want to retrieve all accessible devices. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if user_identity_id is not None: @@ -462,7 +512,11 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc :param user_identity_id: ID of the user identity for which you want to retrieve all accessible entrances. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if user_identity_id is not None: @@ -479,7 +533,11 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: :param user_identity_id: ID of the user identity for which you want to retrieve all access systems. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if user_identity_id is not None: @@ -494,7 +552,11 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: :param user_identity_id: ID of the user identity for which you want to retrieve all access system users. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if user_identity_id is not None: @@ -510,7 +572,10 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: :param acs_user_id: ID of the access system user that you want to remove from the user identity.. :param user_identity_id: ID of the user identity from which you want to remove an access system user. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(acs_user_id is not None, user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if acs_user_id is not None: @@ -528,7 +593,10 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N :param device_id: ID of the managed device to which you want to revoke access from the user identity. :param user_identity_id: ID of the user identity from which you want to revoke access to a device. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any(device_id is not None, user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if device_id is not None: @@ -559,7 +627,17 @@ def update( :param phone_number: Unique phone number for the user identity. - :param user_identity_key: Unique key for the user identity.""" + :param user_identity_key: Unique key for the user identity. + + :raises ValueError: At least one parameter must be provided.""" + if not any( + user_identity_id is not None, + email_address is not None, + full_name is not None, + phone_number is not None, + user_identity_key is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if user_identity_id is not None: diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index 78e78800..c05581ec 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -12,7 +12,9 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: :param user_identity_id: ID of the unmanaged user identity that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -54,7 +56,8 @@ def update( :param user_identity_id: ID of the unmanaged user identity that you want to update. :param user_identity_key: Unique key for the user identity. If not provided, the existing key will be preserved. - """ + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -68,7 +71,11 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: :param user_identity_id: ID of the unmanaged user identity that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(user_identity_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if user_identity_id is not None: @@ -130,7 +137,14 @@ def update( :param user_identity_id: ID of the unmanaged user identity that you want to update. :param user_identity_key: Unique key for the user identity. If not provided, the existing key will be preserved. - """ + + :raises ValueError: At least one parameter must be provided.""" + if not any( + is_managed is not None, + user_identity_id is not None, + user_identity_key is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if is_managed is not None: diff --git a/seam/routes/webhooks.py b/seam/routes/webhooks.py index 4c8d8893..862634cf 100644 --- a/seam/routes/webhooks.py +++ b/seam/routes/webhooks.py @@ -14,14 +14,18 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo :param event_types: Types of events that you want the new webhook to receive. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod def delete(self, *, webhook_id: str) -> None: """Deletes a specified `webhook `_. - :param webhook_id: ID of the webhook that you want to delete.""" + :param webhook_id: ID of the webhook that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -30,7 +34,9 @@ def get(self, *, webhook_id: str) -> Webhook: :param webhook_id: ID of the webhook that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -46,7 +52,9 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: :param event_types: Types of events that you want the webhook to receive. - :param webhook_id: ID of the webhook that you want to update.""" + :param webhook_id: ID of the webhook that you want to update. + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @@ -62,7 +70,11 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo :param event_types: Types of events that you want the new webhook to receive. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(url is not None, event_types is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if url is not None: @@ -77,7 +89,11 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo def delete(self, *, webhook_id: str) -> None: """Deletes a specified `webhook `_. - :param webhook_id: ID of the webhook that you want to delete.""" + :param webhook_id: ID of the webhook that you want to delete. + + :raises ValueError: At least one parameter must be provided.""" + if not any(webhook_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if webhook_id is not None: @@ -92,7 +108,11 @@ def get(self, *, webhook_id: str) -> Webhook: :param webhook_id: ID of the webhook that you want to get. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any(webhook_id is not None): + raise ValueError("At least one parameter must be provided") params: Dict[str, Any] = {} if webhook_id is not None: @@ -117,7 +137,11 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: :param event_types: Types of events that you want the webhook to receive. - :param webhook_id: ID of the webhook that you want to update.""" + :param webhook_id: ID of the webhook that you want to update. + + :raises ValueError: At least one parameter must be provided.""" + if not any(event_types is not None, webhook_id is not None): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if event_types is not None: diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index d8fc3ab5..671eab3f 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -44,7 +44,9 @@ def create( :param webview_success_message: Deprecated: Use ``connect_webview_customization.webview_success_message`` instead. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" raise NotImplementedError() @abc.abstractmethod @@ -141,7 +143,22 @@ def create( :param webview_success_message: Deprecated: Use ``connect_webview_customization.webview_success_message`` instead. - :returns: OK""" + :returns: OK + + :raises ValueError: At least one parameter must be provided.""" + if not any( + name is not None, + company_name is not None, + connect_partner_name is not None, + connect_webview_customization is not None, + is_sandbox is not None, + organization_id is not None, + webview_logo_shape is not None, + webview_primary_button_color is not None, + webview_primary_button_text_color is not None, + webview_success_message is not None, + ): + raise ValueError("At least one parameter must be provided") json_payload: Dict[str, Any] = {} if name is not None: From cb12e23c6bf97263eaf7459a07029a3f45c03e93 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:22:24 -0700 Subject: [PATCH 21/39] feat: preserve nullable request parameters --- codegen/layouts/partials/method-signature.hbs | 2 +- codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 2 ++ codegen/lib/routes.ts | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/codegen/layouts/partials/method-signature.hbs b/codegen/layouts/partials/method-signature.hbs index 10977db0..6cbad0d4 100644 --- a/codegen/layouts/partials/method-signature.hbs +++ b/codegen/layouts/partials/method-signature.hbs @@ -1 +1 @@ -{{name}}(self{{#if params}}, *{{else}}{{#if (eq returnType "ActionAttempt")}}, *{{/if}}{{/if}}{{#each params}}, {{name}}: {{#if required}}{{type}}{{else}}Optional[{{type}}] = None{{/if}}{{/each}}{{#if (eq returnType "ActionAttempt")}}, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None{{/if}}) -> {{returnType}} \ No newline at end of file +{{name}}(self{{#if params}}, *{{else}}{{#if (eq returnType "ActionAttempt")}}, *{{/if}}{{/if}}{{#each params}}, {{name}}: {{#if nullable}}Optional[{{type}}]{{else}}{{type}}{{/if}}{{#unless required}} = None{{/unless}}{{/each}}{{#if (eq returnType "ActionAttempt")}}, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None{{/if}}) -> {{returnType}} \ No newline at end of file diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index fbfa2750..1869ba97 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -9,6 +9,7 @@ export interface ClassMethodParameter { deprecationMessage: string position?: number | undefined required?: boolean | undefined + nullable?: boolean | undefined } export interface ClassMethod { diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index dc3217ed..c9269a20 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -26,6 +26,7 @@ export interface MethodLayoutContext { isDeprecated: boolean deprecationMessage: string required: boolean + nullable: boolean }> returnPath: string[] returnType: string @@ -85,6 +86,7 @@ export const getMethodLayoutContext = ( isDeprecated: parameter.isDeprecated, deprecationMessage: parameter.deprecationMessage, required: parameter.required ?? false, + nullable: parameter.nullable ?? false, })), returnPath: method.returnPath, returnType: method.returnResource, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 03490b8f..10464d3d 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -103,6 +103,7 @@ export const routes = ( deprecationMessage: parameter.deprecationMessage, position: parameter.name === idParameterName ? 0 : undefined, required: parameter.isRequired, + nullable: parameter.isNullable, })), ...resolveResponse(response), }) From b941a83c5b9e33fc6d9b529b0969a61441dc15e0 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:23:02 -0700 Subject: [PATCH 22/39] Revert "feat: preserve nullable request parameters" This reverts commit cb12e23c6bf97263eaf7459a07029a3f45c03e93. --- codegen/layouts/partials/method-signature.hbs | 2 +- codegen/lib/class-model.ts | 1 - codegen/lib/layouts/route.ts | 2 -- codegen/lib/routes.ts | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/codegen/layouts/partials/method-signature.hbs b/codegen/layouts/partials/method-signature.hbs index 6cbad0d4..10977db0 100644 --- a/codegen/layouts/partials/method-signature.hbs +++ b/codegen/layouts/partials/method-signature.hbs @@ -1 +1 @@ -{{name}}(self{{#if params}}, *{{else}}{{#if (eq returnType "ActionAttempt")}}, *{{/if}}{{/if}}{{#each params}}, {{name}}: {{#if nullable}}Optional[{{type}}]{{else}}{{type}}{{/if}}{{#unless required}} = None{{/unless}}{{/each}}{{#if (eq returnType "ActionAttempt")}}, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None{{/if}}) -> {{returnType}} \ No newline at end of file +{{name}}(self{{#if params}}, *{{else}}{{#if (eq returnType "ActionAttempt")}}, *{{/if}}{{/if}}{{#each params}}, {{name}}: {{#if required}}{{type}}{{else}}Optional[{{type}}] = None{{/if}}{{/each}}{{#if (eq returnType "ActionAttempt")}}, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None{{/if}}) -> {{returnType}} \ No newline at end of file diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index 1869ba97..fbfa2750 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -9,7 +9,6 @@ export interface ClassMethodParameter { deprecationMessage: string position?: number | undefined required?: boolean | undefined - nullable?: boolean | undefined } export interface ClassMethod { diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index c9269a20..dc3217ed 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -26,7 +26,6 @@ export interface MethodLayoutContext { isDeprecated: boolean deprecationMessage: string required: boolean - nullable: boolean }> returnPath: string[] returnType: string @@ -86,7 +85,6 @@ export const getMethodLayoutContext = ( isDeprecated: parameter.isDeprecated, deprecationMessage: parameter.deprecationMessage, required: parameter.required ?? false, - nullable: parameter.nullable ?? false, })), returnPath: method.returnPath, returnType: method.returnResource, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 10464d3d..03490b8f 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -103,7 +103,6 @@ export const routes = ( deprecationMessage: parameter.deprecationMessage, position: parameter.name === idParameterName ? 0 : undefined, required: parameter.isRequired, - nullable: parameter.isNullable, })), ...resolveResponse(response), }) From 583596ac057433890d57525bbeab57bf31b1a59e Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 00:02:27 -0700 Subject: [PATCH 23/39] Validate for required params --- codegen/layouts/partials/route-method.hbs | 3 ++- codegen/layouts/route.hbs | 1 + codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 2 ++ codegen/lib/routes.ts | 1 + seam/route.py | 16 ++++++++++++++++ seam/seam.py | 12 ++++++++++++ 7 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 seam/route.py diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 43f45ce5..5f85357f 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,8 +1,9 @@ + @route_metadata(path="{{path}}", has_required_parameters={{#if hasRequiredParameters}}True{{else}}False{{/if}}, has_pagination={{#if hasPagination}}True{{else}}False{{/if}}) def {{> method-signature}}: """{{> method-docstring}}""" {{#if hasRequiredParameters}} if not any({{#each params}}{{name}} is not None{{#unless @last}}, {{/unless}}{{/each}}): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for {{path}}") {{/if}} {{payloadVar}}: Dict[str, Any] = {} diff --git a/codegen/layouts/route.hbs b/codegen/layouts/route.hbs index 1793adfb..afaf2b43 100644 --- a/codegen/layouts/route.hbs +++ b/codegen/layouts/route.hbs @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata {{#if resourceClasses}} from ..resources import ({{#each resourceClasses}}{{this}}{{#unless @last}},{{/unless}}{{/each}}) {{/if}} diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index fbfa2750..9ecca230 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -16,6 +16,7 @@ export interface ClassMethod { path: string preferredMethod: string hasRequiredParameters: boolean + hasPagination: boolean description: string responseDescription: string isDeprecated: boolean diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index dc3217ed..be784fdc 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -15,6 +15,7 @@ export interface MethodLayoutContext { payloadVar: string payloadArg: string hasRequiredParameters: boolean + hasPagination: boolean description: string responseDescription: string isDeprecated: boolean @@ -74,6 +75,7 @@ export const getMethodLayoutContext = ( path: method.path, ...getRequestLayoutContext(method.preferredMethod), hasRequiredParameters: method.hasRequiredParameters, + hasPagination: method.hasPagination, description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 03490b8f..2d5f33d1 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -91,6 +91,7 @@ export const routes = ( path: endpoint.path, preferredMethod: endpoint.request.preferredMethod, hasRequiredParameters: endpoint.request.hasRequiredParameters, + hasPagination: endpoint.hasPagination, description: endpoint.description, responseDescription: endpoint.response.description, isDeprecated: endpoint.isDeprecated, diff --git a/seam/route.py b/seam/route.py new file mode 100644 index 00000000..e69366ca --- /dev/null +++ b/seam/route.py @@ -0,0 +1,16 @@ +from typing import Callable, TypeVar + + +F = TypeVar("F", bound=Callable) + + +def route_metadata(*, path: str, has_required_parameters: bool, has_pagination: bool): + """Attach generated route metadata to a request callable.""" + + def decorate(request: F) -> F: + request.__seam_path__ = path + request.__seam_has_required_parameters__ = has_required_parameters + request.__seam_has_pagination__ = has_pagination + return request + + return decorate diff --git a/seam/seam.py b/seam/seam.py index db96efbd..7a42d895 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -126,6 +126,18 @@ def create_paginator( >>> for connected_account in connected_accounts_paginator.flatten(): >>> print(connected_account.account_type_display_name) """ + if not getattr(request, "__seam_has_pagination__", False): + raise ValueError("Cannot create a paginator for a non-paginated endpoint") + + has_required_parameters = getattr( + request, "__seam_has_required_parameters__", False + ) + if has_required_parameters and ( + not params or not any(value is not None for value in params.values()) + ): + path = getattr(request, "__seam_path__", "this endpoint") + raise ValueError(f"At least one parameter is required for {path}") + return SeamPaginator(self.client, request, params) @classmethod From 255704dff6a78459d5895395458ed4c5507abb39 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 07:03:16 +0000 Subject: [PATCH 24/39] ci: Generate code --- seam/routes/access_codes.py | 79 ++++++++++-- seam/routes/access_codes_simulate.py | 10 +- seam/routes/access_codes_unmanaged.py | 46 ++++++- seam/routes/access_grants.py | 50 +++++++- seam/routes/access_grants_unmanaged.py | 24 +++- seam/routes/access_methods.py | 60 +++++++-- seam/routes/access_methods_unmanaged.py | 19 ++- seam/routes/acs.py | 1 + seam/routes/acs_access_groups.py | 60 ++++++++- seam/routes/acs_credentials.py | 65 ++++++++-- seam/routes/acs_encoders.py | 36 +++++- seam/routes/acs_encoders_simulate.py | 37 +++++- seam/routes/acs_entrances.py | 36 +++++- seam/routes/acs_systems.py | 27 +++- seam/routes/acs_users.py | 74 +++++++++-- seam/routes/action_attempts.py | 11 +- seam/routes/client_sessions.py | 46 ++++++- seam/routes/connect_webviews.py | 27 +++- seam/routes/connected_accounts.py | 42 ++++++- seam/routes/connected_accounts_simulate.py | 10 +- seam/routes/customers.py | 18 ++- seam/routes/devices.py | 28 ++++- seam/routes/devices_simulate.py | 55 ++++++++- seam/routes/devices_unmanaged.py | 24 +++- seam/routes/events.py | 11 +- seam/routes/instant_keys.py | 16 ++- seam/routes/locks.py | 30 ++++- seam/routes/locks_simulate.py | 19 ++- seam/routes/noise_sensors.py | 4 + seam/routes/noise_sensors_noise_thresholds.py | 46 ++++++- seam/routes/noise_sensors_simulate.py | 10 +- seam/routes/phones.py | 16 ++- seam/routes/phones_simulate.py | 10 +- seam/routes/spaces.py | 81 ++++++++++-- seam/routes/thermostats.py | 109 +++++++++++++++-- seam/routes/thermostats_daily_programs.py | 28 ++++- seam/routes/thermostats_schedules.py | 46 ++++++- seam/routes/thermostats_simulate.py | 19 ++- seam/routes/user_identities.py | 115 ++++++++++++++++-- seam/routes/user_identities_unmanaged.py | 24 +++- seam/routes/webhooks.py | 24 +++- seam/routes/workspaces.py | 22 +++- 42 files changed, 1350 insertions(+), 165 deletions(-) diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index fe14f8c7..31a89427 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import AccessCode from .access_codes_simulate import AbstractAccessCodesSimulate, AccessCodesSimulate from .access_codes_unmanaged import AbstractAccessCodesUnmanaged, AccessCodesUnmanaged @@ -379,6 +380,9 @@ def simulate(self) -> AccessCodesSimulate: def unmanaged(self) -> AccessCodesUnmanaged: return self._unmanaged + @route_metadata( + path="/access_codes/create", has_required_parameters=True, has_pagination=False + ) def create( self, *, @@ -460,7 +464,9 @@ def create( use_backup_access_code_pool is not None, use_offline_access_code is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/create" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -502,6 +508,11 @@ def create( return AccessCode.from_dict(res["access_code"]) + @route_metadata( + path="/access_codes/create_multiple", + has_required_parameters=True, + has_pagination=False, + ) def create_multiple( self, *, @@ -577,7 +588,9 @@ def create_multiple( starts_at is not None, use_backup_access_code_pool is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/create_multiple" + ) json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -613,6 +626,9 @@ def create_multiple( return [AccessCode.from_dict(item) for item in res["access_codes"]] + @route_metadata( + path="/access_codes/delete", has_required_parameters=True, has_pagination=False + ) def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> None: """Deletes an `access code `_. @@ -622,7 +638,9 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non :raises ValueError: At least one parameter must be provided.""" if not any(access_code_id is not None, device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/delete" + ) params: Dict[str, Any] = {} if access_code_id is not None: @@ -634,6 +652,11 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non return None + @route_metadata( + path="/access_codes/generate_code", + has_required_parameters=True, + has_pagination=False, + ) def generate_code(self, *, device_id: str) -> AccessCode: """Generates a code for an `access code `_, given a device ID. @@ -643,7 +666,9 @@ def generate_code(self, *, device_id: str) -> AccessCode: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/generate_code" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -653,6 +678,9 @@ def generate_code(self, *, device_id: str) -> AccessCode: return AccessCode.from_dict(res["generated_code"]) + @route_metadata( + path="/access_codes/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, @@ -674,7 +702,7 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(access_code_id is not None, code is not None, device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /access_codes/get") params: Dict[str, Any] = {} if access_code_id is not None: @@ -688,6 +716,9 @@ def get( return AccessCode.from_dict(res["access_code"]) + @route_metadata( + path="/access_codes/list", has_required_parameters=True, has_pagination=True + ) def list( self, *, @@ -741,7 +772,9 @@ def list( search is not None, user_identifier_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/list" + ) json_payload: Dict[str, Any] = {} if access_code_ids is not None: @@ -769,6 +802,11 @@ def list( return [AccessCode.from_dict(item) for item in res["access_codes"]] + @route_metadata( + path="/access_codes/pull_backup_access_code", + has_required_parameters=True, + has_pagination=False, + ) def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: """Retrieves a backup access code for an `access code `_. See also `Managing Backup Access Codes `_. @@ -786,7 +824,9 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: :raises ValueError: At least one parameter must be provided.""" if not any(access_code_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/pull_backup_access_code" + ) json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -798,6 +838,11 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: return AccessCode.from_dict(res["access_code"]) + @route_metadata( + path="/access_codes/report_device_constraints", + has_required_parameters=True, + has_pagination=False, + ) def report_device_constraints( self, *, @@ -825,7 +870,9 @@ def report_device_constraints( min_code_length is not None, supported_code_lengths is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/report_device_constraints" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -841,6 +888,9 @@ def report_device_constraints( return None + @route_metadata( + path="/access_codes/update", has_required_parameters=True, has_pagination=False + ) def update( self, *, @@ -902,7 +952,9 @@ def update( starts_at is not None, type is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/update" + ) json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -934,6 +986,11 @@ def update( return None + @route_metadata( + path="/access_codes/update_multiple", + has_required_parameters=True, + has_pagination=False, + ) def update_multiple( self, *, @@ -969,7 +1026,9 @@ def update_multiple( name is not None, starts_at is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/update_multiple" + ) json_payload: Dict[str, Any] = {} if common_code_key is not None: diff --git a/seam/routes/access_codes_simulate.py b/seam/routes/access_codes_simulate.py index 9c4b72b1..50790597 100644 --- a/seam/routes/access_codes_simulate.py +++ b/seam/routes/access_codes_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import UnmanagedAccessCode @@ -29,6 +30,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/access_codes/simulate/create_unmanaged_access_code", + has_required_parameters=True, + has_pagination=False, + ) def create_unmanaged_access_code( self, *, code: str, device_id: str, name: str ) -> UnmanagedAccessCode: @@ -44,7 +50,9 @@ def create_unmanaged_access_code( :raises ValueError: At least one parameter must be provided.""" if not any(code is not None, device_id is not None, name is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code" + ) json_payload: Dict[str, Any] = {} if code is not None: diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index 0631f38f..d750151e 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import UnmanagedAccessCode @@ -122,6 +123,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/access_codes/unmanaged/convert_to_managed", + has_required_parameters=True, + has_pagination=False, + ) def convert_to_managed( self, *, @@ -151,7 +157,9 @@ def convert_to_managed( force is not None, is_external_modification_allowed is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/convert_to_managed" + ) json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -171,6 +179,11 @@ def convert_to_managed( return None + @route_metadata( + path="/access_codes/unmanaged/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, access_code_id: str) -> None: """Deletes an `unmanaged access code `_. @@ -178,7 +191,9 @@ def delete(self, *, access_code_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(access_code_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/delete" + ) params: Dict[str, Any] = {} if access_code_id is not None: @@ -188,6 +203,11 @@ def delete(self, *, access_code_id: str) -> None: return None + @route_metadata( + path="/access_codes/unmanaged/get", + has_required_parameters=True, + has_pagination=False, + ) def get( self, *, @@ -209,7 +229,9 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(access_code_id is not None, code is not None, device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/get" + ) params: Dict[str, Any] = {} if access_code_id is not None: @@ -223,6 +245,11 @@ def get( return UnmanagedAccessCode.from_dict(res["access_code"]) + @route_metadata( + path="/access_codes/unmanaged/list", + has_required_parameters=True, + has_pagination=True, + ) def list( self, *, @@ -254,7 +281,9 @@ def list( search is not None, user_identifier_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/list" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -272,6 +301,11 @@ def list( return [UnmanagedAccessCode.from_dict(item) for item in res["access_codes"]] + @route_metadata( + path="/access_codes/unmanaged/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -301,7 +335,9 @@ def update( force is not None, is_external_modification_allowed is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/update" + ) json_payload: Dict[str, Any] = {} if access_code_id is not None: diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index f28e953c..c4a259e6 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import AccessGrant, Batch from .access_grants_unmanaged import ( AbstractAccessGrantsUnmanaged, @@ -223,6 +224,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def unmanaged(self) -> AccessGrantsUnmanaged: return self._unmanaged + @route_metadata( + path="/access_grants/create", has_required_parameters=True, has_pagination=False + ) def create( self, *, @@ -294,7 +298,9 @@ def create( space_keys is not None, starts_at is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/create" + ) json_payload: Dict[str, Any] = {} if requested_access_methods is not None: @@ -332,6 +338,9 @@ def create( return AccessGrant.from_dict(res["access_grant"]) + @route_metadata( + path="/access_grants/delete", has_required_parameters=True, has_pagination=False + ) def delete(self, *, access_grant_id: str) -> None: """Delete an Access Grant. @@ -339,7 +348,9 @@ def delete(self, *, access_grant_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(access_grant_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/delete" + ) params: Dict[str, Any] = {} if access_grant_id is not None: @@ -349,6 +360,9 @@ def delete(self, *, access_grant_id: str) -> None: return None + @route_metadata( + path="/access_grants/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, @@ -365,7 +379,9 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(access_grant_id is not None, access_grant_key is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/get" + ) params: Dict[str, Any] = {} if access_grant_id is not None: @@ -377,6 +393,11 @@ def get( return AccessGrant.from_dict(res["access_grant"]) + @route_metadata( + path="/access_grants/get_related", + has_required_parameters=True, + has_pagination=False, + ) def get_related( self, *, @@ -404,7 +425,9 @@ def get_related( exclude is not None, include is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/get_related" + ) json_payload: Dict[str, Any] = {} if access_grant_ids is not None: @@ -420,6 +443,9 @@ def get_related( return Batch.from_dict(res["batch"]) + @route_metadata( + path="/access_grants/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -499,6 +525,11 @@ def list( return [AccessGrant.from_dict(item) for item in res["access_grants"]] + @route_metadata( + path="/access_grants/request_access_methods", + has_required_parameters=True, + has_pagination=False, + ) def request_access_methods( self, *, access_grant_id: str, requested_access_methods: List[Dict[str, Any]] ) -> AccessGrant: @@ -512,7 +543,9 @@ def request_access_methods( :raises ValueError: At least one parameter must be provided.""" if not any(access_grant_id is not None, requested_access_methods is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/request_access_methods" + ) json_payload: Dict[str, Any] = {} if access_grant_id is not None: @@ -526,6 +559,9 @@ def request_access_methods( return AccessGrant.from_dict(res["access_grant"]) + @route_metadata( + path="/access_grants/update", has_required_parameters=True, has_pagination=False + ) def update( self, *, @@ -555,7 +591,9 @@ def update( name is not None, starts_at is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/update" + ) json_payload: Dict[str, Any] = {} if access_grant_id is not None: diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index 7dd410db..e9f3042c 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import UnmanagedAccessGrant @@ -74,6 +75,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/access_grants/unmanaged/get", + has_required_parameters=True, + has_pagination=False, + ) def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: """Get an unmanaged Access Grant (where is_managed = false). @@ -83,7 +89,9 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: :raises ValueError: At least one parameter must be provided.""" if not any(access_grant_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/unmanaged/get" + ) params: Dict[str, Any] = {} if access_grant_id is not None: @@ -93,6 +101,11 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: return UnmanagedAccessGrant.from_dict(res["access_grant"]) + @route_metadata( + path="/access_grants/unmanaged/list", + has_required_parameters=False, + has_pagination=True, + ) def list( self, *, @@ -137,6 +150,11 @@ def list( return [UnmanagedAccessGrant.from_dict(item) for item in res["access_grants"]] + @route_metadata( + path="/access_grants/unmanaged/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -162,7 +180,9 @@ def update( is_managed is not None, access_grant_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_grants/unmanaged/update" + ) json_payload: Dict[str, Any] = {} if access_grant_id is not None: diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index 39ac4088..8d369b09 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ActionAttempt, AccessMethod, Batch from .access_methods_unmanaged import ( AbstractAccessMethodsUnmanaged, @@ -177,6 +178,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def unmanaged(self) -> AccessMethodsUnmanaged: return self._unmanaged + @route_metadata( + path="/access_methods/assign_card", + has_required_parameters=True, + has_pagination=False, + ) def assign_card( self, *, @@ -196,7 +202,9 @@ def assign_card( :raises ValueError: At least one parameter must be provided.""" if not any(access_method_id is not None, card_number is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/assign_card" + ) json_payload: Dict[str, Any] = {} if access_method_id is not None: @@ -218,6 +226,11 @@ def assign_card( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/access_methods/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete( self, *, @@ -239,7 +252,9 @@ def delete( access_grant_id is not None, reservation_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/delete" + ) params: Dict[str, Any] = {} if access_method_id is not None: @@ -253,6 +268,11 @@ def delete( return None + @route_metadata( + path="/access_methods/encode", + has_required_parameters=True, + has_pagination=False, + ) def encode( self, *, @@ -272,7 +292,9 @@ def encode( :raises ValueError: At least one parameter must be provided.""" if not any(access_method_id is not None, acs_encoder_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/encode" + ) json_payload: Dict[str, Any] = {} if access_method_id is not None: @@ -294,6 +316,9 @@ def encode( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/access_methods/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, access_method_id: str) -> AccessMethod: """Gets an access method. @@ -303,7 +328,9 @@ def get(self, *, access_method_id: str) -> AccessMethod: :raises ValueError: At least one parameter must be provided.""" if not any(access_method_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/get" + ) params: Dict[str, Any] = {} if access_method_id is not None: @@ -313,6 +340,11 @@ def get(self, *, access_method_id: str) -> AccessMethod: return AccessMethod.from_dict(res["access_method"]) + @route_metadata( + path="/access_methods/get_related", + has_required_parameters=True, + has_pagination=False, + ) def get_related( self, *, @@ -334,7 +366,9 @@ def get_related( if not any( access_method_ids is not None, exclude is not None, include is not None ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/get_related" + ) json_payload: Dict[str, Any] = {} if access_method_ids is not None: @@ -348,6 +382,9 @@ def get_related( return Batch.from_dict(res["batch"]) + @route_metadata( + path="/access_methods/list", has_required_parameters=True, has_pagination=True + ) def list( self, *, @@ -391,7 +428,9 @@ def list( page_cursor is not None, space_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/list" + ) params: Dict[str, Any] = {} if access_code_id is not None: @@ -415,6 +454,11 @@ def list( return [AccessMethod.from_dict(item) for item in res["access_methods"]] + @route_metadata( + path="/access_methods/unlock_door", + has_required_parameters=True, + has_pagination=False, + ) def unlock_door( self, *, @@ -434,7 +478,9 @@ def unlock_door( :raises ValueError: At least one parameter must be provided.""" if not any(access_method_id is not None, acs_entrance_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/unlock_door" + ) json_payload: Dict[str, Any] = {} if access_method_id is not None: diff --git a/seam/routes/access_methods_unmanaged.py b/seam/routes/access_methods_unmanaged.py index bb9218f6..c9a39c79 100644 --- a/seam/routes/access_methods_unmanaged.py +++ b/seam/routes/access_methods_unmanaged.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import UnmanagedAccessMethod @@ -47,6 +48,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/access_methods/unmanaged/get", + has_required_parameters=True, + has_pagination=False, + ) def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: """Gets an unmanaged access method (where is_managed = false). @@ -56,7 +62,9 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: :raises ValueError: At least one parameter must be provided.""" if not any(access_method_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/unmanaged/get" + ) params: Dict[str, Any] = {} if access_method_id is not None: @@ -66,6 +74,11 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: return UnmanagedAccessMethod.from_dict(res["access_method"]) + @route_metadata( + path="/access_methods/unmanaged/list", + has_required_parameters=True, + has_pagination=False, + ) def list( self, *, @@ -93,7 +106,9 @@ def list( device_id is not None, space_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /access_methods/unmanaged/list" + ) params: Dict[str, Any] = {} if access_grant_id is not None: diff --git a/seam/routes/acs.py b/seam/routes/acs.py index 8207cf12..125f3c31 100644 --- a/seam/routes/acs.py +++ b/seam/routes/acs.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from .acs_access_groups import AbstractAcsAccessGroups, AcsAccessGroups from .acs_credentials import AbstractAcsCredentials, AcsCredentials from .acs_encoders import AbstractAcsEncoders, AcsEncoders diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index 32735d65..fa3ee46f 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import AcsAccessGroup, AcsEntrance, AcsUser @@ -116,6 +117,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/acs/access_groups/add_user", + has_required_parameters=True, + has_pagination=False, + ) def add_user( self, *, @@ -137,7 +143,9 @@ def add_user( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/access_groups/add_user" + ) json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -151,6 +159,11 @@ def add_user( return None + @route_metadata( + path="/acs/access_groups/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, acs_access_group_id: str) -> None: """Deletes a specified `access group `_. @@ -158,7 +171,9 @@ def delete(self, *, acs_access_group_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(acs_access_group_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/access_groups/delete" + ) params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -168,6 +183,11 @@ def delete(self, *, acs_access_group_id: str) -> None: return None + @route_metadata( + path="/acs/access_groups/get", + has_required_parameters=True, + has_pagination=False, + ) def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: """Returns a specified `access group `_. @@ -177,7 +197,9 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: :raises ValueError: At least one parameter must be provided.""" if not any(acs_access_group_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/access_groups/get" + ) params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -187,6 +209,11 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: return AcsAccessGroup.from_dict(res["acs_access_group"]) + @route_metadata( + path="/acs/access_groups/list", + has_required_parameters=False, + has_pagination=False, + ) def list( self, *, @@ -221,6 +248,11 @@ def list( return [AcsAccessGroup.from_dict(item) for item in res["acs_access_groups"]] + @route_metadata( + path="/acs/access_groups/list_accessible_entrances", + has_required_parameters=True, + has_pagination=False, + ) def list_accessible_entrances( self, *, acs_access_group_id: str ) -> List[AcsEntrance]: @@ -232,7 +264,9 @@ def list_accessible_entrances( :raises ValueError: At least one parameter must be provided.""" if not any(acs_access_group_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/access_groups/list_accessible_entrances" + ) params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -244,6 +278,11 @@ def list_accessible_entrances( return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] + @route_metadata( + path="/acs/access_groups/list_users", + has_required_parameters=True, + has_pagination=False, + ) def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: """Returns a list of all `access system users `_ in an `access group `_. @@ -253,7 +292,9 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: :raises ValueError: At least one parameter must be provided.""" if not any(acs_access_group_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/access_groups/list_users" + ) params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -263,6 +304,11 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: return [AcsUser.from_dict(item) for item in res["acs_users"]] + @route_metadata( + path="/acs/access_groups/remove_user", + has_required_parameters=True, + has_pagination=False, + ) def remove_user( self, *, @@ -284,7 +330,9 @@ def remove_user( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/access_groups/remove_user" + ) params: Dict[str, Any] = {} if acs_access_group_id is not None: diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index e2aa19e0..b8faccc6 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import AcsCredential, AcsEntrance @@ -185,6 +186,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/acs/credentials/assign", + has_required_parameters=True, + has_pagination=False, + ) def assign( self, *, @@ -206,7 +212,9 @@ def assign( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/credentials/assign" + ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -220,6 +228,11 @@ def assign( return None + @route_metadata( + path="/acs/credentials/create", + has_required_parameters=True, + has_pagination=False, + ) def create( self, *, @@ -283,7 +296,9 @@ def create( user_identity_id is not None, visionline_metadata is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/credentials/create" + ) json_payload: Dict[str, Any] = {} if access_method is not None: @@ -321,6 +336,11 @@ def create( return AcsCredential.from_dict(res["acs_credential"]) + @route_metadata( + path="/acs/credentials/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, acs_credential_id: str) -> None: """Deletes a specified `credential `_. @@ -328,7 +348,9 @@ def delete(self, *, acs_credential_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(acs_credential_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/credentials/delete" + ) params: Dict[str, Any] = {} if acs_credential_id is not None: @@ -338,6 +360,9 @@ def delete(self, *, acs_credential_id: str) -> None: return None + @route_metadata( + path="/acs/credentials/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, acs_credential_id: str) -> AcsCredential: """Returns a specified `credential `_. @@ -347,7 +372,9 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: :raises ValueError: At least one parameter must be provided.""" if not any(acs_credential_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/credentials/get" + ) params: Dict[str, Any] = {} if acs_credential_id is not None: @@ -357,6 +384,9 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: return AcsCredential.from_dict(res["acs_credential"]) + @route_metadata( + path="/acs/credentials/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -411,6 +441,11 @@ def list( return [AcsCredential.from_dict(item) for item in res["acs_credentials"]] + @route_metadata( + path="/acs/credentials/list_accessible_entrances", + has_required_parameters=True, + has_pagination=False, + ) def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntrance]: """Returns a list of all `entrances `_ to which a `credential `_ grants access. @@ -420,7 +455,9 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran :raises ValueError: At least one parameter must be provided.""" if not any(acs_credential_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/credentials/list_accessible_entrances" + ) params: Dict[str, Any] = {} if acs_credential_id is not None: @@ -432,6 +469,11 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] + @route_metadata( + path="/acs/credentials/unassign", + has_required_parameters=True, + has_pagination=False, + ) def unassign( self, *, @@ -453,7 +495,9 @@ def unassign( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/credentials/unassign" + ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -467,6 +511,11 @@ def unassign( return None + @route_metadata( + path="/acs/credentials/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -486,7 +535,9 @@ def update( if not any( acs_credential_id is not None, code is not None, ends_at is not None ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/credentials/update" + ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index 52bb90b8..34e8c077 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ActionAttempt, AcsEncoder from .acs_encoders_simulate import AbstractAcsEncodersSimulate, AcsEncodersSimulate from ..modules.action_attempts import resolve_action_attempt @@ -132,6 +133,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def simulate(self) -> AcsEncodersSimulate: return self._simulate + @route_metadata( + path="/acs/encoders/encode_credential", + has_required_parameters=True, + has_pagination=False, + ) def encode_credential( self, *, @@ -158,7 +164,9 @@ def encode_credential( access_method_id is not None, acs_credential_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/encoders/encode_credential" + ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -182,6 +190,9 @@ def encode_credential( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/acs/encoders/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, acs_encoder_id: str) -> AcsEncoder: """Returns a specified `encoder `_. @@ -191,7 +202,7 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: :raises ValueError: At least one parameter must be provided.""" if not any(acs_encoder_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /acs/encoders/get") params: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -201,6 +212,9 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: return AcsEncoder.from_dict(res["acs_encoder"]) + @route_metadata( + path="/acs/encoders/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -240,6 +254,11 @@ def list( return [AcsEncoder.from_dict(item) for item in res["acs_encoders"]] + @route_metadata( + path="/acs/encoders/scan_credential", + has_required_parameters=True, + has_pagination=False, + ) def scan_credential( self, *, @@ -259,7 +278,9 @@ def scan_credential( :raises ValueError: At least one parameter must be provided.""" if not any(acs_encoder_id is not None, salto_ks_metadata is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/encoders/scan_credential" + ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -281,6 +302,11 @@ def scan_credential( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/acs/encoders/scan_to_assign_credential", + has_required_parameters=True, + has_pagination=False, + ) def scan_to_assign_credential( self, *, @@ -311,7 +337,9 @@ def scan_to_assign_credential( salto_ks_metadata is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/encoders/scan_to_assign_credential" + ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: diff --git a/seam/routes/acs_encoders_simulate.py b/seam/routes/acs_encoders_simulate.py index a9d48b43..724ebb54 100644 --- a/seam/routes/acs_encoders_simulate.py +++ b/seam/routes/acs_encoders_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata class AbstractAcsEncodersSimulate(abc.ABC): @@ -81,6 +82,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/acs/encoders/simulate/next_credential_encode_will_fail", + has_required_parameters=True, + has_pagination=False, + ) def next_credential_encode_will_fail( self, *, @@ -102,7 +108,9 @@ def next_credential_encode_will_fail( error_code is not None, acs_credential_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail" + ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -118,6 +126,11 @@ def next_credential_encode_will_fail( return None + @route_metadata( + path="/acs/encoders/simulate/next_credential_encode_will_succeed", + has_required_parameters=True, + has_pagination=False, + ) def next_credential_encode_will_succeed( self, *, acs_encoder_id: str, scenario: Optional[str] = None ) -> None: @@ -129,7 +142,9 @@ def next_credential_encode_will_succeed( :raises ValueError: At least one parameter must be provided.""" if not any(acs_encoder_id is not None, scenario is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed" + ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -144,6 +159,11 @@ def next_credential_encode_will_succeed( return None + @route_metadata( + path="/acs/encoders/simulate/next_credential_scan_will_fail", + has_required_parameters=True, + has_pagination=False, + ) def next_credential_scan_will_fail( self, *, @@ -165,7 +185,9 @@ def next_credential_scan_will_fail( error_code is not None, acs_credential_id_on_seam is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail" + ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -181,6 +203,11 @@ def next_credential_scan_will_fail( return None + @route_metadata( + path="/acs/encoders/simulate/next_credential_scan_will_succeed", + has_required_parameters=True, + has_pagination=False, + ) def next_credential_scan_will_succeed( self, *, @@ -202,7 +229,9 @@ def next_credential_scan_will_succeed( acs_credential_id_on_seam is not None, scenario is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed" + ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 4be3bc85..88c7db06 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import AcsEntrance, AcsCredential, ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -122,6 +123,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/acs/entrances/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, acs_entrance_id: str) -> AcsEntrance: """Returns a specified `access system entrance `_. @@ -131,7 +135,9 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: :raises ValueError: At least one parameter must be provided.""" if not any(acs_entrance_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/entrances/get" + ) params: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -141,6 +147,11 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: return AcsEntrance.from_dict(res["acs_entrance"]) + @route_metadata( + path="/acs/entrances/grant_access", + has_required_parameters=True, + has_pagination=False, + ) def grant_access( self, *, @@ -162,7 +173,9 @@ def grant_access( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/entrances/grant_access" + ) json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -176,6 +189,9 @@ def grant_access( return None + @route_metadata( + path="/acs/entrances/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -245,6 +261,11 @@ def list( return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] + @route_metadata( + path="/acs/entrances/list_credentials_with_access", + has_required_parameters=True, + has_pagination=False, + ) def list_credentials_with_access( self, *, acs_entrance_id: str, include_if: Optional[List[str]] = None ) -> List[AcsCredential]: @@ -258,7 +279,9 @@ def list_credentials_with_access( :raises ValueError: At least one parameter must be provided.""" if not any(acs_entrance_id is not None, include_if is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/entrances/list_credentials_with_access" + ) json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -272,6 +295,9 @@ def list_credentials_with_access( return [AcsCredential.from_dict(item) for item in res["acs_credentials"]] + @route_metadata( + path="/acs/entrances/unlock", has_required_parameters=True, has_pagination=False + ) def unlock( self, *, @@ -291,7 +317,9 @@ def unlock( :raises ValueError: At least one parameter must be provided.""" if not any(acs_credential_id is not None, acs_entrance_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/entrances/unlock" + ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index 4b0c8f1f..04eda58a 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import AcsSystem @@ -78,6 +79,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/acs/systems/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, acs_system_id: str) -> AcsSystem: """Returns a specified `access system `_. @@ -87,7 +91,7 @@ def get(self, *, acs_system_id: str) -> AcsSystem: :raises ValueError: At least one parameter must be provided.""" if not any(acs_system_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /acs/systems/get") params: Dict[str, Any] = {} if acs_system_id is not None: @@ -97,6 +101,9 @@ def get(self, *, acs_system_id: str) -> AcsSystem: return AcsSystem.from_dict(res["acs_system"]) + @route_metadata( + path="/acs/systems/list", has_required_parameters=False, has_pagination=False + ) def list( self, *, @@ -128,6 +135,11 @@ def list( return [AcsSystem.from_dict(item) for item in res["acs_systems"]] + @route_metadata( + path="/acs/systems/list_compatible_credential_manager_acs_systems", + has_required_parameters=True, + has_pagination=False, + ) def list_compatible_credential_manager_acs_systems( self, *, acs_system_id: str ) -> List[AcsSystem]: @@ -141,7 +153,9 @@ def list_compatible_credential_manager_acs_systems( :raises ValueError: At least one parameter must be provided.""" if not any(acs_system_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems" + ) params: Dict[str, Any] = {} if acs_system_id is not None: @@ -153,6 +167,11 @@ def list_compatible_credential_manager_acs_systems( return [AcsSystem.from_dict(item) for item in res["acs_systems"]] + @route_metadata( + path="/acs/systems/report_devices", + has_required_parameters=True, + has_pagination=False, + ) def report_devices( self, *, @@ -174,7 +193,9 @@ def report_devices( acs_encoders is not None, acs_entrances is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/systems/report_devices" + ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 59699c10..9d9473a6 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import AcsUser, AcsEntrance @@ -269,6 +270,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/acs/users/add_to_access_group", + has_required_parameters=True, + has_pagination=False, + ) def add_to_access_group( self, *, acs_access_group_id: str, acs_user_id: str ) -> None: @@ -280,7 +286,9 @@ def add_to_access_group( :raises ValueError: At least one parameter must be provided.""" if not any(acs_access_group_id is not None, acs_user_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/users/add_to_access_group" + ) json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -292,6 +300,9 @@ def add_to_access_group( return None + @route_metadata( + path="/acs/users/create", has_required_parameters=True, has_pagination=False + ) def create( self, *, @@ -335,7 +346,7 @@ def create( phone_number is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /acs/users/create") json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -359,6 +370,9 @@ def create( return AcsUser.from_dict(res["acs_user"]) + @route_metadata( + path="/acs/users/delete", has_required_parameters=True, has_pagination=False + ) def delete( self, *, @@ -380,7 +394,7 @@ def delete( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /acs/users/delete") params: Dict[str, Any] = {} if acs_system_id is not None: @@ -394,6 +408,9 @@ def delete( return None + @route_metadata( + path="/acs/users/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, @@ -417,7 +434,7 @@ def get( acs_system_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /acs/users/get") params: Dict[str, Any] = {} if acs_user_id is not None: @@ -431,6 +448,9 @@ def get( return AcsUser.from_dict(res["acs_user"]) + @route_metadata( + path="/acs/users/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -485,6 +505,11 @@ def list( return [AcsUser.from_dict(item) for item in res["acs_users"]] + @route_metadata( + path="/acs/users/list_accessible_entrances", + has_required_parameters=True, + has_pagination=False, + ) def list_accessible_entrances( self, *, @@ -508,7 +533,9 @@ def list_accessible_entrances( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/users/list_accessible_entrances" + ) params: Dict[str, Any] = {} if acs_system_id is not None: @@ -522,6 +549,11 @@ def list_accessible_entrances( return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] + @route_metadata( + path="/acs/users/remove_from_access_group", + has_required_parameters=True, + has_pagination=False, + ) def remove_from_access_group( self, *, @@ -543,7 +575,9 @@ def remove_from_access_group( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/users/remove_from_access_group" + ) params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -557,6 +591,11 @@ def remove_from_access_group( return None + @route_metadata( + path="/acs/users/revoke_access_to_all_entrances", + has_required_parameters=True, + has_pagination=False, + ) def revoke_access_to_all_entrances( self, *, @@ -578,7 +617,9 @@ def revoke_access_to_all_entrances( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/users/revoke_access_to_all_entrances" + ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -592,6 +633,9 @@ def revoke_access_to_all_entrances( return None + @route_metadata( + path="/acs/users/suspend", has_required_parameters=True, has_pagination=False + ) def suspend( self, *, @@ -613,7 +657,9 @@ def suspend( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/users/suspend" + ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -627,6 +673,9 @@ def suspend( return None + @route_metadata( + path="/acs/users/unsuspend", has_required_parameters=True, has_pagination=False + ) def unsuspend( self, *, @@ -648,7 +697,9 @@ def unsuspend( acs_user_id is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /acs/users/unsuspend" + ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -662,6 +713,9 @@ def unsuspend( return None + @route_metadata( + path="/acs/users/update", has_required_parameters=True, has_pagination=False + ) def update( self, *, @@ -707,7 +761,7 @@ def update( phone_number is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /acs/users/update") json_payload: Dict[str, Any] = {} if access_schedule is not None: diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index e293a2b5..4d7bde7d 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -53,6 +54,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/action_attempts/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, @@ -69,7 +73,9 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(action_attempt_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /action_attempts/get" + ) params: Dict[str, Any] = {} if action_attempt_id is not None: @@ -89,6 +95,9 @@ def get( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/action_attempts/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index 89d9d2af..d5037ebf 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ClientSession @@ -163,6 +164,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/client_sessions/create", + has_required_parameters=False, + has_pagination=False, + ) def create( self, *, @@ -217,6 +223,11 @@ def create( return ClientSession.from_dict(res["client_session"]) + @route_metadata( + path="/client_sessions/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, client_session_id: str) -> None: """Deletes a `client session `_. @@ -224,7 +235,9 @@ def delete(self, *, client_session_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(client_session_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /client_sessions/delete" + ) params: Dict[str, Any] = {} if client_session_id is not None: @@ -234,6 +247,9 @@ def delete(self, *, client_session_id: str) -> None: return None + @route_metadata( + path="/client_sessions/get", has_required_parameters=False, has_pagination=False + ) def get( self, *, @@ -258,6 +274,11 @@ def get( return ClientSession.from_dict(res["client_session"]) + @route_metadata( + path="/client_sessions/get_or_create", + has_required_parameters=False, + has_pagination=False, + ) def get_or_create( self, *, @@ -302,6 +323,11 @@ def get_or_create( return ClientSession.from_dict(res["client_session"]) + @route_metadata( + path="/client_sessions/grant_access", + has_required_parameters=True, + has_pagination=False, + ) def grant_access( self, *, @@ -335,7 +361,9 @@ def grant_access( user_identity_id is not None, user_identity_ids is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /client_sessions/grant_access" + ) json_payload: Dict[str, Any] = {} if client_session_id is not None: @@ -355,6 +383,11 @@ def grant_access( return None + @route_metadata( + path="/client_sessions/list", + has_required_parameters=False, + has_pagination=False, + ) def list( self, *, @@ -394,6 +427,11 @@ def list( return [ClientSession.from_dict(item) for item in res["client_sessions"]] + @route_metadata( + path="/client_sessions/revoke", + has_required_parameters=True, + has_pagination=False, + ) def revoke(self, *, client_session_id: str) -> None: """Revokes a `client session `_. @@ -403,7 +441,9 @@ def revoke(self, *, client_session_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(client_session_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /client_sessions/revoke" + ) json_payload: Dict[str, Any] = {} if client_session_id is not None: diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index b2f6f383..e1d975dd 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ConnectWebview @@ -110,6 +111,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/connect_webviews/create", + has_required_parameters=False, + has_pagination=False, + ) def create( self, *, @@ -182,6 +188,11 @@ def create( return ConnectWebview.from_dict(res["connect_webview"]) + @route_metadata( + path="/connect_webviews/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, connect_webview_id: str) -> None: """Deletes a `Connect Webview `_. @@ -191,7 +202,9 @@ def delete(self, *, connect_webview_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(connect_webview_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /connect_webviews/delete" + ) params: Dict[str, Any] = {} if connect_webview_id is not None: @@ -201,6 +214,9 @@ def delete(self, *, connect_webview_id: str) -> None: return None + @route_metadata( + path="/connect_webviews/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, connect_webview_id: str) -> ConnectWebview: """Returns a specified `Connect Webview `_. @@ -212,7 +228,9 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: :raises ValueError: At least one parameter must be provided.""" if not any(connect_webview_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /connect_webviews/get" + ) params: Dict[str, Any] = {} if connect_webview_id is not None: @@ -222,6 +240,11 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: return ConnectWebview.from_dict(res["connect_webview"]) + @route_metadata( + path="/connect_webviews/list", + has_required_parameters=False, + has_pagination=True, + ) def list( self, *, diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index aba881ff..e1f15532 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ConnectedAccount from .connected_accounts_simulate import ( AbstractConnectedAccountsSimulate, @@ -122,6 +123,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def simulate(self) -> ConnectedAccountsSimulate: return self._simulate + @route_metadata( + path="/connected_accounts/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, connected_account_id: str) -> None: """Deletes a specified `connected account `_. @@ -133,7 +139,9 @@ def delete(self, *, connected_account_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(connected_account_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /connected_accounts/delete" + ) params: Dict[str, Any] = {} if connected_account_id is not None: @@ -143,6 +151,11 @@ def delete(self, *, connected_account_id: str) -> None: return None + @route_metadata( + path="/connected_accounts/get", + has_required_parameters=True, + has_pagination=False, + ) def get( self, *, connected_account_id: Optional[str] = None, email: Optional[str] = None ) -> ConnectedAccount: @@ -156,7 +169,9 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(connected_account_id is not None, email is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /connected_accounts/get" + ) params: Dict[str, Any] = {} if connected_account_id is not None: @@ -168,6 +183,11 @@ def get( return ConnectedAccount.from_dict(res["connected_account"]) + @route_metadata( + path="/connected_accounts/list", + has_required_parameters=False, + has_pagination=True, + ) def list( self, *, @@ -217,6 +237,11 @@ def list( return [ConnectedAccount.from_dict(item) for item in res["connected_accounts"]] + @route_metadata( + path="/connected_accounts/sync", + has_required_parameters=True, + has_pagination=False, + ) def sync(self, *, connected_account_id: str) -> None: """Request a `connected account `_ sync attempt for the specified ``connected_account_id``. @@ -224,7 +249,9 @@ def sync(self, *, connected_account_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(connected_account_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /connected_accounts/sync" + ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: @@ -234,6 +261,11 @@ def sync(self, *, connected_account_id: str) -> None: return None + @route_metadata( + path="/connected_accounts/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -267,7 +299,9 @@ def update( customer_key is not None, display_name is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /connected_accounts/update" + ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: diff --git a/seam/routes/connected_accounts_simulate.py b/seam/routes/connected_accounts_simulate.py index af66950b..47119ee9 100644 --- a/seam/routes/connected_accounts_simulate.py +++ b/seam/routes/connected_accounts_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata class AbstractConnectedAccountsSimulate(abc.ABC): @@ -20,6 +21,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/connected_accounts/simulate/disconnect", + has_required_parameters=True, + has_pagination=False, + ) def disconnect(self, *, connected_account_id: str) -> None: """Simulates a connected account becoming disconnected from Seam. Only applicable for `sandbox workspaces `_. @@ -27,7 +33,9 @@ def disconnect(self, *, connected_account_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(connected_account_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /connected_accounts/simulate/disconnect" + ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: diff --git a/seam/routes/customers.py b/seam/routes/customers.py index 9ed18ec9..01b857ca 100644 --- a/seam/routes/customers.py +++ b/seam/routes/customers.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import CustomerPortal @@ -191,6 +192,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/customers/create_portal", + has_required_parameters=False, + has_pagination=False, + ) def create_portal( self, *, @@ -260,6 +266,11 @@ def create_portal( return CustomerPortal.from_dict(res["customer_portal"]) + @route_metadata( + path="/customers/delete_data", + has_required_parameters=False, + has_pagination=False, + ) def delete_data( self, *, @@ -368,6 +379,9 @@ def delete_data( return None + @route_metadata( + path="/customers/push_data", has_required_parameters=True, has_pagination=False + ) def push_data( self, *, @@ -457,7 +471,9 @@ def push_data( user_identities is not None, users is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /customers/push_data" + ) json_payload: Dict[str, Any] = {} if customer_key is not None: diff --git a/seam/routes/devices.py b/seam/routes/devices.py index 441e9e4c..9895a304 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import Device, DeviceProvider from .devices_simulate import AbstractDevicesSimulate, DevicesSimulate from .devices_unmanaged import AbstractDevicesUnmanaged, DevicesUnmanaged @@ -163,6 +164,9 @@ def simulate(self) -> DevicesSimulate: def unmanaged(self) -> DevicesUnmanaged: return self._unmanaged + @route_metadata( + path="/devices/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, device_id: Optional[str] = None, name: Optional[str] = None ) -> Device: @@ -178,7 +182,7 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, name is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /devices/get") params: Dict[str, Any] = {} if device_id is not None: @@ -190,6 +194,9 @@ def get( return Device.from_dict(res["device"]) + @route_metadata( + path="/devices/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -284,6 +291,11 @@ def list( return [Device.from_dict(item) for item in res["devices"]] + @route_metadata( + path="/devices/list_device_providers", + has_required_parameters=False, + has_pagination=False, + ) def list_device_providers( self, *, provider_category: Optional[str] = None ) -> List[DeviceProvider]: @@ -305,6 +317,11 @@ def list_device_providers( return [DeviceProvider.from_dict(item) for item in res["device_providers"]] + @route_metadata( + path="/devices/report_provider_metadata", + has_required_parameters=True, + has_pagination=False, + ) def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: """Updates provider-specific metadata for devices. @@ -312,7 +329,9 @@ def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(devices is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/report_provider_metadata" + ) json_payload: Dict[str, Any] = {} if devices is not None: @@ -322,6 +341,9 @@ def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: return None + @route_metadata( + path="/devices/update", has_required_parameters=True, has_pagination=False + ) def update( self, *, @@ -357,7 +379,7 @@ def update( name is not None, properties is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /devices/update") json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/devices_simulate.py b/seam/routes/devices_simulate.py index 12b294d7..574fea0a 100644 --- a/seam/routes/devices_simulate.py +++ b/seam/routes/devices_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata class AbstractDevicesSimulate(abc.ABC): @@ -76,6 +77,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/devices/simulate/connect", + has_required_parameters=True, + has_pagination=False, + ) def connect(self, *, device_id: str) -> None: """Simulates connecting a device to Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. @@ -83,7 +89,9 @@ def connect(self, *, device_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/simulate/connect" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -93,6 +101,11 @@ def connect(self, *, device_id: str) -> None: return None + @route_metadata( + path="/devices/simulate/connect_to_hub", + has_required_parameters=True, + has_pagination=False, + ) def connect_to_hub(self, *, device_id: str) -> None: """Simulates bringing the Wi‑Fi hub (bridge) back online for a device. Only applicable for sandbox workspaces and currently @@ -103,7 +116,9 @@ def connect_to_hub(self, *, device_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/simulate/connect_to_hub" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -113,6 +128,11 @@ def connect_to_hub(self, *, device_id: str) -> None: return None + @route_metadata( + path="/devices/simulate/disconnect", + has_required_parameters=True, + has_pagination=False, + ) def disconnect(self, *, device_id: str) -> None: """Simulates disconnecting a device from Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. @@ -120,7 +140,9 @@ def disconnect(self, *, device_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/simulate/disconnect" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -130,6 +152,11 @@ def disconnect(self, *, device_id: str) -> None: return None + @route_metadata( + path="/devices/simulate/disconnect_from_hub", + has_required_parameters=True, + has_pagination=False, + ) def disconnect_from_hub(self, *, device_id: str) -> None: """Simulates taking the Wi‑Fi hub (bridge) offline for a device. Only applicable for sandbox workspaces and currently @@ -141,7 +168,9 @@ def disconnect_from_hub(self, *, device_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/simulate/disconnect_from_hub" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -151,6 +180,11 @@ def disconnect_from_hub(self, *, device_id: str) -> None: return None + @route_metadata( + path="/devices/simulate/paid_subscription", + has_required_parameters=True, + has_pagination=False, + ) def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: """Toggle the simulated Nuki Smart Hosting subscription for a device (sandbox only). Send ``is_expired: true`` to simulate an expired subscription, or ``false`` to simulate an active subscription. @@ -162,7 +196,9 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, is_expired is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/simulate/paid_subscription" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -174,6 +210,11 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: return None + @route_metadata( + path="/devices/simulate/remove", + has_required_parameters=True, + has_pagination=False, + ) def remove(self, *, device_id: str) -> None: """Simulates removing a device from Seam. Only applicable for `sandbox devices `_. See also `Testing Your App Against Device Disconnection and Removal `_. @@ -181,7 +222,9 @@ def remove(self, *, device_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/simulate/remove" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 03f2230e..4014be6c 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import UnmanagedDevice @@ -100,6 +101,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/devices/unmanaged/get", + has_required_parameters=True, + has_pagination=False, + ) def get( self, *, device_id: Optional[str] = None, name: Optional[str] = None ) -> UnmanagedDevice: @@ -117,7 +123,9 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, name is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/unmanaged/get" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -129,6 +137,11 @@ def get( return UnmanagedDevice.from_dict(res["device"]) + @route_metadata( + path="/devices/unmanaged/list", + has_required_parameters=False, + has_pagination=True, + ) def list( self, *, @@ -205,6 +218,11 @@ def list( return [UnmanagedDevice.from_dict(item) for item in res["devices"]] + @route_metadata( + path="/devices/unmanaged/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -226,7 +244,9 @@ def update( if not any( device_id is not None, custom_metadata is not None, is_managed is not None ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /devices/unmanaged/update" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/events.py b/seam/routes/events.py index 54138841..a93c063f 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import SeamEvent @@ -129,6 +130,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/events/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, @@ -148,7 +152,7 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(event_id is not None, device_id is not None, event_type is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /events/get") params: Dict[str, Any] = {} if event_id is not None: @@ -162,6 +166,9 @@ def get( return SeamEvent.from_dict(res["event"]) + @route_metadata( + path="/events/list", has_required_parameters=True, has_pagination=False + ) def list( self, *, @@ -285,7 +292,7 @@ def list( unstable_offset is not None, user_identity_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /events/list") json_payload: Dict[str, Any] = {} if access_code_id is not None: diff --git a/seam/routes/instant_keys.py b/seam/routes/instant_keys.py index af0020ca..cbe60e0c 100644 --- a/seam/routes/instant_keys.py +++ b/seam/routes/instant_keys.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import InstantKey @@ -48,6 +49,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/instant_keys/delete", has_required_parameters=True, has_pagination=False + ) def delete(self, *, instant_key_id: str) -> None: """Deletes a specified `Instant Key `_. @@ -55,7 +59,9 @@ def delete(self, *, instant_key_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(instant_key_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /instant_keys/delete" + ) params: Dict[str, Any] = {} if instant_key_id is not None: @@ -65,6 +71,9 @@ def delete(self, *, instant_key_id: str) -> None: return None + @route_metadata( + path="/instant_keys/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, @@ -81,7 +90,7 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(instant_key_id is not None, instant_key_url is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /instant_keys/get") params: Dict[str, Any] = {} if instant_key_id is not None: @@ -93,6 +102,9 @@ def get( return InstantKey.from_dict(res["instant_key"]) + @route_metadata( + path="/instant_keys/list", has_required_parameters=False, has_pagination=False + ) def list(self, *, user_identity_id: Optional[str] = None) -> List[InstantKey]: """Returns a list of all `instant keys `_. diff --git a/seam/routes/locks.py b/seam/routes/locks.py index 5cd5ba08..3996ec60 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ActionAttempt, Device from .locks_simulate import AbstractLocksSimulate, LocksSimulate from ..modules.action_attempts import resolve_action_attempt @@ -130,6 +131,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def simulate(self) -> LocksSimulate: return self._simulate + @route_metadata( + path="/locks/configure_auto_lock", + has_required_parameters=True, + has_pagination=False, + ) def configure_auto_lock( self, *, @@ -156,7 +162,9 @@ def configure_auto_lock( device_id is not None, auto_lock_delay_seconds is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /locks/configure_auto_lock" + ) json_payload: Dict[str, Any] = {} if auto_lock_enabled is not None: @@ -180,6 +188,9 @@ def configure_auto_lock( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/locks/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, device_id: Optional[str] = None, name: Optional[str] = None ) -> Device: @@ -196,7 +207,7 @@ def get( .. deprecated:: Use ``/devices/get`` instead.""" if not any(device_id is not None, name is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /locks/get") params: Dict[str, Any] = {} if device_id is not None: @@ -208,6 +219,9 @@ def get( return Device.from_dict(res["device"]) + @route_metadata( + path="/locks/list", has_required_parameters=False, has_pagination=False + ) def list( self, *, @@ -252,6 +266,9 @@ def list( return [Device.from_dict(item) for item in res["devices"]] + @route_metadata( + path="/locks/lock_door", has_required_parameters=True, has_pagination=False + ) def lock_door( self, *, @@ -268,7 +285,7 @@ def lock_door( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /locks/lock_door") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -288,6 +305,9 @@ def lock_door( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/locks/unlock_door", has_required_parameters=True, has_pagination=False + ) def unlock_door( self, *, @@ -304,7 +324,9 @@ def unlock_door( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /locks/unlock_door" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/locks_simulate.py b/seam/routes/locks_simulate.py index 562ca18b..038b7b2a 100644 --- a/seam/routes/locks_simulate.py +++ b/seam/routes/locks_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -52,6 +53,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/locks/simulate/keypad_code_entry", + has_required_parameters=True, + has_pagination=False, + ) def keypad_code_entry( self, *, @@ -71,7 +77,9 @@ def keypad_code_entry( :raises ValueError: At least one parameter must be provided.""" if not any(code is not None, device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /locks/simulate/keypad_code_entry" + ) json_payload: Dict[str, Any] = {} if code is not None: @@ -93,6 +101,11 @@ def keypad_code_entry( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/locks/simulate/manual_lock_via_keypad", + has_required_parameters=True, + has_pagination=False, + ) def manual_lock_via_keypad( self, *, @@ -109,7 +122,9 @@ def manual_lock_via_keypad( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /locks/simulate/manual_lock_via_keypad" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/noise_sensors.py b/seam/routes/noise_sensors.py index ab99a7cb..6ab4492b 100644 --- a/seam/routes/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import Device from .noise_sensors_noise_thresholds import ( AbstractNoiseSensorsNoiseThresholds, @@ -67,6 +68,9 @@ def noise_thresholds(self) -> NoiseSensorsNoiseThresholds: def simulate(self) -> NoiseSensorsSimulate: return self._simulate + @route_metadata( + path="/noise_sensors/list", has_required_parameters=False, has_pagination=False + ) def list( self, *, diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index 128f53ff..1bef9c4a 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import NoiseThreshold @@ -106,6 +107,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/noise_sensors/noise_thresholds/create", + has_required_parameters=True, + has_pagination=False, + ) def create( self, *, @@ -141,7 +147,9 @@ def create( noise_threshold_decibels is not None, noise_threshold_nrs is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/create" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -163,6 +171,11 @@ def create( return NoiseThreshold.from_dict(res["noise_threshold"]) + @route_metadata( + path="/noise_sensors/noise_thresholds/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, device_id: str, noise_threshold_id: str) -> None: """Deletes a `noise threshold `_ from a `noise sensor `_. @@ -172,7 +185,9 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, noise_threshold_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/delete" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -184,6 +199,11 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: return None + @route_metadata( + path="/noise_sensors/noise_thresholds/get", + has_required_parameters=True, + has_pagination=False, + ) def get(self, *, noise_threshold_id: str) -> NoiseThreshold: """Returns a specified `noise threshold `_ for a `noise sensor `_. @@ -193,7 +213,9 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: :raises ValueError: At least one parameter must be provided.""" if not any(noise_threshold_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/get" + ) params: Dict[str, Any] = {} if noise_threshold_id is not None: @@ -203,6 +225,11 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: return NoiseThreshold.from_dict(res["noise_threshold"]) + @route_metadata( + path="/noise_sensors/noise_thresholds/list", + has_required_parameters=True, + has_pagination=False, + ) def list(self, *, device_id: str) -> List[NoiseThreshold]: """Returns a list of all `noise thresholds `_ for a `noise sensor `_. @@ -212,7 +239,9 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/list" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -222,6 +251,11 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: return [NoiseThreshold.from_dict(item) for item in res["noise_thresholds"]] + @route_metadata( + path="/noise_sensors/noise_thresholds/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -259,7 +293,9 @@ def update( noise_threshold_nrs is not None, starts_daily_at is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/update" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/noise_sensors_simulate.py b/seam/routes/noise_sensors_simulate.py index 44a10c68..a4039ccf 100644 --- a/seam/routes/noise_sensors_simulate.py +++ b/seam/routes/noise_sensors_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata class AbstractNoiseSensorsSimulate(abc.ABC): @@ -20,6 +21,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/noise_sensors/simulate/trigger_noise_threshold", + has_required_parameters=True, + has_pagination=False, + ) def trigger_noise_threshold(self, *, device_id: str) -> None: """Simulates the triggering of a `noise threshold `_ for a `noise sensor `_ in a `sandbox workspace `_. @@ -27,7 +33,9 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/phones.py b/seam/routes/phones.py index 837ac363..3a72c84a 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import Phone from .phones_simulate import AbstractPhonesSimulate, PhonesSimulate @@ -59,6 +60,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def simulate(self) -> PhonesSimulate: return self._simulate + @route_metadata( + path="/phones/deactivate", has_required_parameters=True, has_pagination=False + ) def deactivate(self, *, device_id: str) -> None: """Deactivates a phone, which is useful, for example, if a user has lost their phone. For more information, see `App User Lost Phone Process `_. @@ -66,7 +70,9 @@ def deactivate(self, *, device_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /phones/deactivate" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -76,6 +82,9 @@ def deactivate(self, *, device_id: str) -> None: return None + @route_metadata( + path="/phones/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, device_id: str) -> Phone: """Returns a specified `phone `_. @@ -85,7 +94,7 @@ def get(self, *, device_id: str) -> Phone: :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /phones/get") params: Dict[str, Any] = {} if device_id is not None: @@ -95,6 +104,9 @@ def get(self, *, device_id: str) -> Phone: return Phone.from_dict(res["phone"]) + @route_metadata( + path="/phones/list", has_required_parameters=False, has_pagination=False + ) def list( self, *, diff --git a/seam/routes/phones_simulate.py b/seam/routes/phones_simulate.py index 7d011e63..1fed90d5 100644 --- a/seam/routes/phones_simulate.py +++ b/seam/routes/phones_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import Phone @@ -36,6 +37,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/phones/simulate/create_sandbox_phone", + has_required_parameters=True, + has_pagination=False, + ) def create_sandbox_phone( self, *, @@ -63,7 +69,9 @@ def create_sandbox_phone( custom_sdk_installation_id is not None, phone_metadata is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /phones/simulate/create_sandbox_phone" + ) json_payload: Dict[str, Any] = {} if user_identity_id is not None: diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index c4e87467..b63fc27b 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import Space, Batch @@ -218,6 +219,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/spaces/add_acs_entrances", + has_required_parameters=True, + has_pagination=False, + ) def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> None: """Adds `entrances `_ to a specific space. @@ -227,7 +233,9 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No :raises ValueError: At least one parameter must be provided.""" if not any(acs_entrance_ids is not None, space_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /spaces/add_acs_entrances" + ) json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: @@ -239,6 +247,11 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No return None + @route_metadata( + path="/spaces/add_connected_account", + has_required_parameters=True, + has_pagination=False, + ) def add_connected_account( self, *, connected_account_id: str, space_id: str ) -> None: @@ -250,7 +263,9 @@ def add_connected_account( :raises ValueError: At least one parameter must be provided.""" if not any(connected_account_id is not None, space_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /spaces/add_connected_account" + ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: @@ -262,6 +277,9 @@ def add_connected_account( return None + @route_metadata( + path="/spaces/add_devices", has_required_parameters=True, has_pagination=False + ) def add_devices(self, *, device_ids: List[str], space_id: str) -> None: """Adds devices to a specific space. @@ -271,7 +289,9 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_ids is not None, space_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /spaces/add_devices" + ) json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -283,6 +303,9 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: return None + @route_metadata( + path="/spaces/create", has_required_parameters=True, has_pagination=False + ) def create( self, *, @@ -322,7 +345,7 @@ def create( device_ids is not None, space_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /spaces/create") json_payload: Dict[str, Any] = {} if name is not None: @@ -344,6 +367,9 @@ def create( return Space.from_dict(res["space"]) + @route_metadata( + path="/spaces/delete", has_required_parameters=True, has_pagination=False + ) def delete(self, *, space_id: str) -> None: """Deletes a space. @@ -351,7 +377,7 @@ def delete(self, *, space_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(space_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /spaces/delete") params: Dict[str, Any] = {} if space_id is not None: @@ -361,6 +387,9 @@ def delete(self, *, space_id: str) -> None: return None + @route_metadata( + path="/spaces/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, space_id: Optional[str] = None, space_key: Optional[str] = None ) -> Space: @@ -374,7 +403,7 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(space_id is not None, space_key is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /spaces/get") params: Dict[str, Any] = {} if space_id is not None: @@ -386,6 +415,9 @@ def get( return Space.from_dict(res["space"]) + @route_metadata( + path="/spaces/get_related", has_required_parameters=True, has_pagination=False + ) def get_related( self, *, @@ -413,7 +445,9 @@ def get_related( space_ids is not None, space_keys is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /spaces/get_related" + ) json_payload: Dict[str, Any] = {} if exclude is not None: @@ -429,6 +463,9 @@ def get_related( return Batch.from_dict(res["batch"]) + @route_metadata( + path="/spaces/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -468,6 +505,11 @@ def list( return [Space.from_dict(item) for item in res["spaces"]] + @route_metadata( + path="/spaces/remove_acs_entrances", + has_required_parameters=True, + has_pagination=False, + ) def remove_acs_entrances( self, *, acs_entrance_ids: List[str], space_id: str ) -> None: @@ -479,7 +521,9 @@ def remove_acs_entrances( :raises ValueError: At least one parameter must be provided.""" if not any(acs_entrance_ids is not None, space_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /spaces/remove_acs_entrances" + ) json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: @@ -491,6 +535,11 @@ def remove_acs_entrances( return None + @route_metadata( + path="/spaces/remove_connected_account", + has_required_parameters=True, + has_pagination=False, + ) def remove_connected_account( self, *, connected_account_id: str, space_id: str ) -> None: @@ -502,7 +551,9 @@ def remove_connected_account( :raises ValueError: At least one parameter must be provided.""" if not any(connected_account_id is not None, space_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /spaces/remove_connected_account" + ) params: Dict[str, Any] = {} if connected_account_id is not None: @@ -514,6 +565,11 @@ def remove_connected_account( return None + @route_metadata( + path="/spaces/remove_devices", + has_required_parameters=True, + has_pagination=False, + ) def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: """Removes devices from a specific space. @@ -523,7 +579,9 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(device_ids is not None, space_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /spaces/remove_devices" + ) json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -535,6 +593,9 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: return None + @route_metadata( + path="/spaces/update", has_required_parameters=False, has_pagination=False + ) def update( self, *, diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index 02e0f405..953fb587 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ActionAttempt, Device from .thermostats_daily_programs import ( AbstractThermostatsDailyPrograms, @@ -433,6 +434,11 @@ def schedules(self) -> ThermostatsSchedules: def simulate(self) -> ThermostatsSimulate: return self._simulate + @route_metadata( + path="/thermostats/activate_climate_preset", + has_required_parameters=True, + has_pagination=False, + ) def activate_climate_preset( self, *, @@ -452,7 +458,9 @@ def activate_climate_preset( :raises ValueError: At least one parameter must be provided.""" if not any(climate_preset_key is not None, device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/activate_climate_preset" + ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -476,6 +484,9 @@ def activate_climate_preset( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/thermostats/cool", has_required_parameters=True, has_pagination=False + ) def cool( self, *, @@ -502,7 +513,7 @@ def cool( cooling_set_point_celsius is not None, cooling_set_point_fahrenheit is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /thermostats/cool") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -526,6 +537,11 @@ def cool( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/thermostats/create_climate_preset", + has_required_parameters=True, + has_pagination=False, + ) def create_climate_preset( self, *, @@ -583,7 +599,9 @@ def create_climate_preset( manual_override_allowed is not None, name is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/create_climate_preset" + ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -615,6 +633,11 @@ def create_climate_preset( return None + @route_metadata( + path="/thermostats/delete_climate_preset", + has_required_parameters=True, + has_pagination=False, + ) def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> None: """Deletes a specified `climate preset `_ for a specified `thermostat `_. @@ -624,7 +647,9 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N :raises ValueError: At least one parameter must be provided.""" if not any(climate_preset_key is not None, device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/delete_climate_preset" + ) params: Dict[str, Any] = {} if climate_preset_key is not None: @@ -636,6 +661,9 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N return None + @route_metadata( + path="/thermostats/heat", has_required_parameters=True, has_pagination=False + ) def heat( self, *, @@ -662,7 +690,7 @@ def heat( heating_set_point_celsius is not None, heating_set_point_fahrenheit is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /thermostats/heat") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -686,6 +714,11 @@ def heat( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/thermostats/heat_cool", + has_required_parameters=True, + has_pagination=False, + ) def heat_cool( self, *, @@ -720,7 +753,9 @@ def heat_cool( heating_set_point_celsius is not None, heating_set_point_fahrenheit is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/heat_cool" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -748,6 +783,9 @@ def heat_cool( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/thermostats/list", has_required_parameters=False, has_pagination=False + ) def list( self, *, @@ -792,6 +830,9 @@ def list( return [Device.from_dict(item) for item in res["devices"]] + @route_metadata( + path="/thermostats/off", has_required_parameters=True, has_pagination=False + ) def off( self, *, @@ -808,7 +849,7 @@ def off( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /thermostats/off") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -828,6 +869,11 @@ def off( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/thermostats/set_fallback_climate_preset", + has_required_parameters=True, + has_pagination=False, + ) def set_fallback_climate_preset( self, *, climate_preset_key: str, device_id: str ) -> None: @@ -839,7 +885,9 @@ def set_fallback_climate_preset( :raises ValueError: At least one parameter must be provided.""" if not any(climate_preset_key is not None, device_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/set_fallback_climate_preset" + ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -851,6 +899,11 @@ def set_fallback_climate_preset( return None + @route_metadata( + path="/thermostats/set_fan_mode", + has_required_parameters=True, + has_pagination=False, + ) def set_fan_mode( self, *, @@ -875,7 +928,9 @@ def set_fan_mode( if not any( device_id is not None, fan_mode is not None, fan_mode_setting is not None ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/set_fan_mode" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -899,6 +954,11 @@ def set_fan_mode( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/thermostats/set_hvac_mode", + has_required_parameters=True, + has_pagination=False, + ) def set_hvac_mode( self, *, @@ -937,7 +997,9 @@ def set_hvac_mode( heating_set_point_celsius is not None, heating_set_point_fahrenheit is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/set_hvac_mode" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -967,6 +1029,11 @@ def set_hvac_mode( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/thermostats/set_temperature_threshold", + has_required_parameters=True, + has_pagination=False, + ) def set_temperature_threshold( self, *, @@ -996,7 +1063,9 @@ def set_temperature_threshold( upper_limit_celsius is not None, upper_limit_fahrenheit is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/set_temperature_threshold" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -1014,6 +1083,11 @@ def set_temperature_threshold( return None + @route_metadata( + path="/thermostats/update_climate_preset", + has_required_parameters=True, + has_pagination=False, + ) def update_climate_preset( self, *, @@ -1071,7 +1145,9 @@ def update_climate_preset( manual_override_allowed is not None, name is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/update_climate_preset" + ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -1103,6 +1179,11 @@ def update_climate_preset( return None + @route_metadata( + path="/thermostats/update_weekly_program", + has_required_parameters=True, + has_pagination=False, + ) def update_weekly_program( self, *, @@ -1149,7 +1230,9 @@ def update_weekly_program( tuesday_program_id is not None, wednesday_program_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/update_weekly_program" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index 4b54bac1..0b09be91 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ThermostatDailyProgram, ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -63,6 +64,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/thermostats/daily_programs/create", + has_required_parameters=True, + has_pagination=False, + ) def create( self, *, device_id: str, name: str, periods: List[Dict[str, Any]] ) -> ThermostatDailyProgram: @@ -78,7 +84,9 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, name is not None, periods is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/daily_programs/create" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -92,6 +100,11 @@ def create( return ThermostatDailyProgram.from_dict(res["thermostat_daily_program"]) + @route_metadata( + path="/thermostats/daily_programs/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, thermostat_daily_program_id: str) -> None: """Deletes a thermostat daily program. @@ -99,7 +112,9 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(thermostat_daily_program_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/daily_programs/delete" + ) params: Dict[str, Any] = {} if thermostat_daily_program_id is not None: @@ -109,6 +124,11 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: return None + @route_metadata( + path="/thermostats/daily_programs/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -135,7 +155,9 @@ def update( periods is not None, thermostat_daily_program_id is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/daily_programs/update" + ) json_payload: Dict[str, Any] = {} if name is not None: diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index 32662695..c05e503c 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ThermostatSchedule @@ -111,6 +112,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/thermostats/schedules/create", + has_required_parameters=True, + has_pagination=False, + ) def create( self, *, @@ -150,7 +156,9 @@ def create( max_override_period_minutes is not None, name is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/schedules/create" + ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -172,6 +180,11 @@ def create( return ThermostatSchedule.from_dict(res["thermostat_schedule"]) + @route_metadata( + path="/thermostats/schedules/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, thermostat_schedule_id: str) -> None: """Deletes a `thermostat schedule `_ for a specified `thermostat `_. @@ -179,7 +192,9 @@ def delete(self, *, thermostat_schedule_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(thermostat_schedule_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/schedules/delete" + ) params: Dict[str, Any] = {} if thermostat_schedule_id is not None: @@ -189,6 +204,11 @@ def delete(self, *, thermostat_schedule_id: str) -> None: return None + @route_metadata( + path="/thermostats/schedules/get", + has_required_parameters=True, + has_pagination=False, + ) def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: """Returns a specified `thermostat schedule `_. @@ -198,7 +218,9 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: :raises ValueError: At least one parameter must be provided.""" if not any(thermostat_schedule_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/schedules/get" + ) params: Dict[str, Any] = {} if thermostat_schedule_id is not None: @@ -208,6 +230,11 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: return ThermostatSchedule.from_dict(res["thermostat_schedule"]) + @route_metadata( + path="/thermostats/schedules/list", + has_required_parameters=True, + has_pagination=False, + ) def list( self, *, device_id: str, user_identifier_key: Optional[str] = None ) -> List[ThermostatSchedule]: @@ -221,7 +248,9 @@ def list( :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, user_identifier_key is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/schedules/list" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -235,6 +264,11 @@ def list( ThermostatSchedule.from_dict(item) for item in res["thermostat_schedules"] ] + @route_metadata( + path="/thermostats/schedules/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -272,7 +306,9 @@ def update( name is not None, starts_at is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/schedules/update" + ) json_payload: Dict[str, Any] = {} if thermostat_schedule_id is not None: diff --git a/seam/routes/thermostats_simulate.py b/seam/routes/thermostats_simulate.py index da8c17ed..4989a7ce 100644 --- a/seam/routes/thermostats_simulate.py +++ b/seam/routes/thermostats_simulate.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata class AbstractThermostatsSimulate(abc.ABC): @@ -58,6 +59,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/thermostats/simulate/hvac_mode_adjusted", + has_required_parameters=True, + has_pagination=False, + ) def hvac_mode_adjusted( self, *, @@ -91,7 +97,9 @@ def hvac_mode_adjusted( heating_set_point_celsius is not None, heating_set_point_fahrenheit is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -111,6 +119,11 @@ def hvac_mode_adjusted( return None + @route_metadata( + path="/thermostats/simulate/temperature_reached", + has_required_parameters=True, + has_pagination=False, + ) def temperature_reached( self, *, @@ -132,7 +145,9 @@ def temperature_reached( temperature_celsius is not None, temperature_fahrenheit is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /thermostats/simulate/temperature_reached" + ) json_payload: Dict[str, Any] = {} if device_id is not None: diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index f550975e..c2ccaa4c 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import ( UserIdentity, InstantKey, @@ -259,6 +260,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def unmanaged(self) -> UserIdentitiesUnmanaged: return self._unmanaged + @route_metadata( + path="/user_identities/add_acs_user", + has_required_parameters=True, + has_pagination=False, + ) def add_acs_user( self, *, @@ -284,7 +290,9 @@ def add_acs_user( user_identity_id is not None, user_identity_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/add_acs_user" + ) json_payload: Dict[str, Any] = {} if acs_user_id is not None: @@ -298,6 +306,11 @@ def add_acs_user( return None + @route_metadata( + path="/user_identities/create", + has_required_parameters=False, + has_pagination=False, + ) def create( self, *, @@ -337,6 +350,11 @@ def create( return UserIdentity.from_dict(res["user_identity"]) + @route_metadata( + path="/user_identities/delete", + has_required_parameters=True, + has_pagination=False, + ) def delete(self, *, user_identity_id: str) -> None: """Deletes a specified `user identity `_. This deletes the user identity and all associated resources, including any `credentials `_, `acs users `_ and `client sessions `_. @@ -344,7 +362,9 @@ def delete(self, *, user_identity_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/delete" + ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -354,6 +374,11 @@ def delete(self, *, user_identity_id: str) -> None: return None + @route_metadata( + path="/user_identities/generate_instant_key", + has_required_parameters=True, + has_pagination=False, + ) def generate_instant_key( self, *, @@ -377,7 +402,9 @@ def generate_instant_key( customization_profile_id is not None, max_use_count is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/generate_instant_key" + ) json_payload: Dict[str, Any] = {} if user_identity_id is not None: @@ -393,6 +420,9 @@ def generate_instant_key( return InstantKey.from_dict(res["instant_key"]) + @route_metadata( + path="/user_identities/get", has_required_parameters=True, has_pagination=False + ) def get( self, *, @@ -409,7 +439,9 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any(user_identity_id is not None, user_identity_key is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/get" + ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -421,6 +453,11 @@ def get( return UserIdentity.from_dict(res["user_identity"]) + @route_metadata( + path="/user_identities/grant_access_to_device", + has_required_parameters=True, + has_pagination=False, + ) def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> None: """Grants a specified `user identity `_ access to a specified `device `_. @@ -430,7 +467,9 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/grant_access_to_device" + ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -442,6 +481,9 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No return None + @route_metadata( + path="/user_identities/list", has_required_parameters=False, has_pagination=True + ) def list( self, *, @@ -488,6 +530,11 @@ def list( return [UserIdentity.from_dict(item) for item in res["user_identities"]] + @route_metadata( + path="/user_identities/list_accessible_devices", + has_required_parameters=True, + has_pagination=False, + ) def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: """Returns a list of all `devices `_ associated with a specified `user identity `_. This includes devices derived from the access grants assigned to the user identity and devices directly linked to the user identity. @@ -497,7 +544,9 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: :raises ValueError: At least one parameter must be provided.""" if not any(user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/list_accessible_devices" + ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -507,6 +556,11 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: return [Device.from_dict(item) for item in res["devices"]] + @route_metadata( + path="/user_identities/list_accessible_entrances", + has_required_parameters=True, + has_pagination=False, + ) def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntrance]: """Returns a list of all `ACS entrances `_ accessible to a specified `user identity `_. This includes entrances derived from the access grants assigned to the user identity and entrances accessible through ACS users linked to the user identity. @@ -516,7 +570,9 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc :raises ValueError: At least one parameter must be provided.""" if not any(user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/list_accessible_entrances" + ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -528,6 +584,11 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] + @route_metadata( + path="/user_identities/list_acs_systems", + has_required_parameters=True, + has_pagination=False, + ) def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: """Returns a list of all `access systems `_ associated with a specified `user identity `_. @@ -537,7 +598,9 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: :raises ValueError: At least one parameter must be provided.""" if not any(user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/list_acs_systems" + ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -547,6 +610,11 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: return [AcsSystem.from_dict(item) for item in res["acs_systems"]] + @route_metadata( + path="/user_identities/list_acs_users", + has_required_parameters=True, + has_pagination=False, + ) def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: """Returns a list of all `access system users `_ assigned to a specified `user identity `_. @@ -556,7 +624,9 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: :raises ValueError: At least one parameter must be provided.""" if not any(user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/list_acs_users" + ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -566,6 +636,11 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: return [AcsUser.from_dict(item) for item in res["acs_users"]] + @route_metadata( + path="/user_identities/remove_acs_user", + has_required_parameters=True, + has_pagination=False, + ) def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: """Removes a specified `access system user `_ from a specified `user identity `_. @@ -575,7 +650,9 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(acs_user_id is not None, user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/remove_acs_user" + ) params: Dict[str, Any] = {} if acs_user_id is not None: @@ -587,6 +664,11 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: return None + @route_metadata( + path="/user_identities/revoke_access_to_device", + has_required_parameters=True, + has_pagination=False, + ) def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> None: """Revokes access to a specified `device `_ from a specified `user identity `_. @@ -596,7 +678,9 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N :raises ValueError: At least one parameter must be provided.""" if not any(device_id is not None, user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/revoke_access_to_device" + ) params: Dict[str, Any] = {} if device_id is not None: @@ -608,6 +692,11 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N return None + @route_metadata( + path="/user_identities/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -637,7 +726,9 @@ def update( phone_number is not None, user_identity_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/update" + ) json_payload: Dict[str, Any] = {} if user_identity_id is not None: diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index c05581ec..9f8c8aad 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import UnmanagedUserIdentity @@ -66,6 +67,11 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/user_identities/unmanaged/get", + has_required_parameters=True, + has_pagination=False, + ) def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: """Returns a specified unmanaged `user identity `_ (where is_managed = false). @@ -75,7 +81,9 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: :raises ValueError: At least one parameter must be provided.""" if not any(user_identity_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/unmanaged/get" + ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -85,6 +93,11 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: return UnmanagedUserIdentity.from_dict(res["user_identity"]) + @route_metadata( + path="/user_identities/unmanaged/list", + has_required_parameters=False, + has_pagination=True, + ) def list( self, *, @@ -121,6 +134,11 @@ def list( UnmanagedUserIdentity.from_dict(item) for item in res["user_identities"] ] + @route_metadata( + path="/user_identities/unmanaged/update", + has_required_parameters=True, + has_pagination=False, + ) def update( self, *, @@ -144,7 +162,9 @@ def update( user_identity_id is not None, user_identity_key is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /user_identities/unmanaged/update" + ) json_payload: Dict[str, Any] = {} if is_managed is not None: diff --git a/seam/routes/webhooks.py b/seam/routes/webhooks.py index 862634cf..33674c96 100644 --- a/seam/routes/webhooks.py +++ b/seam/routes/webhooks.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import Webhook @@ -63,6 +64,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/webhooks/create", has_required_parameters=True, has_pagination=False + ) def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhook: """Creates a new `webhook `_. @@ -74,7 +78,7 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo :raises ValueError: At least one parameter must be provided.""" if not any(url is not None, event_types is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /webhooks/create") json_payload: Dict[str, Any] = {} if url is not None: @@ -86,6 +90,9 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo return Webhook.from_dict(res["webhook"]) + @route_metadata( + path="/webhooks/delete", has_required_parameters=True, has_pagination=False + ) def delete(self, *, webhook_id: str) -> None: """Deletes a specified `webhook `_. @@ -93,7 +100,7 @@ def delete(self, *, webhook_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(webhook_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /webhooks/delete") params: Dict[str, Any] = {} if webhook_id is not None: @@ -103,6 +110,9 @@ def delete(self, *, webhook_id: str) -> None: return None + @route_metadata( + path="/webhooks/get", has_required_parameters=True, has_pagination=False + ) def get(self, *, webhook_id: str) -> Webhook: """Gets a specified `webhook `_. @@ -112,7 +122,7 @@ def get(self, *, webhook_id: str) -> Webhook: :raises ValueError: At least one parameter must be provided.""" if not any(webhook_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /webhooks/get") params: Dict[str, Any] = {} if webhook_id is not None: @@ -122,6 +132,9 @@ def get(self, *, webhook_id: str) -> Webhook: return Webhook.from_dict(res["webhook"]) + @route_metadata( + path="/webhooks/list", has_required_parameters=False, has_pagination=False + ) def list(self) -> List[Webhook]: """Returns a list of all `webhooks `_. @@ -132,6 +145,9 @@ def list(self) -> List[Webhook]: return [Webhook.from_dict(item) for item in res["webhooks"]] + @route_metadata( + path="/webhooks/update", has_required_parameters=True, has_pagination=False + ) def update(self, *, event_types: List[str], webhook_id: str) -> None: """Updates a specified `webhook `_. @@ -141,7 +157,7 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: :raises ValueError: At least one parameter must be provided.""" if not any(event_types is not None, webhook_id is not None): - raise ValueError("At least one parameter must be provided") + raise ValueError("At least one parameter is required for /webhooks/update") json_payload: Dict[str, Any] = {} if event_types is not None: diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index 671eab3f..26f3529d 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -1,6 +1,7 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient +from ..route import route_metadata from ..resources import Workspace, ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -107,6 +108,9 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.client = client self.defaults = defaults + @route_metadata( + path="/workspaces/create", has_required_parameters=True, has_pagination=False + ) def create( self, *, @@ -158,7 +162,9 @@ def create( webview_primary_button_text_color is not None, webview_success_message is not None, ): - raise ValueError("At least one parameter must be provided") + raise ValueError( + "At least one parameter is required for /workspaces/create" + ) json_payload: Dict[str, Any] = {} if name is not None: @@ -190,6 +196,9 @@ def create( return Workspace.from_dict(res["workspace"]) + @route_metadata( + path="/workspaces/get", has_required_parameters=False, has_pagination=False + ) def get(self) -> Workspace: """Returns the `workspace `_ associated with the authentication value. @@ -200,6 +209,9 @@ def get(self) -> Workspace: return Workspace.from_dict(res["workspace"]) + @route_metadata( + path="/workspaces/list", has_required_parameters=False, has_pagination=False + ) def list(self) -> List[Workspace]: """Returns a list of `workspaces `_ associated with the authentication value. @@ -210,6 +222,11 @@ def list(self) -> List[Workspace]: return [Workspace.from_dict(item) for item in res["workspaces"]] + @route_metadata( + path="/workspaces/reset_sandbox", + has_required_parameters=False, + has_pagination=False, + ) def reset_sandbox( self, *, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: @@ -234,6 +251,9 @@ def reset_sandbox( wait_for_action_attempt=wait_for_action_attempt, ) + @route_metadata( + path="/workspaces/update", has_required_parameters=False, has_pagination=False + ) def update( self, *, From fccc16dada1072cd7c859015186d721716c63cb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:15:47 +0000 Subject: [PATCH 25/39] fix: Generate a valid any() call for required parameter checks The required parameter guard generated one any() argument per parameter, so every call raised TypeError instead of validating: any() takes a single iterable, not a variadic list of conditions. Wrap the conditions in a list and regenerate. Also fix the fallout that this masked in the rest of the suite: - Type the put and patch client helpers as returning the decoded body, matching get, post and delete, now that generated routes use them. - Set the route metadata attributes through Any, since a function does not declare them. - Allow TODO comments, which the test suite uses to track routes that cannot use the generated method yet. - Assert the GET request line and empty body in the default headers test, and drive the invalid input test through a route that still takes a JSON payload, now that /devices/get is a GET route. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01EFYHA1UHvARfcMBkUnWgGs --- codegen/layouts/partials/route-method.hbs | 2 +- pylintrc | 1 + seam/client.py | 6 + seam/route.py | 10 +- seam/routes/access_codes.py | 136 ++++++++++-------- seam/routes/access_codes_simulate.py | 2 +- seam/routes/access_codes_unmanaged.py | 40 +++--- seam/routes/access_grants.py | 60 ++++---- seam/routes/access_grants_unmanaged.py | 10 +- seam/routes/access_methods.py | 36 ++--- seam/routes/access_methods_unmanaged.py | 12 +- seam/routes/acs_access_groups.py | 24 ++-- seam/routes/acs_credentials.py | 52 ++++--- seam/routes/acs_encoders.py | 22 +-- seam/routes/acs_encoders_simulate.py | 26 ++-- seam/routes/acs_entrances.py | 14 +- seam/routes/acs_systems.py | 12 +- seam/routes/acs_users.py | 96 ++++++++----- seam/routes/action_attempts.py | 2 +- seam/routes/client_sessions.py | 18 +-- seam/routes/connect_webviews.py | 4 +- seam/routes/connected_accounts.py | 20 +-- seam/routes/connected_accounts_simulate.py | 2 +- seam/routes/customers.py | 42 +++--- seam/routes/devices.py | 18 +-- seam/routes/devices_simulate.py | 12 +- seam/routes/devices_unmanaged.py | 4 +- seam/routes/events.py | 62 ++++---- seam/routes/instant_keys.py | 4 +- seam/routes/locks.py | 14 +- seam/routes/locks_simulate.py | 4 +- seam/routes/noise_sensors_noise_thresholds.py | 36 ++--- seam/routes/noise_sensors_simulate.py | 2 +- seam/routes/phones.py | 4 +- seam/routes/phones_simulate.py | 10 +- seam/routes/spaces.py | 42 +++--- seam/routes/thermostats.py | 134 +++++++++-------- seam/routes/thermostats_daily_programs.py | 12 +- seam/routes/thermostats_schedules.py | 38 ++--- seam/routes/thermostats_simulate.py | 22 +-- seam/routes/user_identities.py | 46 +++--- seam/routes/user_identities_unmanaged.py | 10 +- seam/routes/webhooks.py | 8 +- seam/routes/workspaces.py | 22 +-- test/headers_test.py | 4 +- test/http_error_test.py | 2 +- 46 files changed, 653 insertions(+), 506 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 5f85357f..5016c716 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -2,7 +2,7 @@ def {{> method-signature}}: """{{> method-docstring}}""" {{#if hasRequiredParameters}} - if not any({{#each params}}{{name}} is not None{{#unless @last}}, {{/unless}}{{/each}}): + if not any([{{#each params}}{{name}} is not None{{#unless @last}}, {{/unless}}{{/each}}]): raise ValueError("At least one parameter is required for {{path}}") {{/if}} {{payloadVar}}: Dict[str, Any] = {} diff --git a/pylintrc b/pylintrc index b7ab19c8..24f4bdb7 100644 --- a/pylintrc +++ b/pylintrc @@ -20,5 +20,6 @@ disable= line-too-long, too-many-lines, unnecessary-pass, + fixme, redefined-outer-name, duplicate-code diff --git a/seam/client.py b/seam/client.py index 600f6a2b..723037f8 100644 --- a/seam/client.py +++ b/seam/client.py @@ -92,6 +92,12 @@ def get(self, url, **kwargs) -> Any: def post(self, url, data=None, json=None, **kwargs) -> Any: return self.request("POST", url, data=data, json=json, **kwargs) + def put(self, url, data=None, json=None, **kwargs) -> Any: + return self.request("PUT", url, data=data, json=json, **kwargs) + + def patch(self, url, data=None, json=None, **kwargs) -> Any: + return self.request("PATCH", url, data=data, json=json, **kwargs) + def delete(self, url, json=None, **kwargs) -> Any: return self.request("DELETE", url, json=json, **kwargs) diff --git a/seam/route.py b/seam/route.py index e69366ca..3a3a4b50 100644 --- a/seam/route.py +++ b/seam/route.py @@ -1,4 +1,4 @@ -from typing import Callable, TypeVar +from typing import Any, Callable, TypeVar, cast F = TypeVar("F", bound=Callable) @@ -8,9 +8,11 @@ def route_metadata(*, path: str, has_required_parameters: bool, has_pagination: """Attach generated route metadata to a request callable.""" def decorate(request: F) -> F: - request.__seam_path__ = path - request.__seam_has_required_parameters__ = has_required_parameters - request.__seam_has_pagination__ = has_pagination + # Functions do not declare these attributes, so set them through Any. + route = cast(Any, request) + route.__seam_path__ = path + route.__seam_has_required_parameters__ = has_required_parameters + route.__seam_has_pagination__ = has_pagination return request return decorate diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index 31a89427..12fb6fc4 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -447,22 +447,24 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - allow_external_modification is not None, - attempt_for_offline_device is not None, - code is not None, - common_code_key is not None, - ends_at is not None, - is_external_modification_allowed is not None, - is_offline_access_code is not None, - is_one_time_use is not None, - max_time_rounding is not None, - name is not None, - prefer_native_scheduling is not None, - preferred_code_length is not None, - starts_at is not None, - use_backup_access_code_pool is not None, - use_offline_access_code is not None, + [ + device_id is not None, + allow_external_modification is not None, + attempt_for_offline_device is not None, + code is not None, + common_code_key is not None, + ends_at is not None, + is_external_modification_allowed is not None, + is_offline_access_code is not None, + is_one_time_use is not None, + max_time_rounding is not None, + name is not None, + prefer_native_scheduling is not None, + preferred_code_length is not None, + starts_at is not None, + use_backup_access_code_pool is not None, + use_offline_access_code is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/create" @@ -575,18 +577,20 @@ def create_multiple( :raises ValueError: At least one parameter must be provided.""" if not any( - device_ids is not None, - allow_external_modification is not None, - attempt_for_offline_device is not None, - behavior_when_code_cannot_be_shared is not None, - code is not None, - ends_at is not None, - is_external_modification_allowed is not None, - name is not None, - prefer_native_scheduling is not None, - preferred_code_length is not None, - starts_at is not None, - use_backup_access_code_pool is not None, + [ + device_ids is not None, + allow_external_modification is not None, + attempt_for_offline_device is not None, + behavior_when_code_cannot_be_shared is not None, + code is not None, + ends_at is not None, + is_external_modification_allowed is not None, + name is not None, + prefer_native_scheduling is not None, + preferred_code_length is not None, + starts_at is not None, + use_backup_access_code_pool is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/create_multiple" @@ -637,7 +641,7 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non :param device_id: ID of the device for which you want to delete the access code. :raises ValueError: At least one parameter must be provided.""" - if not any(access_code_id is not None, device_id is not None): + if not any([access_code_id is not None, device_id is not None]): raise ValueError( "At least one parameter is required for /access_codes/delete" ) @@ -665,7 +669,7 @@ def generate_code(self, *, device_id: str) -> AccessCode: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /access_codes/generate_code" ) @@ -701,7 +705,9 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_code_id is not None, code is not None, device_id is not None): + if not any( + [access_code_id is not None, code is not None, device_id is not None] + ): raise ValueError("At least one parameter is required for /access_codes/get") params: Dict[str, Any] = {} @@ -761,16 +767,18 @@ def list( :raises ValueError: At least one parameter must be provided.""" if not any( - access_code_ids is not None, - access_grant_id is not None, - access_grant_key is not None, - access_method_id is not None, - customer_key is not None, - device_id is not None, - limit is not None, - page_cursor is not None, - search is not None, - user_identifier_key is not None, + [ + access_code_ids is not None, + access_grant_id is not None, + access_grant_key is not None, + access_method_id is not None, + customer_key is not None, + device_id is not None, + limit is not None, + page_cursor is not None, + search is not None, + user_identifier_key is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/list" @@ -823,7 +831,7 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_code_id is not None): + if not any([access_code_id is not None]): raise ValueError( "At least one parameter is required for /access_codes/pull_backup_access_code" ) @@ -865,10 +873,12 @@ def report_device_constraints( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - max_code_length is not None, - min_code_length is not None, - supported_code_lengths is not None, + [ + device_id is not None, + max_code_length is not None, + min_code_length is not None, + supported_code_lengths is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/report_device_constraints" @@ -940,17 +950,19 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - access_code_id is not None, - allow_external_modification is not None, - attempt_for_offline_device is not None, - code is not None, - device_id is not None, - ends_at is not None, - is_external_modification_allowed is not None, - is_managed is not None, - name is not None, - starts_at is not None, - type is not None, + [ + access_code_id is not None, + allow_external_modification is not None, + attempt_for_offline_device is not None, + code is not None, + device_id is not None, + ends_at is not None, + is_external_modification_allowed is not None, + is_managed is not None, + name is not None, + starts_at is not None, + type is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/update" @@ -1021,10 +1033,12 @@ def update_multiple( :raises ValueError: At least one parameter must be provided.""" if not any( - common_code_key is not None, - ends_at is not None, - name is not None, - starts_at is not None, + [ + common_code_key is not None, + ends_at is not None, + name is not None, + starts_at is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/update_multiple" diff --git a/seam/routes/access_codes_simulate.py b/seam/routes/access_codes_simulate.py index 50790597..899049f3 100644 --- a/seam/routes/access_codes_simulate.py +++ b/seam/routes/access_codes_simulate.py @@ -49,7 +49,7 @@ def create_unmanaged_access_code( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(code is not None, device_id is not None, name is not None): + if not any([code is not None, device_id is not None, name is not None]): raise ValueError( "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code" ) diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index d750151e..988ec2c0 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -152,10 +152,12 @@ def convert_to_managed( :raises ValueError: At least one parameter must be provided.""" if not any( - access_code_id is not None, - allow_external_modification is not None, - force is not None, - is_external_modification_allowed is not None, + [ + access_code_id is not None, + allow_external_modification is not None, + force is not None, + is_external_modification_allowed is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/unmanaged/convert_to_managed" @@ -190,7 +192,7 @@ def delete(self, *, access_code_id: str) -> None: :param access_code_id: ID of the unmanaged access code that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(access_code_id is not None): + if not any([access_code_id is not None]): raise ValueError( "At least one parameter is required for /access_codes/unmanaged/delete" ) @@ -228,7 +230,9 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_code_id is not None, code is not None, device_id is not None): + if not any( + [access_code_id is not None, code is not None, device_id is not None] + ): raise ValueError( "At least one parameter is required for /access_codes/unmanaged/get" ) @@ -275,11 +279,13 @@ def list( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - limit is not None, - page_cursor is not None, - search is not None, - user_identifier_key is not None, + [ + device_id is not None, + limit is not None, + page_cursor is not None, + search is not None, + user_identifier_key is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/unmanaged/list" @@ -329,11 +335,13 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - access_code_id is not None, - is_managed is not None, - allow_external_modification is not None, - force is not None, - is_external_modification_allowed is not None, + [ + access_code_id is not None, + is_managed is not None, + allow_external_modification is not None, + force is not None, + is_external_modification_allowed is not None, + ] ): raise ValueError( "At least one parameter is required for /access_codes/unmanaged/update" diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index c4a259e6..864f3556 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -282,21 +282,23 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - requested_access_methods is not None, - user_identity_id is not None, - user_identity is not None, - access_grant_key is not None, - acs_entrance_ids is not None, - customization_profile_id is not None, - device_ids is not None, - ends_at is not None, - location is not None, - location_ids is not None, - name is not None, - reservation_key is not None, - space_ids is not None, - space_keys is not None, - starts_at is not None, + [ + requested_access_methods is not None, + user_identity_id is not None, + user_identity is not None, + access_grant_key is not None, + acs_entrance_ids is not None, + customization_profile_id is not None, + device_ids is not None, + ends_at is not None, + location is not None, + location_ids is not None, + name is not None, + reservation_key is not None, + space_ids is not None, + space_keys is not None, + starts_at is not None, + ] ): raise ValueError( "At least one parameter is required for /access_grants/create" @@ -347,7 +349,7 @@ def delete(self, *, access_grant_id: str) -> None: :param access_grant_id: ID of Access Grant to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(access_grant_id is not None): + if not any([access_grant_id is not None]): raise ValueError( "At least one parameter is required for /access_grants/delete" ) @@ -378,7 +380,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_grant_id is not None, access_grant_key is not None): + if not any([access_grant_id is not None, access_grant_key is not None]): raise ValueError( "At least one parameter is required for /access_grants/get" ) @@ -420,10 +422,12 @@ def get_related( :raises ValueError: At least one parameter must be provided.""" if not any( - access_grant_ids is not None, - access_grant_keys is not None, - exclude is not None, - include is not None, + [ + access_grant_ids is not None, + access_grant_keys is not None, + exclude is not None, + include is not None, + ] ): raise ValueError( "At least one parameter is required for /access_grants/get_related" @@ -542,7 +546,7 @@ def request_access_methods( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_grant_id is not None, requested_access_methods is not None): + if not any([access_grant_id is not None, requested_access_methods is not None]): raise ValueError( "At least one parameter is required for /access_grants/request_access_methods" ) @@ -585,11 +589,13 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - access_grant_id is not None, - access_grant_key is not None, - ends_at is not None, - name is not None, - starts_at is not None, + [ + access_grant_id is not None, + access_grant_key is not None, + ends_at is not None, + name is not None, + starts_at is not None, + ] ): raise ValueError( "At least one parameter is required for /access_grants/update" diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index e9f3042c..841a23ee 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -88,7 +88,7 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_grant_id is not None): + if not any([access_grant_id is not None]): raise ValueError( "At least one parameter is required for /access_grants/unmanaged/get" ) @@ -176,9 +176,11 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - access_grant_id is not None, - is_managed is not None, - access_grant_key is not None, + [ + access_grant_id is not None, + is_managed is not None, + access_grant_key is not None, + ] ): raise ValueError( "At least one parameter is required for /access_grants/unmanaged/update" diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index 8d369b09..d11cc4d9 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -201,7 +201,7 @@ def assign_card( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_method_id is not None, card_number is not None): + if not any([access_method_id is not None, card_number is not None]): raise ValueError( "At least one parameter is required for /access_methods/assign_card" ) @@ -248,9 +248,11 @@ def delete( :raises ValueError: At least one parameter must be provided.""" if not any( - access_method_id is not None, - access_grant_id is not None, - reservation_key is not None, + [ + access_method_id is not None, + access_grant_id is not None, + reservation_key is not None, + ] ): raise ValueError( "At least one parameter is required for /access_methods/delete" @@ -291,7 +293,7 @@ def encode( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_method_id is not None, acs_encoder_id is not None): + if not any([access_method_id is not None, acs_encoder_id is not None]): raise ValueError( "At least one parameter is required for /access_methods/encode" ) @@ -327,7 +329,7 @@ def get(self, *, access_method_id: str) -> AccessMethod: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_method_id is not None): + if not any([access_method_id is not None]): raise ValueError( "At least one parameter is required for /access_methods/get" ) @@ -364,7 +366,7 @@ def get_related( :raises ValueError: At least one parameter must be provided.""" if not any( - access_method_ids is not None, exclude is not None, include is not None + [access_method_ids is not None, exclude is not None, include is not None] ): raise ValueError( "At least one parameter is required for /access_methods/get_related" @@ -419,14 +421,16 @@ def list( :raises ValueError: At least one parameter must be provided.""" if not any( - access_code_id is not None, - access_grant_id is not None, - access_grant_key is not None, - acs_entrance_id is not None, - device_id is not None, - limit is not None, - page_cursor is not None, - space_id is not None, + [ + access_code_id is not None, + access_grant_id is not None, + access_grant_key is not None, + acs_entrance_id is not None, + device_id is not None, + limit is not None, + page_cursor is not None, + space_id is not None, + ] ): raise ValueError( "At least one parameter is required for /access_methods/list" @@ -477,7 +481,7 @@ def unlock_door( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_method_id is not None, acs_entrance_id is not None): + if not any([access_method_id is not None, acs_entrance_id is not None]): raise ValueError( "At least one parameter is required for /access_methods/unlock_door" ) diff --git a/seam/routes/access_methods_unmanaged.py b/seam/routes/access_methods_unmanaged.py index c9a39c79..34436af1 100644 --- a/seam/routes/access_methods_unmanaged.py +++ b/seam/routes/access_methods_unmanaged.py @@ -61,7 +61,7 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(access_method_id is not None): + if not any([access_method_id is not None]): raise ValueError( "At least one parameter is required for /access_methods/unmanaged/get" ) @@ -101,10 +101,12 @@ def list( :raises ValueError: At least one parameter must be provided.""" if not any( - access_grant_id is not None, - acs_entrance_id is not None, - device_id is not None, - space_id is not None, + [ + access_grant_id is not None, + acs_entrance_id is not None, + device_id is not None, + space_id is not None, + ] ): raise ValueError( "At least one parameter is required for /access_methods/unmanaged/list" diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index fa3ee46f..7a0d93f9 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -139,9 +139,11 @@ def add_user( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_access_group_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_access_group_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/access_groups/add_user" @@ -170,7 +172,7 @@ def delete(self, *, acs_access_group_id: str) -> None: :param acs_access_group_id: ID of the access group that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(acs_access_group_id is not None): + if not any([acs_access_group_id is not None]): raise ValueError( "At least one parameter is required for /acs/access_groups/delete" ) @@ -196,7 +198,7 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_access_group_id is not None): + if not any([acs_access_group_id is not None]): raise ValueError( "At least one parameter is required for /acs/access_groups/get" ) @@ -263,7 +265,7 @@ def list_accessible_entrances( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_access_group_id is not None): + if not any([acs_access_group_id is not None]): raise ValueError( "At least one parameter is required for /acs/access_groups/list_accessible_entrances" ) @@ -291,7 +293,7 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_access_group_id is not None): + if not any([acs_access_group_id is not None]): raise ValueError( "At least one parameter is required for /acs/access_groups/list_users" ) @@ -326,9 +328,11 @@ def remove_user( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_access_group_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_access_group_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/access_groups/remove_user" diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index b8faccc6..791e75da 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -208,9 +208,11 @@ def assign( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_credential_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_credential_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/credentials/assign" @@ -282,19 +284,21 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - access_method is not None, - acs_system_id is not None, - acs_user_id is not None, - allowed_acs_entrance_ids is not None, - assa_abloy_vostio_metadata is not None, - code is not None, - credential_manager_acs_system_id is not None, - ends_at is not None, - is_multi_phone_sync_credential is not None, - salto_space_metadata is not None, - starts_at is not None, - user_identity_id is not None, - visionline_metadata is not None, + [ + access_method is not None, + acs_system_id is not None, + acs_user_id is not None, + allowed_acs_entrance_ids is not None, + assa_abloy_vostio_metadata is not None, + code is not None, + credential_manager_acs_system_id is not None, + ends_at is not None, + is_multi_phone_sync_credential is not None, + salto_space_metadata is not None, + starts_at is not None, + user_identity_id is not None, + visionline_metadata is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/credentials/create" @@ -347,7 +351,7 @@ def delete(self, *, acs_credential_id: str) -> None: :param acs_credential_id: ID of the credential that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(acs_credential_id is not None): + if not any([acs_credential_id is not None]): raise ValueError( "At least one parameter is required for /acs/credentials/delete" ) @@ -371,7 +375,7 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_credential_id is not None): + if not any([acs_credential_id is not None]): raise ValueError( "At least one parameter is required for /acs/credentials/get" ) @@ -454,7 +458,7 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_credential_id is not None): + if not any([acs_credential_id is not None]): raise ValueError( "At least one parameter is required for /acs/credentials/list_accessible_entrances" ) @@ -491,9 +495,11 @@ def unassign( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_credential_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_credential_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/credentials/unassign" @@ -533,7 +539,7 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_credential_id is not None, code is not None, ends_at is not None + [acs_credential_id is not None, code is not None, ends_at is not None] ): raise ValueError( "At least one parameter is required for /acs/credentials/update" diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index 34e8c077..008cc01d 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -160,9 +160,11 @@ def encode_credential( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_encoder_id is not None, - access_method_id is not None, - acs_credential_id is not None, + [ + acs_encoder_id is not None, + access_method_id is not None, + acs_credential_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/encoders/encode_credential" @@ -201,7 +203,7 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_encoder_id is not None): + if not any([acs_encoder_id is not None]): raise ValueError("At least one parameter is required for /acs/encoders/get") params: Dict[str, Any] = {} @@ -277,7 +279,7 @@ def scan_credential( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_encoder_id is not None, salto_ks_metadata is not None): + if not any([acs_encoder_id is not None, salto_ks_metadata is not None]): raise ValueError( "At least one parameter is required for /acs/encoders/scan_credential" ) @@ -332,10 +334,12 @@ def scan_to_assign_credential( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_encoder_id is not None, - acs_user_id is not None, - salto_ks_metadata is not None, - user_identity_id is not None, + [ + acs_encoder_id is not None, + acs_user_id is not None, + salto_ks_metadata is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/encoders/scan_to_assign_credential" diff --git a/seam/routes/acs_encoders_simulate.py b/seam/routes/acs_encoders_simulate.py index 724ebb54..9a5926bd 100644 --- a/seam/routes/acs_encoders_simulate.py +++ b/seam/routes/acs_encoders_simulate.py @@ -104,9 +104,11 @@ def next_credential_encode_will_fail( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_encoder_id is not None, - error_code is not None, - acs_credential_id is not None, + [ + acs_encoder_id is not None, + error_code is not None, + acs_credential_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail" @@ -141,7 +143,7 @@ def next_credential_encode_will_succeed( :param scenario: Scenario to simulate. :raises ValueError: At least one parameter must be provided.""" - if not any(acs_encoder_id is not None, scenario is not None): + if not any([acs_encoder_id is not None, scenario is not None]): raise ValueError( "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed" ) @@ -181,9 +183,11 @@ def next_credential_scan_will_fail( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_encoder_id is not None, - error_code is not None, - acs_credential_id_on_seam is not None, + [ + acs_encoder_id is not None, + error_code is not None, + acs_credential_id_on_seam is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail" @@ -225,9 +229,11 @@ def next_credential_scan_will_succeed( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_encoder_id is not None, - acs_credential_id_on_seam is not None, - scenario is not None, + [ + acs_encoder_id is not None, + acs_credential_id_on_seam is not None, + scenario is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed" diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 88c7db06..c25e7848 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -134,7 +134,7 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_entrance_id is not None): + if not any([acs_entrance_id is not None]): raise ValueError( "At least one parameter is required for /acs/entrances/get" ) @@ -169,9 +169,11 @@ def grant_access( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_entrance_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_entrance_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/entrances/grant_access" @@ -278,7 +280,7 @@ def list_credentials_with_access( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_entrance_id is not None, include_if is not None): + if not any([acs_entrance_id is not None, include_if is not None]): raise ValueError( "At least one parameter is required for /acs/entrances/list_credentials_with_access" ) @@ -316,7 +318,7 @@ def unlock( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_credential_id is not None, acs_entrance_id is not None): + if not any([acs_credential_id is not None, acs_entrance_id is not None]): raise ValueError( "At least one parameter is required for /acs/entrances/unlock" ) diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index 04eda58a..41a728c5 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -90,7 +90,7 @@ def get(self, *, acs_system_id: str) -> AcsSystem: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_system_id is not None): + if not any([acs_system_id is not None]): raise ValueError("At least one parameter is required for /acs/systems/get") params: Dict[str, Any] = {} @@ -152,7 +152,7 @@ def list_compatible_credential_manager_acs_systems( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(acs_system_id is not None): + if not any([acs_system_id is not None]): raise ValueError( "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems" ) @@ -189,9 +189,11 @@ def report_devices( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_system_id is not None, - acs_encoders is not None, - acs_entrances is not None, + [ + acs_system_id is not None, + acs_encoders is not None, + acs_entrances is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/systems/report_devices" diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 9d9473a6..1b0636bc 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -285,7 +285,7 @@ def add_to_access_group( :param acs_user_id: ID of the access system user that you want to add to an access group. :raises ValueError: At least one parameter must be provided.""" - if not any(acs_access_group_id is not None, acs_user_id is not None): + if not any([acs_access_group_id is not None, acs_user_id is not None]): raise ValueError( "At least one parameter is required for /acs/users/add_to_access_group" ) @@ -337,14 +337,16 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_system_id is not None, - full_name is not None, - access_schedule is not None, - acs_access_group_ids is not None, - email is not None, - email_address is not None, - phone_number is not None, - user_identity_id is not None, + [ + acs_system_id is not None, + full_name is not None, + access_schedule is not None, + acs_access_group_ids is not None, + email is not None, + email_address is not None, + phone_number is not None, + user_identity_id is not None, + ] ): raise ValueError("At least one parameter is required for /acs/users/create") json_payload: Dict[str, Any] = {} @@ -390,9 +392,11 @@ def delete( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError("At least one parameter is required for /acs/users/delete") params: Dict[str, Any] = {} @@ -430,9 +434,11 @@ def get( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_user_id is not None, - acs_system_id is not None, - user_identity_id is not None, + [ + acs_user_id is not None, + acs_system_id is not None, + user_identity_id is not None, + ] ): raise ValueError("At least one parameter is required for /acs/users/get") params: Dict[str, Any] = {} @@ -529,9 +535,11 @@ def list_accessible_entrances( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/users/list_accessible_entrances" @@ -571,9 +579,11 @@ def remove_from_access_group( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_access_group_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_access_group_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/users/remove_from_access_group" @@ -613,9 +623,11 @@ def revoke_access_to_all_entrances( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/users/revoke_access_to_all_entrances" @@ -653,9 +665,11 @@ def suspend( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/users/suspend" @@ -693,9 +707,11 @@ def unsuspend( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, + [ + acs_system_id is not None, + acs_user_id is not None, + user_identity_id is not None, + ] ): raise ValueError( "At least one parameter is required for /acs/users/unsuspend" @@ -751,15 +767,17 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - access_schedule is not None, - acs_system_id is not None, - acs_user_id is not None, - email is not None, - email_address is not None, - full_name is not None, - hid_acs_system_id is not None, - phone_number is not None, - user_identity_id is not None, + [ + access_schedule is not None, + acs_system_id is not None, + acs_user_id is not None, + email is not None, + email_address is not None, + full_name is not None, + hid_acs_system_id is not None, + phone_number is not None, + user_identity_id is not None, + ] ): raise ValueError("At least one parameter is required for /acs/users/update") json_payload: Dict[str, Any] = {} diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index 4d7bde7d..06ebaac8 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -72,7 +72,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(action_attempt_id is not None): + if not any([action_attempt_id is not None]): raise ValueError( "At least one parameter is required for /action_attempts/get" ) diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index d5037ebf..aeb1cb53 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -234,7 +234,7 @@ def delete(self, *, client_session_id: str) -> None: :param client_session_id: ID of the client session that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(client_session_id is not None): + if not any([client_session_id is not None]): raise ValueError( "At least one parameter is required for /client_sessions/delete" ) @@ -354,12 +354,14 @@ def grant_access( :raises ValueError: At least one parameter must be provided.""" if not any( - client_session_id is not None, - connect_webview_ids is not None, - connected_account_ids is not None, - user_identifier_key is not None, - user_identity_id is not None, - user_identity_ids is not None, + [ + client_session_id is not None, + connect_webview_ids is not None, + connected_account_ids is not None, + user_identifier_key is not None, + user_identity_id is not None, + user_identity_ids is not None, + ] ): raise ValueError( "At least one parameter is required for /client_sessions/grant_access" @@ -440,7 +442,7 @@ def revoke(self, *, client_session_id: str) -> None: :param client_session_id: ID of the client session that you want to revoke. :raises ValueError: At least one parameter must be provided.""" - if not any(client_session_id is not None): + if not any([client_session_id is not None]): raise ValueError( "At least one parameter is required for /client_sessions/revoke" ) diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index e1d975dd..de808af6 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -201,7 +201,7 @@ def delete(self, *, connect_webview_id: str) -> None: :param connect_webview_id: ID of the Connect Webview that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(connect_webview_id is not None): + if not any([connect_webview_id is not None]): raise ValueError( "At least one parameter is required for /connect_webviews/delete" ) @@ -227,7 +227,7 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(connect_webview_id is not None): + if not any([connect_webview_id is not None]): raise ValueError( "At least one parameter is required for /connect_webviews/get" ) diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index e1f15532..4c0c88c3 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -138,7 +138,7 @@ def delete(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(connected_account_id is not None): + if not any([connected_account_id is not None]): raise ValueError( "At least one parameter is required for /connected_accounts/delete" ) @@ -168,7 +168,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(connected_account_id is not None, email is not None): + if not any([connected_account_id is not None, email is not None]): raise ValueError( "At least one parameter is required for /connected_accounts/get" ) @@ -248,7 +248,7 @@ def sync(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account that you want to sync. :raises ValueError: At least one parameter must be provided.""" - if not any(connected_account_id is not None): + if not any([connected_account_id is not None]): raise ValueError( "At least one parameter is required for /connected_accounts/sync" ) @@ -292,12 +292,14 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - connected_account_id is not None, - accepted_capabilities is not None, - automatically_manage_new_devices is not None, - custom_metadata is not None, - customer_key is not None, - display_name is not None, + [ + connected_account_id is not None, + accepted_capabilities is not None, + automatically_manage_new_devices is not None, + custom_metadata is not None, + customer_key is not None, + display_name is not None, + ] ): raise ValueError( "At least one parameter is required for /connected_accounts/update" diff --git a/seam/routes/connected_accounts_simulate.py b/seam/routes/connected_accounts_simulate.py index 47119ee9..f1463af1 100644 --- a/seam/routes/connected_accounts_simulate.py +++ b/seam/routes/connected_accounts_simulate.py @@ -32,7 +32,7 @@ def disconnect(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account you want to simulate as disconnected. :raises ValueError: At least one parameter must be provided.""" - if not any(connected_account_id is not None): + if not any([connected_account_id is not None]): raise ValueError( "At least one parameter is required for /connected_accounts/simulate/disconnect" ) diff --git a/seam/routes/customers.py b/seam/routes/customers.py index 01b857ca..88d34e01 100644 --- a/seam/routes/customers.py +++ b/seam/routes/customers.py @@ -450,26 +450,28 @@ def push_data( :raises ValueError: At least one parameter must be provided.""" if not any( - customer_key is not None, - access_grants is not None, - bookings is not None, - buildings is not None, - common_areas is not None, - facilities is not None, - guests is not None, - listings is not None, - properties is not None, - property_listings is not None, - reservations is not None, - residents is not None, - rooms is not None, - sites is not None, - spaces is not None, - staff_members is not None, - tenants is not None, - units is not None, - user_identities is not None, - users is not None, + [ + customer_key is not None, + access_grants is not None, + bookings is not None, + buildings is not None, + common_areas is not None, + facilities is not None, + guests is not None, + listings is not None, + properties is not None, + property_listings is not None, + reservations is not None, + residents is not None, + rooms is not None, + sites is not None, + spaces is not None, + staff_members is not None, + tenants is not None, + units is not None, + user_identities is not None, + users is not None, + ] ): raise ValueError( "At least one parameter is required for /customers/push_data" diff --git a/seam/routes/devices.py b/seam/routes/devices.py index 9895a304..b0539308 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -181,7 +181,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, name is not None): + if not any([device_id is not None, name is not None]): raise ValueError("At least one parameter is required for /devices/get") params: Dict[str, Any] = {} @@ -328,7 +328,7 @@ def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: :param devices: Array of devices with provider metadata to update :raises ValueError: At least one parameter must be provided.""" - if not any(devices is not None): + if not any([devices is not None]): raise ValueError( "At least one parameter is required for /devices/report_provider_metadata" ) @@ -372,12 +372,14 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - backup_access_code_pool_enabled is not None, - custom_metadata is not None, - is_managed is not None, - name is not None, - properties is not None, + [ + device_id is not None, + backup_access_code_pool_enabled is not None, + custom_metadata is not None, + is_managed is not None, + name is not None, + properties is not None, + ] ): raise ValueError("At least one parameter is required for /devices/update") json_payload: Dict[str, Any] = {} diff --git a/seam/routes/devices_simulate.py b/seam/routes/devices_simulate.py index 574fea0a..c5497e79 100644 --- a/seam/routes/devices_simulate.py +++ b/seam/routes/devices_simulate.py @@ -88,7 +88,7 @@ def connect(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate connecting to Seam. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /devices/simulate/connect" ) @@ -115,7 +115,7 @@ def connect_to_hub(self, *, device_id: str) -> None: :param device_id: ID of the device whose hub you want to reconnect. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /devices/simulate/connect_to_hub" ) @@ -139,7 +139,7 @@ def disconnect(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate disconnecting from Seam. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /devices/simulate/disconnect" ) @@ -167,7 +167,7 @@ def disconnect_from_hub(self, *, device_id: str) -> None: :param device_id: ID of the device whose hub you want to disconnect. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /devices/simulate/disconnect_from_hub" ) @@ -195,7 +195,7 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: :param is_expired: :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, is_expired is not None): + if not any([device_id is not None, is_expired is not None]): raise ValueError( "At least one parameter is required for /devices/simulate/paid_subscription" ) @@ -221,7 +221,7 @@ def remove(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate removing from Seam. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /devices/simulate/remove" ) diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 4014be6c..6980512c 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -122,7 +122,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, name is not None): + if not any([device_id is not None, name is not None]): raise ValueError( "At least one parameter is required for /devices/unmanaged/get" ) @@ -242,7 +242,7 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, custom_metadata is not None, is_managed is not None + [device_id is not None, custom_metadata is not None, is_managed is not None] ): raise ValueError( "At least one parameter is required for /devices/unmanaged/update" diff --git a/seam/routes/events.py b/seam/routes/events.py index a93c063f..234d037e 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -151,7 +151,9 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(event_id is not None, device_id is not None, event_type is not None): + if not any( + [event_id is not None, device_id is not None, event_type is not None] + ): raise ValueError("At least one parameter is required for /events/get") params: Dict[str, Any] = {} @@ -263,34 +265,36 @@ def list( :raises ValueError: At least one parameter must be provided.""" if not any( - access_code_id is not None, - access_code_ids is not None, - access_grant_id is not None, - access_grant_ids is not None, - access_method_id is not None, - access_method_ids is not None, - acs_access_group_id is not None, - acs_credential_id is not None, - acs_encoder_id is not None, - acs_entrance_id is not None, - acs_system_id is not None, - acs_system_ids is not None, - acs_user_id is not None, - between is not None, - connect_webview_id is not None, - connected_account_id is not None, - customer_key is not None, - device_id is not None, - device_ids is not None, - event_ids is not None, - event_type is not None, - event_types is not None, - limit is not None, - since is not None, - space_id is not None, - space_ids is not None, - unstable_offset is not None, - user_identity_id is not None, + [ + access_code_id is not None, + access_code_ids is not None, + access_grant_id is not None, + access_grant_ids is not None, + access_method_id is not None, + access_method_ids is not None, + acs_access_group_id is not None, + acs_credential_id is not None, + acs_encoder_id is not None, + acs_entrance_id is not None, + acs_system_id is not None, + acs_system_ids is not None, + acs_user_id is not None, + between is not None, + connect_webview_id is not None, + connected_account_id is not None, + customer_key is not None, + device_id is not None, + device_ids is not None, + event_ids is not None, + event_type is not None, + event_types is not None, + limit is not None, + since is not None, + space_id is not None, + space_ids is not None, + unstable_offset is not None, + user_identity_id is not None, + ] ): raise ValueError("At least one parameter is required for /events/list") json_payload: Dict[str, Any] = {} diff --git a/seam/routes/instant_keys.py b/seam/routes/instant_keys.py index cbe60e0c..acba857d 100644 --- a/seam/routes/instant_keys.py +++ b/seam/routes/instant_keys.py @@ -58,7 +58,7 @@ def delete(self, *, instant_key_id: str) -> None: :param instant_key_id: ID of the Instant Key that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(instant_key_id is not None): + if not any([instant_key_id is not None]): raise ValueError( "At least one parameter is required for /instant_keys/delete" ) @@ -89,7 +89,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(instant_key_id is not None, instant_key_url is not None): + if not any([instant_key_id is not None, instant_key_url is not None]): raise ValueError("At least one parameter is required for /instant_keys/get") params: Dict[str, Any] = {} diff --git a/seam/routes/locks.py b/seam/routes/locks.py index 3996ec60..8b997e7e 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -158,9 +158,11 @@ def configure_auto_lock( :raises ValueError: At least one parameter must be provided.""" if not any( - auto_lock_enabled is not None, - device_id is not None, - auto_lock_delay_seconds is not None, + [ + auto_lock_enabled is not None, + device_id is not None, + auto_lock_delay_seconds is not None, + ] ): raise ValueError( "At least one parameter is required for /locks/configure_auto_lock" @@ -206,7 +208,7 @@ def get( .. deprecated:: Use ``/devices/get`` instead.""" - if not any(device_id is not None, name is not None): + if not any([device_id is not None, name is not None]): raise ValueError("At least one parameter is required for /locks/get") params: Dict[str, Any] = {} @@ -284,7 +286,7 @@ def lock_door( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError("At least one parameter is required for /locks/lock_door") json_payload: Dict[str, Any] = {} @@ -323,7 +325,7 @@ def unlock_door( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /locks/unlock_door" ) diff --git a/seam/routes/locks_simulate.py b/seam/routes/locks_simulate.py index 038b7b2a..cc02ff36 100644 --- a/seam/routes/locks_simulate.py +++ b/seam/routes/locks_simulate.py @@ -76,7 +76,7 @@ def keypad_code_entry( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(code is not None, device_id is not None): + if not any([code is not None, device_id is not None]): raise ValueError( "At least one parameter is required for /locks/simulate/keypad_code_entry" ) @@ -121,7 +121,7 @@ def manual_lock_via_keypad( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /locks/simulate/manual_lock_via_keypad" ) diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index 1bef9c4a..33324f15 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -140,12 +140,14 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - ends_daily_at is not None, - starts_daily_at is not None, - name is not None, - noise_threshold_decibels is not None, - noise_threshold_nrs is not None, + [ + device_id is not None, + ends_daily_at is not None, + starts_daily_at is not None, + name is not None, + noise_threshold_decibels is not None, + noise_threshold_nrs is not None, + ] ): raise ValueError( "At least one parameter is required for /noise_sensors/noise_thresholds/create" @@ -184,7 +186,7 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: :param noise_threshold_id: ID of the noise threshold that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, noise_threshold_id is not None): + if not any([device_id is not None, noise_threshold_id is not None]): raise ValueError( "At least one parameter is required for /noise_sensors/noise_thresholds/delete" ) @@ -212,7 +214,7 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(noise_threshold_id is not None): + if not any([noise_threshold_id is not None]): raise ValueError( "At least one parameter is required for /noise_sensors/noise_thresholds/get" ) @@ -238,7 +240,7 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /noise_sensors/noise_thresholds/list" ) @@ -285,13 +287,15 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - noise_threshold_id is not None, - ends_daily_at is not None, - name is not None, - noise_threshold_decibels is not None, - noise_threshold_nrs is not None, - starts_daily_at is not None, + [ + device_id is not None, + noise_threshold_id is not None, + ends_daily_at is not None, + name is not None, + noise_threshold_decibels is not None, + noise_threshold_nrs is not None, + starts_daily_at is not None, + ] ): raise ValueError( "At least one parameter is required for /noise_sensors/noise_thresholds/update" diff --git a/seam/routes/noise_sensors_simulate.py b/seam/routes/noise_sensors_simulate.py index a4039ccf..e7517c82 100644 --- a/seam/routes/noise_sensors_simulate.py +++ b/seam/routes/noise_sensors_simulate.py @@ -32,7 +32,7 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: :param device_id: ID of the device for which you want to simulate the triggering of a noise threshold. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold" ) diff --git a/seam/routes/phones.py b/seam/routes/phones.py index 3a72c84a..cf892b85 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -69,7 +69,7 @@ def deactivate(self, *, device_id: str) -> None: :param device_id: Device ID of the phone that you want to deactivate. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError( "At least one parameter is required for /phones/deactivate" ) @@ -93,7 +93,7 @@ def get(self, *, device_id: str) -> Phone: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError("At least one parameter is required for /phones/get") params: Dict[str, Any] = {} diff --git a/seam/routes/phones_simulate.py b/seam/routes/phones_simulate.py index 1fed90d5..a6f36859 100644 --- a/seam/routes/phones_simulate.py +++ b/seam/routes/phones_simulate.py @@ -64,10 +64,12 @@ def create_sandbox_phone( :raises ValueError: At least one parameter must be provided.""" if not any( - user_identity_id is not None, - assa_abloy_metadata is not None, - custom_sdk_installation_id is not None, - phone_metadata is not None, + [ + user_identity_id is not None, + assa_abloy_metadata is not None, + custom_sdk_installation_id is not None, + phone_metadata is not None, + ] ): raise ValueError( "At least one parameter is required for /phones/simulate/create_sandbox_phone" diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index b63fc27b..fdcc2f3e 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -232,7 +232,7 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No :param space_id: ID of the space to which you want to add entrances. :raises ValueError: At least one parameter must be provided.""" - if not any(acs_entrance_ids is not None, space_id is not None): + if not any([acs_entrance_ids is not None, space_id is not None]): raise ValueError( "At least one parameter is required for /spaces/add_acs_entrances" ) @@ -262,7 +262,7 @@ def add_connected_account( :param space_id: ID of the space to which you want to add the connected account. :raises ValueError: At least one parameter must be provided.""" - if not any(connected_account_id is not None, space_id is not None): + if not any([connected_account_id is not None, space_id is not None]): raise ValueError( "At least one parameter is required for /spaces/add_connected_account" ) @@ -288,7 +288,7 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: :param space_id: ID of the space to which you want to add devices. :raises ValueError: At least one parameter must be provided.""" - if not any(device_ids is not None, space_id is not None): + if not any([device_ids is not None, space_id is not None]): raise ValueError( "At least one parameter is required for /spaces/add_devices" ) @@ -337,13 +337,15 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - name is not None, - acs_entrance_ids is not None, - connected_account_ids is not None, - customer_data is not None, - customer_key is not None, - device_ids is not None, - space_key is not None, + [ + name is not None, + acs_entrance_ids is not None, + connected_account_ids is not None, + customer_data is not None, + customer_key is not None, + device_ids is not None, + space_key is not None, + ] ): raise ValueError("At least one parameter is required for /spaces/create") json_payload: Dict[str, Any] = {} @@ -376,7 +378,7 @@ def delete(self, *, space_id: str) -> None: :param space_id: ID of the space that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(space_id is not None): + if not any([space_id is not None]): raise ValueError("At least one parameter is required for /spaces/delete") params: Dict[str, Any] = {} @@ -402,7 +404,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(space_id is not None, space_key is not None): + if not any([space_id is not None, space_key is not None]): raise ValueError("At least one parameter is required for /spaces/get") params: Dict[str, Any] = {} @@ -440,10 +442,12 @@ def get_related( :raises ValueError: At least one parameter must be provided.""" if not any( - exclude is not None, - include is not None, - space_ids is not None, - space_keys is not None, + [ + exclude is not None, + include is not None, + space_ids is not None, + space_keys is not None, + ] ): raise ValueError( "At least one parameter is required for /spaces/get_related" @@ -520,7 +524,7 @@ def remove_acs_entrances( :param space_id: ID of the space from which you want to remove entrances. :raises ValueError: At least one parameter must be provided.""" - if not any(acs_entrance_ids is not None, space_id is not None): + if not any([acs_entrance_ids is not None, space_id is not None]): raise ValueError( "At least one parameter is required for /spaces/remove_acs_entrances" ) @@ -550,7 +554,7 @@ def remove_connected_account( :param space_id: ID of the space from which you want to remove the connected account. :raises ValueError: At least one parameter must be provided.""" - if not any(connected_account_id is not None, space_id is not None): + if not any([connected_account_id is not None, space_id is not None]): raise ValueError( "At least one parameter is required for /spaces/remove_connected_account" ) @@ -578,7 +582,7 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: :param space_id: ID of the space from which you want to remove devices. :raises ValueError: At least one parameter must be provided.""" - if not any(device_ids is not None, space_id is not None): + if not any([device_ids is not None, space_id is not None]): raise ValueError( "At least one parameter is required for /spaces/remove_devices" ) diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index 953fb587..0428721b 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -457,7 +457,7 @@ def activate_climate_preset( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(climate_preset_key is not None, device_id is not None): + if not any([climate_preset_key is not None, device_id is not None]): raise ValueError( "At least one parameter is required for /thermostats/activate_climate_preset" ) @@ -509,9 +509,11 @@ def cool( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, + [ + device_id is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + ] ): raise ValueError("At least one parameter is required for /thermostats/cool") json_payload: Dict[str, Any] = {} @@ -586,18 +588,20 @@ def create_climate_preset( :raises ValueError: At least one parameter must be provided.""" if not any( - climate_preset_key is not None, - device_id is not None, - climate_preset_mode is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - ecobee_metadata is not None, - fan_mode_setting is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - hvac_mode_setting is not None, - manual_override_allowed is not None, - name is not None, + [ + climate_preset_key is not None, + device_id is not None, + climate_preset_mode is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + ecobee_metadata is not None, + fan_mode_setting is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + hvac_mode_setting is not None, + manual_override_allowed is not None, + name is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/create_climate_preset" @@ -646,7 +650,7 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N :param device_id: ID of the thermostat device for which you want to delete a climate preset. :raises ValueError: At least one parameter must be provided.""" - if not any(climate_preset_key is not None, device_id is not None): + if not any([climate_preset_key is not None, device_id is not None]): raise ValueError( "At least one parameter is required for /thermostats/delete_climate_preset" ) @@ -686,9 +690,11 @@ def heat( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, + [ + device_id is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ] ): raise ValueError("At least one parameter is required for /thermostats/heat") json_payload: Dict[str, Any] = {} @@ -747,11 +753,13 @@ def heat_cool( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, + [ + device_id is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/heat_cool" @@ -848,7 +856,7 @@ def off( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None): + if not any([device_id is not None]): raise ValueError("At least one parameter is required for /thermostats/off") json_payload: Dict[str, Any] = {} @@ -884,7 +892,7 @@ def set_fallback_climate_preset( :param device_id: ID of the thermostat device for which you want to set the fallback climate preset. :raises ValueError: At least one parameter must be provided.""" - if not any(climate_preset_key is not None, device_id is not None): + if not any([climate_preset_key is not None, device_id is not None]): raise ValueError( "At least one parameter is required for /thermostats/set_fallback_climate_preset" ) @@ -926,7 +934,7 @@ def set_fan_mode( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, fan_mode is not None, fan_mode_setting is not None + [device_id is not None, fan_mode is not None, fan_mode_setting is not None] ): raise ValueError( "At least one parameter is required for /thermostats/set_fan_mode" @@ -990,12 +998,14 @@ def set_hvac_mode( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - hvac_mode_setting is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, + [ + device_id is not None, + hvac_mode_setting is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/set_hvac_mode" @@ -1057,11 +1067,13 @@ def set_temperature_threshold( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - lower_limit_celsius is not None, - lower_limit_fahrenheit is not None, - upper_limit_celsius is not None, - upper_limit_fahrenheit is not None, + [ + device_id is not None, + lower_limit_celsius is not None, + lower_limit_fahrenheit is not None, + upper_limit_celsius is not None, + upper_limit_fahrenheit is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/set_temperature_threshold" @@ -1132,18 +1144,20 @@ def update_climate_preset( :raises ValueError: At least one parameter must be provided.""" if not any( - climate_preset_key is not None, - device_id is not None, - climate_preset_mode is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - ecobee_metadata is not None, - fan_mode_setting is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - hvac_mode_setting is not None, - manual_override_allowed is not None, - name is not None, + [ + climate_preset_key is not None, + device_id is not None, + climate_preset_mode is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + ecobee_metadata is not None, + fan_mode_setting is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + hvac_mode_setting is not None, + manual_override_allowed is not None, + name is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/update_climate_preset" @@ -1221,14 +1235,16 @@ def update_weekly_program( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - friday_program_id is not None, - monday_program_id is not None, - saturday_program_id is not None, - sunday_program_id is not None, - thursday_program_id is not None, - tuesday_program_id is not None, - wednesday_program_id is not None, + [ + device_id is not None, + friday_program_id is not None, + monday_program_id is not None, + saturday_program_id is not None, + sunday_program_id is not None, + thursday_program_id is not None, + tuesday_program_id is not None, + wednesday_program_id is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/update_weekly_program" diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index 0b09be91..9d19d1f7 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -83,7 +83,7 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, name is not None, periods is not None): + if not any([device_id is not None, name is not None, periods is not None]): raise ValueError( "At least one parameter is required for /thermostats/daily_programs/create" ) @@ -111,7 +111,7 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: :param thermostat_daily_program_id: ID of the thermostat daily program that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(thermostat_daily_program_id is not None): + if not any([thermostat_daily_program_id is not None]): raise ValueError( "At least one parameter is required for /thermostats/daily_programs/delete" ) @@ -151,9 +151,11 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - name is not None, - periods is not None, - thermostat_daily_program_id is not None, + [ + name is not None, + periods is not None, + thermostat_daily_program_id is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/daily_programs/update" diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index c05e503c..642b0974 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -148,13 +148,15 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - climate_preset_key is not None, - device_id is not None, - ends_at is not None, - starts_at is not None, - is_override_allowed is not None, - max_override_period_minutes is not None, - name is not None, + [ + climate_preset_key is not None, + device_id is not None, + ends_at is not None, + starts_at is not None, + is_override_allowed is not None, + max_override_period_minutes is not None, + name is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/schedules/create" @@ -191,7 +193,7 @@ def delete(self, *, thermostat_schedule_id: str) -> None: :param thermostat_schedule_id: ID of the thermostat schedule that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(thermostat_schedule_id is not None): + if not any([thermostat_schedule_id is not None]): raise ValueError( "At least one parameter is required for /thermostats/schedules/delete" ) @@ -217,7 +219,7 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(thermostat_schedule_id is not None): + if not any([thermostat_schedule_id is not None]): raise ValueError( "At least one parameter is required for /thermostats/schedules/get" ) @@ -247,7 +249,7 @@ def list( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, user_identifier_key is not None): + if not any([device_id is not None, user_identifier_key is not None]): raise ValueError( "At least one parameter is required for /thermostats/schedules/list" ) @@ -298,13 +300,15 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - thermostat_schedule_id is not None, - climate_preset_key is not None, - ends_at is not None, - is_override_allowed is not None, - max_override_period_minutes is not None, - name is not None, - starts_at is not None, + [ + thermostat_schedule_id is not None, + climate_preset_key is not None, + ends_at is not None, + is_override_allowed is not None, + max_override_period_minutes is not None, + name is not None, + starts_at is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/schedules/update" diff --git a/seam/routes/thermostats_simulate.py b/seam/routes/thermostats_simulate.py index 4989a7ce..07a9749a 100644 --- a/seam/routes/thermostats_simulate.py +++ b/seam/routes/thermostats_simulate.py @@ -90,12 +90,14 @@ def hvac_mode_adjusted( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - hvac_mode is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, + [ + device_id is not None, + hvac_mode is not None, + cooling_set_point_celsius is not None, + cooling_set_point_fahrenheit is not None, + heating_set_point_celsius is not None, + heating_set_point_fahrenheit is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted" @@ -141,9 +143,11 @@ def temperature_reached( :raises ValueError: At least one parameter must be provided.""" if not any( - device_id is not None, - temperature_celsius is not None, - temperature_fahrenheit is not None, + [ + device_id is not None, + temperature_celsius is not None, + temperature_fahrenheit is not None, + ] ): raise ValueError( "At least one parameter is required for /thermostats/simulate/temperature_reached" diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index c2ccaa4c..3f51725b 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -286,9 +286,11 @@ def add_acs_user( :raises ValueError: At least one parameter must be provided.""" if not any( - acs_user_id is not None, - user_identity_id is not None, - user_identity_key is not None, + [ + acs_user_id is not None, + user_identity_id is not None, + user_identity_key is not None, + ] ): raise ValueError( "At least one parameter is required for /user_identities/add_acs_user" @@ -361,7 +363,7 @@ def delete(self, *, user_identity_id: str) -> None: :param user_identity_id: ID of the user identity that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(user_identity_id is not None): + if not any([user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/delete" ) @@ -398,9 +400,11 @@ def generate_instant_key( :raises ValueError: At least one parameter must be provided.""" if not any( - user_identity_id is not None, - customization_profile_id is not None, - max_use_count is not None, + [ + user_identity_id is not None, + customization_profile_id is not None, + max_use_count is not None, + ] ): raise ValueError( "At least one parameter is required for /user_identities/generate_instant_key" @@ -438,7 +442,7 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(user_identity_id is not None, user_identity_key is not None): + if not any([user_identity_id is not None, user_identity_key is not None]): raise ValueError( "At least one parameter is required for /user_identities/get" ) @@ -466,7 +470,7 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No :param user_identity_id: ID of the user identity that you want to grant access to a device. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, user_identity_id is not None): + if not any([device_id is not None, user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/grant_access_to_device" ) @@ -543,7 +547,7 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(user_identity_id is not None): + if not any([user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/list_accessible_devices" ) @@ -569,7 +573,7 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(user_identity_id is not None): + if not any([user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/list_accessible_entrances" ) @@ -597,7 +601,7 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(user_identity_id is not None): + if not any([user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/list_acs_systems" ) @@ -623,7 +627,7 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(user_identity_id is not None): + if not any([user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/list_acs_users" ) @@ -649,7 +653,7 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: :param user_identity_id: ID of the user identity from which you want to remove an access system user. :raises ValueError: At least one parameter must be provided.""" - if not any(acs_user_id is not None, user_identity_id is not None): + if not any([acs_user_id is not None, user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/remove_acs_user" ) @@ -677,7 +681,7 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N :param user_identity_id: ID of the user identity from which you want to revoke access to a device. :raises ValueError: At least one parameter must be provided.""" - if not any(device_id is not None, user_identity_id is not None): + if not any([device_id is not None, user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/revoke_access_to_device" ) @@ -720,11 +724,13 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - user_identity_id is not None, - email_address is not None, - full_name is not None, - phone_number is not None, - user_identity_key is not None, + [ + user_identity_id is not None, + email_address is not None, + full_name is not None, + phone_number is not None, + user_identity_key is not None, + ] ): raise ValueError( "At least one parameter is required for /user_identities/update" diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index 9f8c8aad..6efe646c 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -80,7 +80,7 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(user_identity_id is not None): + if not any([user_identity_id is not None]): raise ValueError( "At least one parameter is required for /user_identities/unmanaged/get" ) @@ -158,9 +158,11 @@ def update( :raises ValueError: At least one parameter must be provided.""" if not any( - is_managed is not None, - user_identity_id is not None, - user_identity_key is not None, + [ + is_managed is not None, + user_identity_id is not None, + user_identity_key is not None, + ] ): raise ValueError( "At least one parameter is required for /user_identities/unmanaged/update" diff --git a/seam/routes/webhooks.py b/seam/routes/webhooks.py index 33674c96..a0429242 100644 --- a/seam/routes/webhooks.py +++ b/seam/routes/webhooks.py @@ -77,7 +77,7 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(url is not None, event_types is not None): + if not any([url is not None, event_types is not None]): raise ValueError("At least one parameter is required for /webhooks/create") json_payload: Dict[str, Any] = {} @@ -99,7 +99,7 @@ def delete(self, *, webhook_id: str) -> None: :param webhook_id: ID of the webhook that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any(webhook_id is not None): + if not any([webhook_id is not None]): raise ValueError("At least one parameter is required for /webhooks/delete") params: Dict[str, Any] = {} @@ -121,7 +121,7 @@ def get(self, *, webhook_id: str) -> Webhook: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any(webhook_id is not None): + if not any([webhook_id is not None]): raise ValueError("At least one parameter is required for /webhooks/get") params: Dict[str, Any] = {} @@ -156,7 +156,7 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: :param webhook_id: ID of the webhook that you want to update. :raises ValueError: At least one parameter must be provided.""" - if not any(event_types is not None, webhook_id is not None): + if not any([event_types is not None, webhook_id is not None]): raise ValueError("At least one parameter is required for /webhooks/update") json_payload: Dict[str, Any] = {} diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index 26f3529d..b48bfc91 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -151,16 +151,18 @@ def create( :raises ValueError: At least one parameter must be provided.""" if not any( - name is not None, - company_name is not None, - connect_partner_name is not None, - connect_webview_customization is not None, - is_sandbox is not None, - organization_id is not None, - webview_logo_shape is not None, - webview_primary_button_color is not None, - webview_primary_button_text_color is not None, - webview_success_message is not None, + [ + name is not None, + company_name is not None, + connect_partner_name is not None, + connect_webview_customization is not None, + is_sandbox is not None, + organization_id is not None, + webview_logo_shape is not None, + webview_primary_button_color is not None, + webview_primary_button_text_color is not None, + webview_success_message is not None, + ] ): raise ValueError( "At least one parameter is required for /workspaces/create" diff --git a/test/headers_test.py b/test/headers_test.py index 7ee6ea06..dfb040e0 100644 --- a/test/headers_test.py +++ b/test/headers_test.py @@ -17,8 +17,8 @@ def test_seam_sends_default_headers(recording_server): assert len(requests) == 1 [request] = requests - assert request["path"] == "/devices/get" - assert request["body"] == {"device_id": device_id} + assert request["path"] == f"/devices/get?device_id={device_id}" + assert request["body"] is None assert request["headers"]["seam-sdk-name"] == "seamapi/python" assert request["headers"]["seam-sdk-version"] == version("seam") diff --git a/test/http_error_test.py b/test/http_error_test.py index 4fc1ac46..dd30329b 100644 --- a/test/http_error_test.py +++ b/test/http_error_test.py @@ -40,7 +40,7 @@ def test_seam_http_throws_invalid_input_error(server): seam = Seam(api_key=seed["seam_apikey1_token"], endpoint=endpoint) with pytest.raises(SeamHttpInvalidInputError) as exc_info: - seam.devices.get(device_id=4242) + seam.devices.list(device_ids=4242) err = exc_info.value assert err.status_code == 400 assert err.code == "invalid_input" From 3dc6fd66598c1cd59c13f1ec4f6c6c888c73ce92 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:25:26 +0000 Subject: [PATCH 26/39] refactor: Check the built payload for required parameters The guard restated every parameter, which ran to seventeen lines on the widest routes. The payload gains a key exactly when its parameter is not None, so checking it after it is built says the same thing in one line and drops 446 lines from the generated routes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01EFYHA1UHvARfcMBkUnWgGs --- codegen/layouts/partials/route-method.hbs | 9 +- seam/routes/access_codes.py | 163 +++++---------- seam/routes/access_codes_simulate.py | 9 +- seam/routes/access_codes_unmanaged.py | 70 +++---- seam/routes/access_grants.py | 87 +++----- seam/routes/access_grants_unmanaged.py | 24 +-- seam/routes/access_methods.py | 82 ++++---- seam/routes/access_methods_unmanaged.py | 25 +-- seam/routes/acs_access_groups.py | 66 +++---- seam/routes/acs_credentials.py | 93 ++++----- seam/routes/acs_encoders.py | 45 ++--- seam/routes/acs_encoders_simulate.py | 54 ++--- seam/routes/acs_entrances.py | 42 ++-- seam/routes/acs_systems.py | 29 ++- seam/routes/acs_users.py | 139 ++++--------- seam/routes/action_attempts.py | 9 +- seam/routes/client_sessions.py | 36 ++-- seam/routes/connect_webviews.py | 18 +- seam/routes/connected_accounts.py | 45 ++--- seam/routes/connected_accounts_simulate.py | 9 +- seam/routes/customers.py | 32 +-- seam/routes/devices.py | 28 ++- seam/routes/devices_simulate.py | 54 ++--- seam/routes/devices_unmanaged.py | 20 +- seam/routes/events.py | 43 +--- seam/routes/instant_keys.py | 14 +- seam/routes/locks.py | 34 ++-- seam/routes/locks_simulate.py | 18 +- seam/routes/noise_sensors_noise_thresholds.py | 64 +++--- seam/routes/noise_sensors_simulate.py | 9 +- seam/routes/phones.py | 14 +- seam/routes/phones_simulate.py | 16 +- seam/routes/spaces.py | 95 +++++---- seam/routes/thermostats.py | 185 ++++++------------ seam/routes/thermostats_daily_programs.py | 33 ++-- seam/routes/thermostats_schedules.py | 65 +++--- seam/routes/thermostats_simulate.py | 33 +--- seam/routes/user_identities.py | 128 ++++++------ seam/routes/user_identities_unmanaged.py | 24 +-- seam/routes/webhooks.py | 20 +- seam/routes/workspaces.py | 22 +-- 41 files changed, 780 insertions(+), 1225 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 5016c716..b615e7f8 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,16 +1,17 @@ @route_metadata(path="{{path}}", has_required_parameters={{#if hasRequiredParameters}}True{{else}}False{{/if}}, has_pagination={{#if hasPagination}}True{{else}}False{{/if}}) def {{> method-signature}}: """{{> method-docstring}}""" -{{#if hasRequiredParameters}} - if not any([{{#each params}}{{name}} is not None{{#unless @last}}, {{/unless}}{{/each}}]): - raise ValueError("At least one parameter is required for {{path}}") -{{/if}} {{payloadVar}}: Dict[str, Any] = {} {{#each params}} if {{name}} is not None: {{../payloadVar}}["{{name}}"] = {{name}} {{/each}} +{{#if hasRequiredParameters}} + + if not {{payloadVar}}: + raise ValueError("At least one parameter is required for {{path}}") +{{/if}} {{#unless (eq returnType "None")}}res = {{/unless}}self.client.{{httpVerb}}("{{path}}", {{payloadArg}}={{payloadVar}}) {{#if (eq returnType "ActionAttempt")}} diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index 12fb6fc4..82231033 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -446,29 +446,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - allow_external_modification is not None, - attempt_for_offline_device is not None, - code is not None, - common_code_key is not None, - ends_at is not None, - is_external_modification_allowed is not None, - is_offline_access_code is not None, - is_one_time_use is not None, - max_time_rounding is not None, - name is not None, - prefer_native_scheduling is not None, - preferred_code_length is not None, - starts_at is not None, - use_backup_access_code_pool is not None, - use_offline_access_code is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/create" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -506,6 +483,11 @@ def create( if use_offline_access_code is not None: json_payload["use_offline_access_code"] = use_offline_access_code + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/create" + ) + res = self.client.post("/access_codes/create", json=json_payload) return AccessCode.from_dict(res["access_code"]) @@ -576,25 +558,6 @@ def create_multiple( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_ids is not None, - allow_external_modification is not None, - attempt_for_offline_device is not None, - behavior_when_code_cannot_be_shared is not None, - code is not None, - ends_at is not None, - is_external_modification_allowed is not None, - name is not None, - prefer_native_scheduling is not None, - preferred_code_length is not None, - starts_at is not None, - use_backup_access_code_pool is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/create_multiple" - ) json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -626,6 +589,11 @@ def create_multiple( if use_backup_access_code_pool is not None: json_payload["use_backup_access_code_pool"] = use_backup_access_code_pool + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/create_multiple" + ) + res = self.client.put("/access_codes/create_multiple", json=json_payload) return [AccessCode.from_dict(item) for item in res["access_codes"]] @@ -641,10 +609,6 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non :param device_id: ID of the device for which you want to delete the access code. :raises ValueError: At least one parameter must be provided.""" - if not any([access_code_id is not None, device_id is not None]): - raise ValueError( - "At least one parameter is required for /access_codes/delete" - ) params: Dict[str, Any] = {} if access_code_id is not None: @@ -652,6 +616,11 @@ def delete(self, *, access_code_id: str, device_id: Optional[str] = None) -> Non if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError( + "At least one parameter is required for /access_codes/delete" + ) + self.client.delete("/access_codes/delete", params=params) return None @@ -669,15 +638,16 @@ def generate_code(self, *, device_id: str) -> AccessCode: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /access_codes/generate_code" - ) params: Dict[str, Any] = {} if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError( + "At least one parameter is required for /access_codes/generate_code" + ) + res = self.client.get("/access_codes/generate_code", params=params) return AccessCode.from_dict(res["generated_code"]) @@ -705,10 +675,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [access_code_id is not None, code is not None, device_id is not None] - ): - raise ValueError("At least one parameter is required for /access_codes/get") params: Dict[str, Any] = {} if access_code_id is not None: @@ -718,6 +684,9 @@ def get( if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError("At least one parameter is required for /access_codes/get") + res = self.client.get("/access_codes/get", params=params) return AccessCode.from_dict(res["access_code"]) @@ -766,23 +735,6 @@ def list( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_code_ids is not None, - access_grant_id is not None, - access_grant_key is not None, - access_method_id is not None, - customer_key is not None, - device_id is not None, - limit is not None, - page_cursor is not None, - search is not None, - user_identifier_key is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/list" - ) json_payload: Dict[str, Any] = {} if access_code_ids is not None: @@ -806,6 +758,11 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/list" + ) + res = self.client.post("/access_codes/list", json=json_payload) return [AccessCode.from_dict(item) for item in res["access_codes"]] @@ -831,15 +788,16 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_code_id is not None]): - raise ValueError( - "At least one parameter is required for /access_codes/pull_backup_access_code" - ) json_payload: Dict[str, Any] = {} if access_code_id is not None: json_payload["access_code_id"] = access_code_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/pull_backup_access_code" + ) + res = self.client.post( "/access_codes/pull_backup_access_code", json=json_payload ) @@ -872,17 +830,6 @@ def report_device_constraints( :param supported_code_lengths: Array of supported code lengths as integers between 4 and 20, inclusive. You can specify either ``supported_code_lengths`` or ``min_code_length``/``max_code_length``. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - max_code_length is not None, - min_code_length is not None, - supported_code_lengths is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/report_device_constraints" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -894,6 +841,11 @@ def report_device_constraints( if supported_code_lengths is not None: json_payload["supported_code_lengths"] = supported_code_lengths + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/report_device_constraints" + ) + self.client.post("/access_codes/report_device_constraints", json=json_payload) return None @@ -949,24 +901,6 @@ def update( :param type: Type to which you want to convert the access code. To convert a time-bound access code to an ongoing access code, set ``type`` to ``ongoing``. See also `Changing a time-bound access code to permanent access `_. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_code_id is not None, - allow_external_modification is not None, - attempt_for_offline_device is not None, - code is not None, - device_id is not None, - ends_at is not None, - is_external_modification_allowed is not None, - is_managed is not None, - name is not None, - starts_at is not None, - type is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/update" - ) json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -994,6 +928,11 @@ def update( if type is not None: json_payload["type"] = type + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/update" + ) + self.client.put("/access_codes/update", json=json_payload) return None @@ -1032,17 +971,6 @@ def update_multiple( :param starts_at: Date and time at which the validity of the new access code starts, in `ISO 8601 `_ format. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - common_code_key is not None, - ends_at is not None, - name is not None, - starts_at is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/update_multiple" - ) json_payload: Dict[str, Any] = {} if common_code_key is not None: @@ -1054,6 +982,11 @@ def update_multiple( if starts_at is not None: json_payload["starts_at"] = starts_at + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/update_multiple" + ) + self.client.patch("/access_codes/update_multiple", json=json_payload) return None diff --git a/seam/routes/access_codes_simulate.py b/seam/routes/access_codes_simulate.py index 899049f3..4c4c756c 100644 --- a/seam/routes/access_codes_simulate.py +++ b/seam/routes/access_codes_simulate.py @@ -49,10 +49,6 @@ def create_unmanaged_access_code( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([code is not None, device_id is not None, name is not None]): - raise ValueError( - "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code" - ) json_payload: Dict[str, Any] = {} if code is not None: @@ -62,6 +58,11 @@ def create_unmanaged_access_code( if name is not None: json_payload["name"] = name + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code" + ) + res = self.client.post( "/access_codes/simulate/create_unmanaged_access_code", json=json_payload ) diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index 988ec2c0..d8f35bc6 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -151,17 +151,6 @@ def convert_to_managed( :param is_external_modification_allowed: Indicates whether `external modification `_ of the access code is allowed. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_code_id is not None, - allow_external_modification is not None, - force is not None, - is_external_modification_allowed is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/unmanaged/convert_to_managed" - ) json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -175,6 +164,11 @@ def convert_to_managed( is_external_modification_allowed ) + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/convert_to_managed" + ) + self.client.patch( "/access_codes/unmanaged/convert_to_managed", json=json_payload ) @@ -192,15 +186,16 @@ def delete(self, *, access_code_id: str) -> None: :param access_code_id: ID of the unmanaged access code that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([access_code_id is not None]): - raise ValueError( - "At least one parameter is required for /access_codes/unmanaged/delete" - ) params: Dict[str, Any] = {} if access_code_id is not None: params["access_code_id"] = access_code_id + if not params: + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/delete" + ) + self.client.delete("/access_codes/unmanaged/delete", params=params) return None @@ -230,12 +225,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [access_code_id is not None, code is not None, device_id is not None] - ): - raise ValueError( - "At least one parameter is required for /access_codes/unmanaged/get" - ) params: Dict[str, Any] = {} if access_code_id is not None: @@ -245,6 +234,11 @@ def get( if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/get" + ) + res = self.client.get("/access_codes/unmanaged/get", params=params) return UnmanagedAccessCode.from_dict(res["access_code"]) @@ -278,18 +272,6 @@ def list( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - limit is not None, - page_cursor is not None, - search is not None, - user_identifier_key is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/unmanaged/list" - ) params: Dict[str, Any] = {} if device_id is not None: @@ -303,6 +285,11 @@ def list( if user_identifier_key is not None: params["user_identifier_key"] = user_identifier_key + if not params: + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/list" + ) + res = self.client.get("/access_codes/unmanaged/list", params=params) return [UnmanagedAccessCode.from_dict(item) for item in res["access_codes"]] @@ -334,18 +321,6 @@ def update( :param is_external_modification_allowed: Indicates whether `external modification `_ of the code is allowed. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_code_id is not None, - is_managed is not None, - allow_external_modification is not None, - force is not None, - is_external_modification_allowed is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_codes/unmanaged/update" - ) json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -361,6 +336,11 @@ def update( is_external_modification_allowed ) + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_codes/unmanaged/update" + ) + self.client.patch("/access_codes/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index 864f3556..dfad2ccd 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -281,28 +281,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - requested_access_methods is not None, - user_identity_id is not None, - user_identity is not None, - access_grant_key is not None, - acs_entrance_ids is not None, - customization_profile_id is not None, - device_ids is not None, - ends_at is not None, - location is not None, - location_ids is not None, - name is not None, - reservation_key is not None, - space_ids is not None, - space_keys is not None, - starts_at is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_grants/create" - ) json_payload: Dict[str, Any] = {} if requested_access_methods is not None: @@ -336,6 +314,11 @@ def create( if starts_at is not None: json_payload["starts_at"] = starts_at + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_grants/create" + ) + res = self.client.post("/access_grants/create", json=json_payload) return AccessGrant.from_dict(res["access_grant"]) @@ -349,15 +332,16 @@ def delete(self, *, access_grant_id: str) -> None: :param access_grant_id: ID of Access Grant to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([access_grant_id is not None]): - raise ValueError( - "At least one parameter is required for /access_grants/delete" - ) params: Dict[str, Any] = {} if access_grant_id is not None: params["access_grant_id"] = access_grant_id + if not params: + raise ValueError( + "At least one parameter is required for /access_grants/delete" + ) + self.client.delete("/access_grants/delete", params=params) return None @@ -380,10 +364,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_grant_id is not None, access_grant_key is not None]): - raise ValueError( - "At least one parameter is required for /access_grants/get" - ) params: Dict[str, Any] = {} if access_grant_id is not None: @@ -391,6 +371,11 @@ def get( if access_grant_key is not None: params["access_grant_key"] = access_grant_key + if not params: + raise ValueError( + "At least one parameter is required for /access_grants/get" + ) + res = self.client.get("/access_grants/get", params=params) return AccessGrant.from_dict(res["access_grant"]) @@ -421,17 +406,6 @@ def get_related( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_grant_ids is not None, - access_grant_keys is not None, - exclude is not None, - include is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_grants/get_related" - ) json_payload: Dict[str, Any] = {} if access_grant_ids is not None: @@ -443,6 +417,11 @@ def get_related( if include is not None: json_payload["include"] = include + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_grants/get_related" + ) + res = self.client.post("/access_grants/get_related", json=json_payload) return Batch.from_dict(res["batch"]) @@ -546,10 +525,6 @@ def request_access_methods( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_grant_id is not None, requested_access_methods is not None]): - raise ValueError( - "At least one parameter is required for /access_grants/request_access_methods" - ) json_payload: Dict[str, Any] = {} if access_grant_id is not None: @@ -557,6 +532,11 @@ def request_access_methods( if requested_access_methods is not None: json_payload["requested_access_methods"] = requested_access_methods + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_grants/request_access_methods" + ) + res = self.client.post( "/access_grants/request_access_methods", json=json_payload ) @@ -588,18 +568,6 @@ def update( :param starts_at: Date and time at which the validity of the grant starts, in `ISO 8601 `_ format. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_grant_id is not None, - access_grant_key is not None, - ends_at is not None, - name is not None, - starts_at is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_grants/update" - ) json_payload: Dict[str, Any] = {} if access_grant_id is not None: @@ -613,6 +581,11 @@ def update( if starts_at is not None: json_payload["starts_at"] = starts_at + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_grants/update" + ) + self.client.patch("/access_grants/update", json=json_payload) return None diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index 841a23ee..78eb8918 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -88,15 +88,16 @@ def get(self, *, access_grant_id: str) -> UnmanagedAccessGrant: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_grant_id is not None]): - raise ValueError( - "At least one parameter is required for /access_grants/unmanaged/get" - ) params: Dict[str, Any] = {} if access_grant_id is not None: params["access_grant_id"] = access_grant_id + if not params: + raise ValueError( + "At least one parameter is required for /access_grants/unmanaged/get" + ) + res = self.client.get("/access_grants/unmanaged/get", params=params) return UnmanagedAccessGrant.from_dict(res["access_grant"]) @@ -175,16 +176,6 @@ def update( :param access_grant_key: Unique key for the access grant. If not provided, the existing key will be preserved. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_grant_id is not None, - is_managed is not None, - access_grant_key is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_grants/unmanaged/update" - ) json_payload: Dict[str, Any] = {} if access_grant_id is not None: @@ -194,6 +185,11 @@ def update( if access_grant_key is not None: json_payload["access_grant_key"] = access_grant_key + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_grants/unmanaged/update" + ) + self.client.patch("/access_grants/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index d11cc4d9..8c9ca9ee 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -201,10 +201,6 @@ def assign_card( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_method_id is not None, card_number is not None]): - raise ValueError( - "At least one parameter is required for /access_methods/assign_card" - ) json_payload: Dict[str, Any] = {} if access_method_id is not None: @@ -212,6 +208,11 @@ def assign_card( if card_number is not None: json_payload["card_number"] = card_number + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_methods/assign_card" + ) + res = self.client.post("/access_methods/assign_card", json=json_payload) wait_for_action_attempt = ( @@ -247,16 +248,6 @@ def delete( :param reservation_key: Reservation key of the access grant whose access methods should be deleted. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_method_id is not None, - access_grant_id is not None, - reservation_key is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_methods/delete" - ) params: Dict[str, Any] = {} if access_method_id is not None: @@ -266,6 +257,11 @@ def delete( if reservation_key is not None: params["reservation_key"] = reservation_key + if not params: + raise ValueError( + "At least one parameter is required for /access_methods/delete" + ) + self.client.delete("/access_methods/delete", params=params) return None @@ -293,10 +289,6 @@ def encode( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_method_id is not None, acs_encoder_id is not None]): - raise ValueError( - "At least one parameter is required for /access_methods/encode" - ) json_payload: Dict[str, Any] = {} if access_method_id is not None: @@ -304,6 +296,11 @@ def encode( if acs_encoder_id is not None: json_payload["acs_encoder_id"] = acs_encoder_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_methods/encode" + ) + res = self.client.post("/access_methods/encode", json=json_payload) wait_for_action_attempt = ( @@ -329,15 +326,16 @@ def get(self, *, access_method_id: str) -> AccessMethod: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_method_id is not None]): - raise ValueError( - "At least one parameter is required for /access_methods/get" - ) params: Dict[str, Any] = {} if access_method_id is not None: params["access_method_id"] = access_method_id + if not params: + raise ValueError( + "At least one parameter is required for /access_methods/get" + ) + res = self.client.get("/access_methods/get", params=params) return AccessMethod.from_dict(res["access_method"]) @@ -365,12 +363,6 @@ def get_related( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [access_method_ids is not None, exclude is not None, include is not None] - ): - raise ValueError( - "At least one parameter is required for /access_methods/get_related" - ) json_payload: Dict[str, Any] = {} if access_method_ids is not None: @@ -380,6 +372,11 @@ def get_related( if include is not None: json_payload["include"] = include + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_methods/get_related" + ) + res = self.client.post("/access_methods/get_related", json=json_payload) return Batch.from_dict(res["batch"]) @@ -420,21 +417,6 @@ def list( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_code_id is not None, - access_grant_id is not None, - access_grant_key is not None, - acs_entrance_id is not None, - device_id is not None, - limit is not None, - page_cursor is not None, - space_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_methods/list" - ) params: Dict[str, Any] = {} if access_code_id is not None: @@ -454,6 +436,11 @@ def list( if space_id is not None: params["space_id"] = space_id + if not params: + raise ValueError( + "At least one parameter is required for /access_methods/list" + ) + res = self.client.get("/access_methods/list", params=params) return [AccessMethod.from_dict(item) for item in res["access_methods"]] @@ -481,10 +468,6 @@ def unlock_door( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_method_id is not None, acs_entrance_id is not None]): - raise ValueError( - "At least one parameter is required for /access_methods/unlock_door" - ) json_payload: Dict[str, Any] = {} if access_method_id is not None: @@ -492,6 +475,11 @@ def unlock_door( if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /access_methods/unlock_door" + ) + res = self.client.post("/access_methods/unlock_door", json=json_payload) wait_for_action_attempt = ( diff --git a/seam/routes/access_methods_unmanaged.py b/seam/routes/access_methods_unmanaged.py index 34436af1..fd7cd14a 100644 --- a/seam/routes/access_methods_unmanaged.py +++ b/seam/routes/access_methods_unmanaged.py @@ -61,15 +61,16 @@ def get(self, *, access_method_id: str) -> UnmanagedAccessMethod: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([access_method_id is not None]): - raise ValueError( - "At least one parameter is required for /access_methods/unmanaged/get" - ) params: Dict[str, Any] = {} if access_method_id is not None: params["access_method_id"] = access_method_id + if not params: + raise ValueError( + "At least one parameter is required for /access_methods/unmanaged/get" + ) + res = self.client.get("/access_methods/unmanaged/get", params=params) return UnmanagedAccessMethod.from_dict(res["access_method"]) @@ -100,17 +101,6 @@ def list( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_grant_id is not None, - acs_entrance_id is not None, - device_id is not None, - space_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /access_methods/unmanaged/list" - ) params: Dict[str, Any] = {} if access_grant_id is not None: @@ -122,6 +112,11 @@ def list( if space_id is not None: params["space_id"] = space_id + if not params: + raise ValueError( + "At least one parameter is required for /access_methods/unmanaged/list" + ) + res = self.client.get("/access_methods/unmanaged/list", params=params) return [UnmanagedAccessMethod.from_dict(item) for item in res["access_methods"]] diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index 7a0d93f9..6b84487e 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -138,16 +138,6 @@ def add_user( :param user_identity_id: ID of the desired user identity that you want to add to an access group. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_access_group_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/access_groups/add_user" - ) json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -157,6 +147,11 @@ def add_user( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/access_groups/add_user" + ) + self.client.put("/acs/access_groups/add_user", json=json_payload) return None @@ -172,15 +167,16 @@ def delete(self, *, acs_access_group_id: str) -> None: :param acs_access_group_id: ID of the access group that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([acs_access_group_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/access_groups/delete" - ) params: Dict[str, Any] = {} if acs_access_group_id is not None: params["acs_access_group_id"] = acs_access_group_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/access_groups/delete" + ) + self.client.delete("/acs/access_groups/delete", params=params) return None @@ -198,15 +194,16 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_access_group_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/access_groups/get" - ) params: Dict[str, Any] = {} if acs_access_group_id is not None: params["acs_access_group_id"] = acs_access_group_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/access_groups/get" + ) + res = self.client.get("/acs/access_groups/get", params=params) return AcsAccessGroup.from_dict(res["acs_access_group"]) @@ -265,15 +262,16 @@ def list_accessible_entrances( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_access_group_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/access_groups/list_accessible_entrances" - ) params: Dict[str, Any] = {} if acs_access_group_id is not None: params["acs_access_group_id"] = acs_access_group_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/access_groups/list_accessible_entrances" + ) + res = self.client.get( "/acs/access_groups/list_accessible_entrances", params=params ) @@ -293,15 +291,16 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_access_group_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/access_groups/list_users" - ) params: Dict[str, Any] = {} if acs_access_group_id is not None: params["acs_access_group_id"] = acs_access_group_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/access_groups/list_users" + ) + res = self.client.get("/acs/access_groups/list_users", params=params) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -327,16 +326,6 @@ def remove_user( :param user_identity_id: ID of the user identity associated with the user that you want to remove from an access group. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_access_group_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/access_groups/remove_user" - ) params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -346,6 +335,11 @@ def remove_user( if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/access_groups/remove_user" + ) + self.client.delete("/acs/access_groups/remove_user", params=params) return None diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index 791e75da..68a56846 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -207,16 +207,6 @@ def assign( :param user_identity_id: ID of the user identity to whom you want to assign a credential. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the credential belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_credential_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/credentials/assign" - ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -226,6 +216,11 @@ def assign( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/credentials/assign" + ) + self.client.patch("/acs/credentials/assign", json=json_payload) return None @@ -283,26 +278,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_method is not None, - acs_system_id is not None, - acs_user_id is not None, - allowed_acs_entrance_ids is not None, - assa_abloy_vostio_metadata is not None, - code is not None, - credential_manager_acs_system_id is not None, - ends_at is not None, - is_multi_phone_sync_credential is not None, - salto_space_metadata is not None, - starts_at is not None, - user_identity_id is not None, - visionline_metadata is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/credentials/create" - ) json_payload: Dict[str, Any] = {} if access_method is not None: @@ -336,6 +311,11 @@ def create( if visionline_metadata is not None: json_payload["visionline_metadata"] = visionline_metadata + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/credentials/create" + ) + res = self.client.post("/acs/credentials/create", json=json_payload) return AcsCredential.from_dict(res["acs_credential"]) @@ -351,15 +331,16 @@ def delete(self, *, acs_credential_id: str) -> None: :param acs_credential_id: ID of the credential that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([acs_credential_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/credentials/delete" - ) params: Dict[str, Any] = {} if acs_credential_id is not None: params["acs_credential_id"] = acs_credential_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/credentials/delete" + ) + self.client.delete("/acs/credentials/delete", params=params) return None @@ -375,15 +356,16 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_credential_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/credentials/get" - ) params: Dict[str, Any] = {} if acs_credential_id is not None: params["acs_credential_id"] = acs_credential_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/credentials/get" + ) + res = self.client.get("/acs/credentials/get", params=params) return AcsCredential.from_dict(res["acs_credential"]) @@ -458,15 +440,16 @@ def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntran :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_credential_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/credentials/list_accessible_entrances" - ) params: Dict[str, Any] = {} if acs_credential_id is not None: params["acs_credential_id"] = acs_credential_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/credentials/list_accessible_entrances" + ) + res = self.client.get( "/acs/credentials/list_accessible_entrances", params=params ) @@ -494,16 +477,6 @@ def unassign( :param user_identity_id: ID of the user identity from which you want to unassign a credential. You can only provide one of acs_user_id or user_identity_id. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_credential_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/credentials/unassign" - ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -513,6 +486,11 @@ def unassign( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/credentials/unassign" + ) + self.client.patch("/acs/credentials/unassign", json=json_payload) return None @@ -538,12 +516,6 @@ def update( :param ends_at: Replacement date and time at which the validity of the credential ends, in `ISO 8601 `_ format. Must be a time in the future and after the ``starts_at`` value that you set when creating the credential. :raises ValueError: At least one parameter must be provided.""" - if not any( - [acs_credential_id is not None, code is not None, ends_at is not None] - ): - raise ValueError( - "At least one parameter is required for /acs/credentials/update" - ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -553,6 +525,11 @@ def update( if ends_at is not None: json_payload["ends_at"] = ends_at + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/credentials/update" + ) + self.client.patch("/acs/credentials/update", json=json_payload) return None diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index 008cc01d..194008aa 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -159,16 +159,6 @@ def encode_credential( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_encoder_id is not None, - access_method_id is not None, - acs_credential_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/encoders/encode_credential" - ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -178,6 +168,11 @@ def encode_credential( if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/encoders/encode_credential" + ) + res = self.client.post("/acs/encoders/encode_credential", json=json_payload) wait_for_action_attempt = ( @@ -203,13 +198,14 @@ def get(self, *, acs_encoder_id: str) -> AcsEncoder: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_encoder_id is not None]): - raise ValueError("At least one parameter is required for /acs/encoders/get") params: Dict[str, Any] = {} if acs_encoder_id is not None: params["acs_encoder_id"] = acs_encoder_id + if not params: + raise ValueError("At least one parameter is required for /acs/encoders/get") + res = self.client.get("/acs/encoders/get", params=params) return AcsEncoder.from_dict(res["acs_encoder"]) @@ -279,10 +275,6 @@ def scan_credential( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_encoder_id is not None, salto_ks_metadata is not None]): - raise ValueError( - "At least one parameter is required for /acs/encoders/scan_credential" - ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -290,6 +282,11 @@ def scan_credential( if salto_ks_metadata is not None: json_payload["salto_ks_metadata"] = salto_ks_metadata + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/encoders/scan_credential" + ) + res = self.client.post("/acs/encoders/scan_credential", json=json_payload) wait_for_action_attempt = ( @@ -333,17 +330,6 @@ def scan_to_assign_credential( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_encoder_id is not None, - acs_user_id is not None, - salto_ks_metadata is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/encoders/scan_to_assign_credential" - ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -355,6 +341,11 @@ def scan_to_assign_credential( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/encoders/scan_to_assign_credential" + ) + res = self.client.post( "/acs/encoders/scan_to_assign_credential", json=json_payload ) diff --git a/seam/routes/acs_encoders_simulate.py b/seam/routes/acs_encoders_simulate.py index 9a5926bd..ac0d1793 100644 --- a/seam/routes/acs_encoders_simulate.py +++ b/seam/routes/acs_encoders_simulate.py @@ -103,16 +103,6 @@ def next_credential_encode_will_fail( :param acs_credential_id: ID of the ``acs_credential`` that will fail to be encoded onto a card in the next request. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_encoder_id is not None, - error_code is not None, - acs_credential_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail" - ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -122,6 +112,11 @@ def next_credential_encode_will_fail( if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail" + ) + self.client.post( "/acs/encoders/simulate/next_credential_encode_will_fail", json=json_payload ) @@ -143,10 +138,6 @@ def next_credential_encode_will_succeed( :param scenario: Scenario to simulate. :raises ValueError: At least one parameter must be provided.""" - if not any([acs_encoder_id is not None, scenario is not None]): - raise ValueError( - "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed" - ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -154,6 +145,11 @@ def next_credential_encode_will_succeed( if scenario is not None: json_payload["scenario"] = scenario + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed" + ) + self.client.post( "/acs/encoders/simulate/next_credential_encode_will_succeed", json=json_payload, @@ -182,16 +178,6 @@ def next_credential_scan_will_fail( :param acs_credential_id_on_seam: :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_encoder_id is not None, - error_code is not None, - acs_credential_id_on_seam is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail" - ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -201,6 +187,11 @@ def next_credential_scan_will_fail( if acs_credential_id_on_seam is not None: json_payload["acs_credential_id_on_seam"] = acs_credential_id_on_seam + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail" + ) + self.client.post( "/acs/encoders/simulate/next_credential_scan_will_fail", json=json_payload ) @@ -228,16 +219,6 @@ def next_credential_scan_will_succeed( :param scenario: Scenario to simulate. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_encoder_id is not None, - acs_credential_id_on_seam is not None, - scenario is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed" - ) json_payload: Dict[str, Any] = {} if acs_encoder_id is not None: @@ -247,6 +228,11 @@ def next_credential_scan_will_succeed( if scenario is not None: json_payload["scenario"] = scenario + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed" + ) + self.client.post( "/acs/encoders/simulate/next_credential_scan_will_succeed", json=json_payload, diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index c25e7848..353929ca 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -134,15 +134,16 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_entrance_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/entrances/get" - ) params: Dict[str, Any] = {} if acs_entrance_id is not None: params["acs_entrance_id"] = acs_entrance_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/entrances/get" + ) + res = self.client.get("/acs/entrances/get", params=params) return AcsEntrance.from_dict(res["acs_entrance"]) @@ -168,16 +169,6 @@ def grant_access( :param user_identity_id: ID of the user identity to whom you want to grant access to an entrance. You can only provide one of acs_user_id or user_identity_id. If the ACS system contains an ACS user with the same ``email_address`` or ``phone_number`` as the user identity that you specify, they are linked, and the access group membership belongs to the ACS user. If the ACS system does not have a corresponding ACS user, one is created. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_entrance_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/entrances/grant_access" - ) json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -187,6 +178,11 @@ def grant_access( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/entrances/grant_access" + ) + self.client.post("/acs/entrances/grant_access", json=json_payload) return None @@ -280,10 +276,6 @@ def list_credentials_with_access( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_entrance_id is not None, include_if is not None]): - raise ValueError( - "At least one parameter is required for /acs/entrances/list_credentials_with_access" - ) json_payload: Dict[str, Any] = {} if acs_entrance_id is not None: @@ -291,6 +283,11 @@ def list_credentials_with_access( if include_if is not None: json_payload["include_if"] = include_if + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/entrances/list_credentials_with_access" + ) + res = self.client.post( "/acs/entrances/list_credentials_with_access", json=json_payload ) @@ -318,10 +315,6 @@ def unlock( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_credential_id is not None, acs_entrance_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/entrances/unlock" - ) json_payload: Dict[str, Any] = {} if acs_credential_id is not None: @@ -329,6 +322,11 @@ def unlock( if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/entrances/unlock" + ) + res = self.client.post("/acs/entrances/unlock", json=json_payload) wait_for_action_attempt = ( diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index 41a728c5..7eb41612 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -90,13 +90,14 @@ def get(self, *, acs_system_id: str) -> AcsSystem: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_system_id is not None]): - raise ValueError("At least one parameter is required for /acs/systems/get") params: Dict[str, Any] = {} if acs_system_id is not None: params["acs_system_id"] = acs_system_id + if not params: + raise ValueError("At least one parameter is required for /acs/systems/get") + res = self.client.get("/acs/systems/get", params=params) return AcsSystem.from_dict(res["acs_system"]) @@ -152,15 +153,16 @@ def list_compatible_credential_manager_acs_systems( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([acs_system_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems" - ) params: Dict[str, Any] = {} if acs_system_id is not None: params["acs_system_id"] = acs_system_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems" + ) + res = self.client.get( "/acs/systems/list_compatible_credential_manager_acs_systems", params=params ) @@ -188,16 +190,6 @@ def report_devices( :param acs_entrances: Array of ACS entrances to report :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_system_id is not None, - acs_encoders is not None, - acs_entrances is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/systems/report_devices" - ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -207,6 +199,11 @@ def report_devices( if acs_entrances is not None: json_payload["acs_entrances"] = acs_entrances + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/systems/report_devices" + ) + self.client.post("/acs/systems/report_devices", json=json_payload) return None diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 1b0636bc..6e905b5e 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -285,10 +285,6 @@ def add_to_access_group( :param acs_user_id: ID of the access system user that you want to add to an access group. :raises ValueError: At least one parameter must be provided.""" - if not any([acs_access_group_id is not None, acs_user_id is not None]): - raise ValueError( - "At least one parameter is required for /acs/users/add_to_access_group" - ) json_payload: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -296,6 +292,11 @@ def add_to_access_group( if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/users/add_to_access_group" + ) + self.client.put("/acs/users/add_to_access_group", json=json_payload) return None @@ -336,19 +337,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_system_id is not None, - full_name is not None, - access_schedule is not None, - acs_access_group_ids is not None, - email is not None, - email_address is not None, - phone_number is not None, - user_identity_id is not None, - ] - ): - raise ValueError("At least one parameter is required for /acs/users/create") json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -368,6 +356,9 @@ def create( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError("At least one parameter is required for /acs/users/create") + res = self.client.post("/acs/users/create", json=json_payload) return AcsUser.from_dict(res["acs_user"]) @@ -391,14 +382,6 @@ def delete( :param user_identity_id: ID of the user identity that you want to delete. You must provide either acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError("At least one parameter is required for /acs/users/delete") params: Dict[str, Any] = {} if acs_system_id is not None: @@ -408,6 +391,9 @@ def delete( if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError("At least one parameter is required for /acs/users/delete") + self.client.delete("/acs/users/delete", params=params) return None @@ -433,14 +419,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_user_id is not None, - acs_system_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError("At least one parameter is required for /acs/users/get") params: Dict[str, Any] = {} if acs_user_id is not None: @@ -450,6 +428,9 @@ def get( if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError("At least one parameter is required for /acs/users/get") + res = self.client.get("/acs/users/get", params=params) return AcsUser.from_dict(res["acs_user"]) @@ -534,16 +515,6 @@ def list_accessible_entrances( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/users/list_accessible_entrances" - ) params: Dict[str, Any] = {} if acs_system_id is not None: @@ -553,6 +524,11 @@ def list_accessible_entrances( if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/users/list_accessible_entrances" + ) + res = self.client.get("/acs/users/list_accessible_entrances", params=params) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -578,16 +554,6 @@ def remove_from_access_group( :param user_identity_id: ID of the user identity that you want to remove from an access group. You can only provide acs_user_id or user_identity_id. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_access_group_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/users/remove_from_access_group" - ) params: Dict[str, Any] = {} if acs_access_group_id is not None: @@ -597,6 +563,11 @@ def remove_from_access_group( if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /acs/users/remove_from_access_group" + ) + self.client.delete("/acs/users/remove_from_access_group", params=params) return None @@ -622,16 +593,6 @@ def revoke_access_to_all_entrances( :param user_identity_id: ID of the user identity for whom you want to revoke access. You can only provide acs_user_id or user_identity_id. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/users/revoke_access_to_all_entrances" - ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -641,6 +602,11 @@ def revoke_access_to_all_entrances( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/users/revoke_access_to_all_entrances" + ) + self.client.post("/acs/users/revoke_access_to_all_entrances", json=json_payload) return None @@ -664,16 +630,6 @@ def suspend( :param user_identity_id: ID of the user identity that you want to suspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/users/suspend" - ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -683,6 +639,11 @@ def suspend( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/users/suspend" + ) + self.client.post("/acs/users/suspend", json=json_payload) return None @@ -706,16 +667,6 @@ def unsuspend( :param user_identity_id: ID of the user identity that you want to unsuspend. You can only provide acs_user_id or the combination of acs_system_id and user_identity_id. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_system_id is not None, - acs_user_id is not None, - user_identity_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /acs/users/unsuspend" - ) json_payload: Dict[str, Any] = {} if acs_system_id is not None: @@ -725,6 +676,11 @@ def unsuspend( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /acs/users/unsuspend" + ) + self.client.post("/acs/users/unsuspend", json=json_payload) return None @@ -766,20 +722,6 @@ def update( :param user_identity_id: ID of the user identity that you want to update. You can only provide acs_user_id or user_identity_id. If you provide user_identity_id, you must also provide acs_system_id. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_schedule is not None, - acs_system_id is not None, - acs_user_id is not None, - email is not None, - email_address is not None, - full_name is not None, - hid_acs_system_id is not None, - phone_number is not None, - user_identity_id is not None, - ] - ): - raise ValueError("At least one parameter is required for /acs/users/update") json_payload: Dict[str, Any] = {} if access_schedule is not None: @@ -801,6 +743,9 @@ def update( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError("At least one parameter is required for /acs/users/update") + self.client.patch("/acs/users/update", json=json_payload) return None diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index 06ebaac8..977ad01b 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -72,15 +72,16 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([action_attempt_id is not None]): - raise ValueError( - "At least one parameter is required for /action_attempts/get" - ) params: Dict[str, Any] = {} if action_attempt_id is not None: params["action_attempt_id"] = action_attempt_id + if not params: + raise ValueError( + "At least one parameter is required for /action_attempts/get" + ) + res = self.client.get("/action_attempts/get", params=params) wait_for_action_attempt = ( diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index aeb1cb53..62da3156 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -234,15 +234,16 @@ def delete(self, *, client_session_id: str) -> None: :param client_session_id: ID of the client session that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([client_session_id is not None]): - raise ValueError( - "At least one parameter is required for /client_sessions/delete" - ) params: Dict[str, Any] = {} if client_session_id is not None: params["client_session_id"] = client_session_id + if not params: + raise ValueError( + "At least one parameter is required for /client_sessions/delete" + ) + self.client.delete("/client_sessions/delete", params=params) return None @@ -353,19 +354,6 @@ def grant_access( :param user_identity_ids: Deprecated: Use ``user_identity_id``. IDs of the `user identities `_ that you want to associate with the client session. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - client_session_id is not None, - connect_webview_ids is not None, - connected_account_ids is not None, - user_identifier_key is not None, - user_identity_id is not None, - user_identity_ids is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /client_sessions/grant_access" - ) json_payload: Dict[str, Any] = {} if client_session_id is not None: @@ -381,6 +369,11 @@ def grant_access( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids + if not json_payload: + raise ValueError( + "At least one parameter is required for /client_sessions/grant_access" + ) + self.client.patch("/client_sessions/grant_access", json=json_payload) return None @@ -442,15 +435,16 @@ def revoke(self, *, client_session_id: str) -> None: :param client_session_id: ID of the client session that you want to revoke. :raises ValueError: At least one parameter must be provided.""" - if not any([client_session_id is not None]): - raise ValueError( - "At least one parameter is required for /client_sessions/revoke" - ) json_payload: Dict[str, Any] = {} if client_session_id is not None: json_payload["client_session_id"] = client_session_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /client_sessions/revoke" + ) + self.client.post("/client_sessions/revoke", json=json_payload) return None diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index de808af6..2310a432 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -201,15 +201,16 @@ def delete(self, *, connect_webview_id: str) -> None: :param connect_webview_id: ID of the Connect Webview that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([connect_webview_id is not None]): - raise ValueError( - "At least one parameter is required for /connect_webviews/delete" - ) params: Dict[str, Any] = {} if connect_webview_id is not None: params["connect_webview_id"] = connect_webview_id + if not params: + raise ValueError( + "At least one parameter is required for /connect_webviews/delete" + ) + self.client.delete("/connect_webviews/delete", params=params) return None @@ -227,15 +228,16 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([connect_webview_id is not None]): - raise ValueError( - "At least one parameter is required for /connect_webviews/get" - ) params: Dict[str, Any] = {} if connect_webview_id is not None: params["connect_webview_id"] = connect_webview_id + if not params: + raise ValueError( + "At least one parameter is required for /connect_webviews/get" + ) + res = self.client.get("/connect_webviews/get", params=params) return ConnectWebview.from_dict(res["connect_webview"]) diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index 4c0c88c3..1d8a6a6f 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -138,15 +138,16 @@ def delete(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([connected_account_id is not None]): - raise ValueError( - "At least one parameter is required for /connected_accounts/delete" - ) params: Dict[str, Any] = {} if connected_account_id is not None: params["connected_account_id"] = connected_account_id + if not params: + raise ValueError( + "At least one parameter is required for /connected_accounts/delete" + ) + self.client.delete("/connected_accounts/delete", params=params) return None @@ -168,10 +169,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([connected_account_id is not None, email is not None]): - raise ValueError( - "At least one parameter is required for /connected_accounts/get" - ) params: Dict[str, Any] = {} if connected_account_id is not None: @@ -179,6 +176,11 @@ def get( if email is not None: params["email"] = email + if not params: + raise ValueError( + "At least one parameter is required for /connected_accounts/get" + ) + res = self.client.get("/connected_accounts/get", params=params) return ConnectedAccount.from_dict(res["connected_account"]) @@ -248,15 +250,16 @@ def sync(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account that you want to sync. :raises ValueError: At least one parameter must be provided.""" - if not any([connected_account_id is not None]): - raise ValueError( - "At least one parameter is required for /connected_accounts/sync" - ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /connected_accounts/sync" + ) + self.client.post("/connected_accounts/sync", json=json_payload) return None @@ -291,19 +294,6 @@ def update( :param display_name: Human-readable name for the connected account, shown in the dashboard. For example, ``Booking from Airbnb House 1``. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - connected_account_id is not None, - accepted_capabilities is not None, - automatically_manage_new_devices is not None, - custom_metadata is not None, - customer_key is not None, - display_name is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /connected_accounts/update" - ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: @@ -321,6 +311,11 @@ def update( if display_name is not None: json_payload["display_name"] = display_name + if not json_payload: + raise ValueError( + "At least one parameter is required for /connected_accounts/update" + ) + self.client.patch("/connected_accounts/update", json=json_payload) return None diff --git a/seam/routes/connected_accounts_simulate.py b/seam/routes/connected_accounts_simulate.py index f1463af1..76df8f62 100644 --- a/seam/routes/connected_accounts_simulate.py +++ b/seam/routes/connected_accounts_simulate.py @@ -32,15 +32,16 @@ def disconnect(self, *, connected_account_id: str) -> None: :param connected_account_id: ID of the connected account you want to simulate as disconnected. :raises ValueError: At least one parameter must be provided.""" - if not any([connected_account_id is not None]): - raise ValueError( - "At least one parameter is required for /connected_accounts/simulate/disconnect" - ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /connected_accounts/simulate/disconnect" + ) + self.client.post("/connected_accounts/simulate/disconnect", json=json_payload) return None diff --git a/seam/routes/customers.py b/seam/routes/customers.py index 88d34e01..494848c5 100644 --- a/seam/routes/customers.py +++ b/seam/routes/customers.py @@ -449,33 +449,6 @@ def push_data( :param users: List of users. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - customer_key is not None, - access_grants is not None, - bookings is not None, - buildings is not None, - common_areas is not None, - facilities is not None, - guests is not None, - listings is not None, - properties is not None, - property_listings is not None, - reservations is not None, - residents is not None, - rooms is not None, - sites is not None, - spaces is not None, - staff_members is not None, - tenants is not None, - units is not None, - user_identities is not None, - users is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /customers/push_data" - ) json_payload: Dict[str, Any] = {} if customer_key is not None: @@ -519,6 +492,11 @@ def push_data( if users is not None: json_payload["users"] = users + if not json_payload: + raise ValueError( + "At least one parameter is required for /customers/push_data" + ) + self.client.post("/customers/push_data", json=json_payload) return None diff --git a/seam/routes/devices.py b/seam/routes/devices.py index b0539308..8fe8841c 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -181,8 +181,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, name is not None]): - raise ValueError("At least one parameter is required for /devices/get") params: Dict[str, Any] = {} if device_id is not None: @@ -190,6 +188,9 @@ def get( if name is not None: params["name"] = name + if not params: + raise ValueError("At least one parameter is required for /devices/get") + res = self.client.get("/devices/get", params=params) return Device.from_dict(res["device"]) @@ -328,15 +329,16 @@ def report_provider_metadata(self, *, devices: List[Dict[str, Any]]) -> None: :param devices: Array of devices with provider metadata to update :raises ValueError: At least one parameter must be provided.""" - if not any([devices is not None]): - raise ValueError( - "At least one parameter is required for /devices/report_provider_metadata" - ) json_payload: Dict[str, Any] = {} if devices is not None: json_payload["devices"] = devices + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/report_provider_metadata" + ) + self.client.post("/devices/report_provider_metadata", json=json_payload) return None @@ -371,17 +373,6 @@ def update( :param properties: :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - backup_access_code_pool_enabled is not None, - custom_metadata is not None, - is_managed is not None, - name is not None, - properties is not None, - ] - ): - raise ValueError("At least one parameter is required for /devices/update") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -399,6 +390,9 @@ def update( if properties is not None: json_payload["properties"] = properties + if not json_payload: + raise ValueError("At least one parameter is required for /devices/update") + self.client.patch("/devices/update", json=json_payload) return None diff --git a/seam/routes/devices_simulate.py b/seam/routes/devices_simulate.py index c5497e79..dfdfdfed 100644 --- a/seam/routes/devices_simulate.py +++ b/seam/routes/devices_simulate.py @@ -88,15 +88,16 @@ def connect(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate connecting to Seam. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /devices/simulate/connect" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/simulate/connect" + ) + self.client.post("/devices/simulate/connect", json=json_payload) return None @@ -115,15 +116,16 @@ def connect_to_hub(self, *, device_id: str) -> None: :param device_id: ID of the device whose hub you want to reconnect. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /devices/simulate/connect_to_hub" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/simulate/connect_to_hub" + ) + self.client.post("/devices/simulate/connect_to_hub", json=json_payload) return None @@ -139,15 +141,16 @@ def disconnect(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate disconnecting from Seam. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /devices/simulate/disconnect" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/simulate/disconnect" + ) + self.client.post("/devices/simulate/disconnect", json=json_payload) return None @@ -167,15 +170,16 @@ def disconnect_from_hub(self, *, device_id: str) -> None: :param device_id: ID of the device whose hub you want to disconnect. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /devices/simulate/disconnect_from_hub" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/simulate/disconnect_from_hub" + ) + self.client.post("/devices/simulate/disconnect_from_hub", json=json_payload) return None @@ -195,10 +199,6 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: :param is_expired: :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, is_expired is not None]): - raise ValueError( - "At least one parameter is required for /devices/simulate/paid_subscription" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -206,6 +206,11 @@ def paid_subscription(self, *, device_id: str, is_expired: bool) -> None: if is_expired is not None: json_payload["is_expired"] = is_expired + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/simulate/paid_subscription" + ) + self.client.post("/devices/simulate/paid_subscription", json=json_payload) return None @@ -221,15 +226,16 @@ def remove(self, *, device_id: str) -> None: :param device_id: ID of the device that you want to simulate removing from Seam. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /devices/simulate/remove" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/simulate/remove" + ) + self.client.post("/devices/simulate/remove", json=json_payload) return None diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 6980512c..4b8b5792 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -122,10 +122,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, name is not None]): - raise ValueError( - "At least one parameter is required for /devices/unmanaged/get" - ) params: Dict[str, Any] = {} if device_id is not None: @@ -133,6 +129,11 @@ def get( if name is not None: params["name"] = name + if not params: + raise ValueError( + "At least one parameter is required for /devices/unmanaged/get" + ) + res = self.client.get("/devices/unmanaged/get", params=params) return UnmanagedDevice.from_dict(res["device"]) @@ -241,12 +242,6 @@ def update( :param is_managed: Indicates whether the device is managed. Set this parameter to ``true`` to convert an unmanaged device to managed. :raises ValueError: At least one parameter must be provided.""" - if not any( - [device_id is not None, custom_metadata is not None, is_managed is not None] - ): - raise ValueError( - "At least one parameter is required for /devices/unmanaged/update" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -256,6 +251,11 @@ def update( if is_managed is not None: json_payload["is_managed"] = is_managed + if not json_payload: + raise ValueError( + "At least one parameter is required for /devices/unmanaged/update" + ) + self.client.patch("/devices/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/events.py b/seam/routes/events.py index 234d037e..8613b639 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -151,10 +151,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [event_id is not None, device_id is not None, event_type is not None] - ): - raise ValueError("At least one parameter is required for /events/get") params: Dict[str, Any] = {} if event_id is not None: @@ -164,6 +160,9 @@ def get( if event_type is not None: params["event_type"] = event_type + if not params: + raise ValueError("At least one parameter is required for /events/get") + res = self.client.get("/events/get", params=params) return SeamEvent.from_dict(res["event"]) @@ -264,39 +263,6 @@ def list( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - access_code_id is not None, - access_code_ids is not None, - access_grant_id is not None, - access_grant_ids is not None, - access_method_id is not None, - access_method_ids is not None, - acs_access_group_id is not None, - acs_credential_id is not None, - acs_encoder_id is not None, - acs_entrance_id is not None, - acs_system_id is not None, - acs_system_ids is not None, - acs_user_id is not None, - between is not None, - connect_webview_id is not None, - connected_account_id is not None, - customer_key is not None, - device_id is not None, - device_ids is not None, - event_ids is not None, - event_type is not None, - event_types is not None, - limit is not None, - since is not None, - space_id is not None, - space_ids is not None, - unstable_offset is not None, - user_identity_id is not None, - ] - ): - raise ValueError("At least one parameter is required for /events/list") json_payload: Dict[str, Any] = {} if access_code_id is not None: @@ -356,6 +322,9 @@ def list( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError("At least one parameter is required for /events/list") + res = self.client.post("/events/list", json=json_payload) return [SeamEvent.from_dict(item) for item in res["events"]] diff --git a/seam/routes/instant_keys.py b/seam/routes/instant_keys.py index acba857d..48787987 100644 --- a/seam/routes/instant_keys.py +++ b/seam/routes/instant_keys.py @@ -58,15 +58,16 @@ def delete(self, *, instant_key_id: str) -> None: :param instant_key_id: ID of the Instant Key that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([instant_key_id is not None]): - raise ValueError( - "At least one parameter is required for /instant_keys/delete" - ) params: Dict[str, Any] = {} if instant_key_id is not None: params["instant_key_id"] = instant_key_id + if not params: + raise ValueError( + "At least one parameter is required for /instant_keys/delete" + ) + self.client.delete("/instant_keys/delete", params=params) return None @@ -89,8 +90,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([instant_key_id is not None, instant_key_url is not None]): - raise ValueError("At least one parameter is required for /instant_keys/get") params: Dict[str, Any] = {} if instant_key_id is not None: @@ -98,6 +97,9 @@ def get( if instant_key_url is not None: params["instant_key_url"] = instant_key_url + if not params: + raise ValueError("At least one parameter is required for /instant_keys/get") + res = self.client.get("/instant_keys/get", params=params) return InstantKey.from_dict(res["instant_key"]) diff --git a/seam/routes/locks.py b/seam/routes/locks.py index 8b997e7e..fdf7843f 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -157,16 +157,6 @@ def configure_auto_lock( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - auto_lock_enabled is not None, - device_id is not None, - auto_lock_delay_seconds is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /locks/configure_auto_lock" - ) json_payload: Dict[str, Any] = {} if auto_lock_enabled is not None: @@ -176,6 +166,11 @@ def configure_auto_lock( if auto_lock_delay_seconds is not None: json_payload["auto_lock_delay_seconds"] = auto_lock_delay_seconds + if not json_payload: + raise ValueError( + "At least one parameter is required for /locks/configure_auto_lock" + ) + res = self.client.post("/locks/configure_auto_lock", json=json_payload) wait_for_action_attempt = ( @@ -208,8 +203,6 @@ def get( .. deprecated:: Use ``/devices/get`` instead.""" - if not any([device_id is not None, name is not None]): - raise ValueError("At least one parameter is required for /locks/get") params: Dict[str, Any] = {} if device_id is not None: @@ -217,6 +210,9 @@ def get( if name is not None: params["name"] = name + if not params: + raise ValueError("At least one parameter is required for /locks/get") + res = self.client.get("/locks/get", params=params) return Device.from_dict(res["device"]) @@ -286,13 +282,14 @@ def lock_door( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError("At least one parameter is required for /locks/lock_door") json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError("At least one parameter is required for /locks/lock_door") + res = self.client.post("/locks/lock_door", json=json_payload) wait_for_action_attempt = ( @@ -325,15 +322,16 @@ def unlock_door( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /locks/unlock_door" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /locks/unlock_door" + ) + res = self.client.post("/locks/unlock_door", json=json_payload) wait_for_action_attempt = ( diff --git a/seam/routes/locks_simulate.py b/seam/routes/locks_simulate.py index cc02ff36..6979c9c2 100644 --- a/seam/routes/locks_simulate.py +++ b/seam/routes/locks_simulate.py @@ -76,10 +76,6 @@ def keypad_code_entry( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([code is not None, device_id is not None]): - raise ValueError( - "At least one parameter is required for /locks/simulate/keypad_code_entry" - ) json_payload: Dict[str, Any] = {} if code is not None: @@ -87,6 +83,11 @@ def keypad_code_entry( if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /locks/simulate/keypad_code_entry" + ) + res = self.client.post("/locks/simulate/keypad_code_entry", json=json_payload) wait_for_action_attempt = ( @@ -121,15 +122,16 @@ def manual_lock_via_keypad( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /locks/simulate/manual_lock_via_keypad" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /locks/simulate/manual_lock_via_keypad" + ) + res = self.client.post( "/locks/simulate/manual_lock_via_keypad", json=json_payload ) diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index 33324f15..54c90ce8 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -139,19 +139,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - ends_daily_at is not None, - starts_daily_at is not None, - name is not None, - noise_threshold_decibels is not None, - noise_threshold_nrs is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /noise_sensors/noise_thresholds/create" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -167,6 +154,11 @@ def create( if noise_threshold_nrs is not None: json_payload["noise_threshold_nrs"] = noise_threshold_nrs + if not json_payload: + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/create" + ) + res = self.client.post( "/noise_sensors/noise_thresholds/create", json=json_payload ) @@ -186,10 +178,6 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: :param noise_threshold_id: ID of the noise threshold that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, noise_threshold_id is not None]): - raise ValueError( - "At least one parameter is required for /noise_sensors/noise_thresholds/delete" - ) params: Dict[str, Any] = {} if device_id is not None: @@ -197,6 +185,11 @@ def delete(self, *, device_id: str, noise_threshold_id: str) -> None: if noise_threshold_id is not None: params["noise_threshold_id"] = noise_threshold_id + if not params: + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/delete" + ) + self.client.delete("/noise_sensors/noise_thresholds/delete", params=params) return None @@ -214,15 +207,16 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([noise_threshold_id is not None]): - raise ValueError( - "At least one parameter is required for /noise_sensors/noise_thresholds/get" - ) params: Dict[str, Any] = {} if noise_threshold_id is not None: params["noise_threshold_id"] = noise_threshold_id + if not params: + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/get" + ) + res = self.client.get("/noise_sensors/noise_thresholds/get", params=params) return NoiseThreshold.from_dict(res["noise_threshold"]) @@ -240,15 +234,16 @@ def list(self, *, device_id: str) -> List[NoiseThreshold]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /noise_sensors/noise_thresholds/list" - ) params: Dict[str, Any] = {} if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/list" + ) + res = self.client.get("/noise_sensors/noise_thresholds/list", params=params) return [NoiseThreshold.from_dict(item) for item in res["noise_thresholds"]] @@ -286,20 +281,6 @@ def update( :param starts_daily_at: Time at which the noise threshold should become active daily. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - noise_threshold_id is not None, - ends_daily_at is not None, - name is not None, - noise_threshold_decibels is not None, - noise_threshold_nrs is not None, - starts_daily_at is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /noise_sensors/noise_thresholds/update" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -317,6 +298,11 @@ def update( if starts_daily_at is not None: json_payload["starts_daily_at"] = starts_daily_at + if not json_payload: + raise ValueError( + "At least one parameter is required for /noise_sensors/noise_thresholds/update" + ) + self.client.put("/noise_sensors/noise_thresholds/update", json=json_payload) return None diff --git a/seam/routes/noise_sensors_simulate.py b/seam/routes/noise_sensors_simulate.py index e7517c82..6c527582 100644 --- a/seam/routes/noise_sensors_simulate.py +++ b/seam/routes/noise_sensors_simulate.py @@ -32,15 +32,16 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: :param device_id: ID of the device for which you want to simulate the triggering of a noise threshold. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold" - ) json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold" + ) + self.client.post( "/noise_sensors/simulate/trigger_noise_threshold", json=json_payload ) diff --git a/seam/routes/phones.py b/seam/routes/phones.py index cf892b85..a00d49f4 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -69,15 +69,16 @@ def deactivate(self, *, device_id: str) -> None: :param device_id: Device ID of the phone that you want to deactivate. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError( - "At least one parameter is required for /phones/deactivate" - ) params: Dict[str, Any] = {} if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError( + "At least one parameter is required for /phones/deactivate" + ) + self.client.delete("/phones/deactivate", params=params) return None @@ -93,13 +94,14 @@ def get(self, *, device_id: str) -> Phone: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError("At least one parameter is required for /phones/get") params: Dict[str, Any] = {} if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError("At least one parameter is required for /phones/get") + res = self.client.get("/phones/get", params=params) return Phone.from_dict(res["phone"]) diff --git a/seam/routes/phones_simulate.py b/seam/routes/phones_simulate.py index a6f36859..58b47aca 100644 --- a/seam/routes/phones_simulate.py +++ b/seam/routes/phones_simulate.py @@ -63,17 +63,6 @@ def create_sandbox_phone( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - user_identity_id is not None, - assa_abloy_metadata is not None, - custom_sdk_installation_id is not None, - phone_metadata is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /phones/simulate/create_sandbox_phone" - ) json_payload: Dict[str, Any] = {} if user_identity_id is not None: @@ -85,6 +74,11 @@ def create_sandbox_phone( if phone_metadata is not None: json_payload["phone_metadata"] = phone_metadata + if not json_payload: + raise ValueError( + "At least one parameter is required for /phones/simulate/create_sandbox_phone" + ) + res = self.client.post( "/phones/simulate/create_sandbox_phone", json=json_payload ) diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index fdcc2f3e..b72ea972 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -232,10 +232,6 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No :param space_id: ID of the space to which you want to add entrances. :raises ValueError: At least one parameter must be provided.""" - if not any([acs_entrance_ids is not None, space_id is not None]): - raise ValueError( - "At least one parameter is required for /spaces/add_acs_entrances" - ) json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: @@ -243,6 +239,11 @@ def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> No if space_id is not None: json_payload["space_id"] = space_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /spaces/add_acs_entrances" + ) + self.client.put("/spaces/add_acs_entrances", json=json_payload) return None @@ -262,10 +263,6 @@ def add_connected_account( :param space_id: ID of the space to which you want to add the connected account. :raises ValueError: At least one parameter must be provided.""" - if not any([connected_account_id is not None, space_id is not None]): - raise ValueError( - "At least one parameter is required for /spaces/add_connected_account" - ) json_payload: Dict[str, Any] = {} if connected_account_id is not None: @@ -273,6 +270,11 @@ def add_connected_account( if space_id is not None: json_payload["space_id"] = space_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /spaces/add_connected_account" + ) + self.client.put("/spaces/add_connected_account", json=json_payload) return None @@ -288,10 +290,6 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: :param space_id: ID of the space to which you want to add devices. :raises ValueError: At least one parameter must be provided.""" - if not any([device_ids is not None, space_id is not None]): - raise ValueError( - "At least one parameter is required for /spaces/add_devices" - ) json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -299,6 +297,11 @@ def add_devices(self, *, device_ids: List[str], space_id: str) -> None: if space_id is not None: json_payload["space_id"] = space_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /spaces/add_devices" + ) + self.client.put("/spaces/add_devices", json=json_payload) return None @@ -336,18 +339,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - name is not None, - acs_entrance_ids is not None, - connected_account_ids is not None, - customer_data is not None, - customer_key is not None, - device_ids is not None, - space_key is not None, - ] - ): - raise ValueError("At least one parameter is required for /spaces/create") json_payload: Dict[str, Any] = {} if name is not None: @@ -365,6 +356,9 @@ def create( if space_key is not None: json_payload["space_key"] = space_key + if not json_payload: + raise ValueError("At least one parameter is required for /spaces/create") + res = self.client.post("/spaces/create", json=json_payload) return Space.from_dict(res["space"]) @@ -378,13 +372,14 @@ def delete(self, *, space_id: str) -> None: :param space_id: ID of the space that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([space_id is not None]): - raise ValueError("At least one parameter is required for /spaces/delete") params: Dict[str, Any] = {} if space_id is not None: params["space_id"] = space_id + if not params: + raise ValueError("At least one parameter is required for /spaces/delete") + self.client.delete("/spaces/delete", params=params) return None @@ -404,8 +399,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([space_id is not None, space_key is not None]): - raise ValueError("At least one parameter is required for /spaces/get") params: Dict[str, Any] = {} if space_id is not None: @@ -413,6 +406,9 @@ def get( if space_key is not None: params["space_key"] = space_key + if not params: + raise ValueError("At least one parameter is required for /spaces/get") + res = self.client.get("/spaces/get", params=params) return Space.from_dict(res["space"]) @@ -441,17 +437,6 @@ def get_related( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - exclude is not None, - include is not None, - space_ids is not None, - space_keys is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /spaces/get_related" - ) json_payload: Dict[str, Any] = {} if exclude is not None: @@ -463,6 +448,11 @@ def get_related( if space_keys is not None: json_payload["space_keys"] = space_keys + if not json_payload: + raise ValueError( + "At least one parameter is required for /spaces/get_related" + ) + res = self.client.post("/spaces/get_related", json=json_payload) return Batch.from_dict(res["batch"]) @@ -524,10 +514,6 @@ def remove_acs_entrances( :param space_id: ID of the space from which you want to remove entrances. :raises ValueError: At least one parameter must be provided.""" - if not any([acs_entrance_ids is not None, space_id is not None]): - raise ValueError( - "At least one parameter is required for /spaces/remove_acs_entrances" - ) json_payload: Dict[str, Any] = {} if acs_entrance_ids is not None: @@ -535,6 +521,11 @@ def remove_acs_entrances( if space_id is not None: json_payload["space_id"] = space_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /spaces/remove_acs_entrances" + ) + self.client.post("/spaces/remove_acs_entrances", json=json_payload) return None @@ -554,10 +545,6 @@ def remove_connected_account( :param space_id: ID of the space from which you want to remove the connected account. :raises ValueError: At least one parameter must be provided.""" - if not any([connected_account_id is not None, space_id is not None]): - raise ValueError( - "At least one parameter is required for /spaces/remove_connected_account" - ) params: Dict[str, Any] = {} if connected_account_id is not None: @@ -565,6 +552,11 @@ def remove_connected_account( if space_id is not None: params["space_id"] = space_id + if not params: + raise ValueError( + "At least one parameter is required for /spaces/remove_connected_account" + ) + self.client.delete("/spaces/remove_connected_account", params=params) return None @@ -582,10 +574,6 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: :param space_id: ID of the space from which you want to remove devices. :raises ValueError: At least one parameter must be provided.""" - if not any([device_ids is not None, space_id is not None]): - raise ValueError( - "At least one parameter is required for /spaces/remove_devices" - ) json_payload: Dict[str, Any] = {} if device_ids is not None: @@ -593,6 +581,11 @@ def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: if space_id is not None: json_payload["space_id"] = space_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /spaces/remove_devices" + ) + self.client.post("/spaces/remove_devices", json=json_payload) return None diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index 0428721b..b5ec077f 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -457,10 +457,6 @@ def activate_climate_preset( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([climate_preset_key is not None, device_id is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/activate_climate_preset" - ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -468,6 +464,11 @@ def activate_climate_preset( if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/activate_climate_preset" + ) + res = self.client.post( "/thermostats/activate_climate_preset", json=json_payload ) @@ -508,14 +509,6 @@ def cool( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - ] - ): - raise ValueError("At least one parameter is required for /thermostats/cool") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -525,6 +518,9 @@ def cool( if cooling_set_point_fahrenheit is not None: json_payload["cooling_set_point_fahrenheit"] = cooling_set_point_fahrenheit + if not json_payload: + raise ValueError("At least one parameter is required for /thermostats/cool") + res = self.client.post("/thermostats/cool", json=json_payload) wait_for_action_attempt = ( @@ -587,25 +583,6 @@ def create_climate_preset( :param name: User-friendly name to identify the `climate preset `_. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - climate_preset_key is not None, - device_id is not None, - climate_preset_mode is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - ecobee_metadata is not None, - fan_mode_setting is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - hvac_mode_setting is not None, - manual_override_allowed is not None, - name is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/create_climate_preset" - ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -633,6 +610,11 @@ def create_climate_preset( if name is not None: json_payload["name"] = name + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/create_climate_preset" + ) + self.client.post("/thermostats/create_climate_preset", json=json_payload) return None @@ -650,10 +632,6 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N :param device_id: ID of the thermostat device for which you want to delete a climate preset. :raises ValueError: At least one parameter must be provided.""" - if not any([climate_preset_key is not None, device_id is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/delete_climate_preset" - ) params: Dict[str, Any] = {} if climate_preset_key is not None: @@ -661,6 +639,11 @@ def delete_climate_preset(self, *, climate_preset_key: str, device_id: str) -> N if device_id is not None: params["device_id"] = device_id + if not params: + raise ValueError( + "At least one parameter is required for /thermostats/delete_climate_preset" + ) + self.client.delete("/thermostats/delete_climate_preset", params=params) return None @@ -689,14 +672,6 @@ def heat( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - ] - ): - raise ValueError("At least one parameter is required for /thermostats/heat") json_payload: Dict[str, Any] = {} if device_id is not None: @@ -706,6 +681,9 @@ def heat( if heating_set_point_fahrenheit is not None: json_payload["heating_set_point_fahrenheit"] = heating_set_point_fahrenheit + if not json_payload: + raise ValueError("At least one parameter is required for /thermostats/heat") + res = self.client.post("/thermostats/heat", json=json_payload) wait_for_action_attempt = ( @@ -752,18 +730,6 @@ def heat_cool( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/heat_cool" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -777,6 +743,11 @@ def heat_cool( if heating_set_point_fahrenheit is not None: json_payload["heating_set_point_fahrenheit"] = heating_set_point_fahrenheit + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/heat_cool" + ) + res = self.client.post("/thermostats/heat_cool", json=json_payload) wait_for_action_attempt = ( @@ -856,13 +827,14 @@ def off( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None]): - raise ValueError("At least one parameter is required for /thermostats/off") json_payload: Dict[str, Any] = {} if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError("At least one parameter is required for /thermostats/off") + res = self.client.post("/thermostats/off", json=json_payload) wait_for_action_attempt = ( @@ -892,10 +864,6 @@ def set_fallback_climate_preset( :param device_id: ID of the thermostat device for which you want to set the fallback climate preset. :raises ValueError: At least one parameter must be provided.""" - if not any([climate_preset_key is not None, device_id is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/set_fallback_climate_preset" - ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -903,6 +871,11 @@ def set_fallback_climate_preset( if device_id is not None: json_payload["device_id"] = device_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/set_fallback_climate_preset" + ) + self.client.post("/thermostats/set_fallback_climate_preset", json=json_payload) return None @@ -933,12 +906,6 @@ def set_fan_mode( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [device_id is not None, fan_mode is not None, fan_mode_setting is not None] - ): - raise ValueError( - "At least one parameter is required for /thermostats/set_fan_mode" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -948,6 +915,11 @@ def set_fan_mode( if fan_mode_setting is not None: json_payload["fan_mode_setting"] = fan_mode_setting + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/set_fan_mode" + ) + res = self.client.post("/thermostats/set_fan_mode", json=json_payload) wait_for_action_attempt = ( @@ -997,19 +969,6 @@ def set_hvac_mode( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - hvac_mode_setting is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/set_hvac_mode" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -1025,6 +984,11 @@ def set_hvac_mode( if heating_set_point_fahrenheit is not None: json_payload["heating_set_point_fahrenheit"] = heating_set_point_fahrenheit + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/set_hvac_mode" + ) + res = self.client.post("/thermostats/set_hvac_mode", json=json_payload) wait_for_action_attempt = ( @@ -1066,18 +1030,6 @@ def set_temperature_threshold( :param upper_limit_fahrenheit: Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either ``upper_limit`` but not both. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - lower_limit_celsius is not None, - lower_limit_fahrenheit is not None, - upper_limit_celsius is not None, - upper_limit_fahrenheit is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/set_temperature_threshold" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -1091,6 +1043,11 @@ def set_temperature_threshold( if upper_limit_fahrenheit is not None: json_payload["upper_limit_fahrenheit"] = upper_limit_fahrenheit + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/set_temperature_threshold" + ) + self.client.patch("/thermostats/set_temperature_threshold", json=json_payload) return None @@ -1143,25 +1100,6 @@ def update_climate_preset( :param name: User-friendly name to identify the `climate preset `_. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - climate_preset_key is not None, - device_id is not None, - climate_preset_mode is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - ecobee_metadata is not None, - fan_mode_setting is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - hvac_mode_setting is not None, - manual_override_allowed is not None, - name is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/update_climate_preset" - ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -1189,6 +1127,11 @@ def update_climate_preset( if name is not None: json_payload["name"] = name + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/update_climate_preset" + ) + self.client.patch("/thermostats/update_climate_preset", json=json_payload) return None @@ -1234,21 +1177,6 @@ def update_weekly_program( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - friday_program_id is not None, - monday_program_id is not None, - saturday_program_id is not None, - sunday_program_id is not None, - thursday_program_id is not None, - tuesday_program_id is not None, - wednesday_program_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/update_weekly_program" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -1268,6 +1196,11 @@ def update_weekly_program( if wednesday_program_id is not None: json_payload["wednesday_program_id"] = wednesday_program_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/update_weekly_program" + ) + res = self.client.post("/thermostats/update_weekly_program", json=json_payload) wait_for_action_attempt = ( diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index 9d19d1f7..cff60944 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -83,10 +83,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, name is not None, periods is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/daily_programs/create" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -96,6 +92,11 @@ def create( if periods is not None: json_payload["periods"] = periods + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/daily_programs/create" + ) + res = self.client.post("/thermostats/daily_programs/create", json=json_payload) return ThermostatDailyProgram.from_dict(res["thermostat_daily_program"]) @@ -111,15 +112,16 @@ def delete(self, *, thermostat_daily_program_id: str) -> None: :param thermostat_daily_program_id: ID of the thermostat daily program that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([thermostat_daily_program_id is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/daily_programs/delete" - ) params: Dict[str, Any] = {} if thermostat_daily_program_id is not None: params["thermostat_daily_program_id"] = thermostat_daily_program_id + if not params: + raise ValueError( + "At least one parameter is required for /thermostats/daily_programs/delete" + ) + self.client.delete("/thermostats/daily_programs/delete", params=params) return None @@ -150,16 +152,6 @@ def update( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - name is not None, - periods is not None, - thermostat_daily_program_id is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/daily_programs/update" - ) json_payload: Dict[str, Any] = {} if name is not None: @@ -169,6 +161,11 @@ def update( if thermostat_daily_program_id is not None: json_payload["thermostat_daily_program_id"] = thermostat_daily_program_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/daily_programs/update" + ) + res = self.client.patch("/thermostats/daily_programs/update", json=json_payload) wait_for_action_attempt = ( diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index 642b0974..d708149e 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -147,20 +147,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - climate_preset_key is not None, - device_id is not None, - ends_at is not None, - starts_at is not None, - is_override_allowed is not None, - max_override_period_minutes is not None, - name is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/schedules/create" - ) json_payload: Dict[str, Any] = {} if climate_preset_key is not None: @@ -178,6 +164,11 @@ def create( if name is not None: json_payload["name"] = name + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/schedules/create" + ) + res = self.client.post("/thermostats/schedules/create", json=json_payload) return ThermostatSchedule.from_dict(res["thermostat_schedule"]) @@ -193,15 +184,16 @@ def delete(self, *, thermostat_schedule_id: str) -> None: :param thermostat_schedule_id: ID of the thermostat schedule that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([thermostat_schedule_id is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/schedules/delete" - ) params: Dict[str, Any] = {} if thermostat_schedule_id is not None: params["thermostat_schedule_id"] = thermostat_schedule_id + if not params: + raise ValueError( + "At least one parameter is required for /thermostats/schedules/delete" + ) + self.client.delete("/thermostats/schedules/delete", params=params) return None @@ -219,15 +211,16 @@ def get(self, *, thermostat_schedule_id: str) -> ThermostatSchedule: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([thermostat_schedule_id is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/schedules/get" - ) params: Dict[str, Any] = {} if thermostat_schedule_id is not None: params["thermostat_schedule_id"] = thermostat_schedule_id + if not params: + raise ValueError( + "At least one parameter is required for /thermostats/schedules/get" + ) + res = self.client.get("/thermostats/schedules/get", params=params) return ThermostatSchedule.from_dict(res["thermostat_schedule"]) @@ -249,10 +242,6 @@ def list( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, user_identifier_key is not None]): - raise ValueError( - "At least one parameter is required for /thermostats/schedules/list" - ) params: Dict[str, Any] = {} if device_id is not None: @@ -260,6 +249,11 @@ def list( if user_identifier_key is not None: params["user_identifier_key"] = user_identifier_key + if not params: + raise ValueError( + "At least one parameter is required for /thermostats/schedules/list" + ) + res = self.client.get("/thermostats/schedules/list", params=params) return [ @@ -299,20 +293,6 @@ def update( :param starts_at: Date and time at which the thermostat schedule starts, in `ISO 8601 `_ format. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - thermostat_schedule_id is not None, - climate_preset_key is not None, - ends_at is not None, - is_override_allowed is not None, - max_override_period_minutes is not None, - name is not None, - starts_at is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/schedules/update" - ) json_payload: Dict[str, Any] = {} if thermostat_schedule_id is not None: @@ -330,6 +310,11 @@ def update( if starts_at is not None: json_payload["starts_at"] = starts_at + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/schedules/update" + ) + self.client.patch("/thermostats/schedules/update", json=json_payload) return None diff --git a/seam/routes/thermostats_simulate.py b/seam/routes/thermostats_simulate.py index 07a9749a..69dec843 100644 --- a/seam/routes/thermostats_simulate.py +++ b/seam/routes/thermostats_simulate.py @@ -89,19 +89,6 @@ def hvac_mode_adjusted( :param heating_set_point_fahrenheit: Heating `set point `_ in °F that you want to simulate. You must set ``heating_set_point_fahrenheit`` or ``heating_set_point_celsius``. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - hvac_mode is not None, - cooling_set_point_celsius is not None, - cooling_set_point_fahrenheit is not None, - heating_set_point_celsius is not None, - heating_set_point_fahrenheit is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -117,6 +104,11 @@ def hvac_mode_adjusted( if heating_set_point_fahrenheit is not None: json_payload["heating_set_point_fahrenheit"] = heating_set_point_fahrenheit + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted" + ) + self.client.post("/thermostats/simulate/hvac_mode_adjusted", json=json_payload) return None @@ -142,16 +134,6 @@ def temperature_reached( :param temperature_fahrenheit: Temperature in °F that you want simulate the thermostat reaching. You must set ``temperature_fahrenheit`` or ``temperature_celsius``. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - device_id is not None, - temperature_celsius is not None, - temperature_fahrenheit is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /thermostats/simulate/temperature_reached" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -161,6 +143,11 @@ def temperature_reached( if temperature_fahrenheit is not None: json_payload["temperature_fahrenheit"] = temperature_fahrenheit + if not json_payload: + raise ValueError( + "At least one parameter is required for /thermostats/simulate/temperature_reached" + ) + self.client.post("/thermostats/simulate/temperature_reached", json=json_payload) return None diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index 3f51725b..ab7442b2 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -285,16 +285,6 @@ def add_acs_user( :param user_identity_key: Key of the user identity to which you want to add an access system user. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - acs_user_id is not None, - user_identity_id is not None, - user_identity_key is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /user_identities/add_acs_user" - ) json_payload: Dict[str, Any] = {} if acs_user_id is not None: @@ -304,6 +294,11 @@ def add_acs_user( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key + if not json_payload: + raise ValueError( + "At least one parameter is required for /user_identities/add_acs_user" + ) + self.client.put("/user_identities/add_acs_user", json=json_payload) return None @@ -363,15 +358,16 @@ def delete(self, *, user_identity_id: str) -> None: :param user_identity_id: ID of the user identity that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/delete" - ) params: Dict[str, Any] = {} if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/delete" + ) + self.client.delete("/user_identities/delete", params=params) return None @@ -399,16 +395,6 @@ def generate_instant_key( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - user_identity_id is not None, - customization_profile_id is not None, - max_use_count is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /user_identities/generate_instant_key" - ) json_payload: Dict[str, Any] = {} if user_identity_id is not None: @@ -418,6 +404,11 @@ def generate_instant_key( if max_use_count is not None: json_payload["max_use_count"] = max_use_count + if not json_payload: + raise ValueError( + "At least one parameter is required for /user_identities/generate_instant_key" + ) + res = self.client.post( "/user_identities/generate_instant_key", json=json_payload ) @@ -442,10 +433,6 @@ def get( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([user_identity_id is not None, user_identity_key is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/get" - ) params: Dict[str, Any] = {} if user_identity_id is not None: @@ -453,6 +440,11 @@ def get( if user_identity_key is not None: params["user_identity_key"] = user_identity_key + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/get" + ) + res = self.client.get("/user_identities/get", params=params) return UserIdentity.from_dict(res["user_identity"]) @@ -470,10 +462,6 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No :param user_identity_id: ID of the user identity that you want to grant access to a device. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/grant_access_to_device" - ) json_payload: Dict[str, Any] = {} if device_id is not None: @@ -481,6 +469,11 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id + if not json_payload: + raise ValueError( + "At least one parameter is required for /user_identities/grant_access_to_device" + ) + self.client.put("/user_identities/grant_access_to_device", json=json_payload) return None @@ -547,15 +540,16 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/list_accessible_devices" - ) params: Dict[str, Any] = {} if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/list_accessible_devices" + ) + res = self.client.get("/user_identities/list_accessible_devices", params=params) return [Device.from_dict(item) for item in res["devices"]] @@ -573,15 +567,16 @@ def list_accessible_entrances(self, *, user_identity_id: str) -> List[AcsEntranc :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/list_accessible_entrances" - ) params: Dict[str, Any] = {} if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/list_accessible_entrances" + ) + res = self.client.get( "/user_identities/list_accessible_entrances", params=params ) @@ -601,15 +596,16 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/list_acs_systems" - ) params: Dict[str, Any] = {} if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/list_acs_systems" + ) + res = self.client.get("/user_identities/list_acs_systems", params=params) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] @@ -627,15 +623,16 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/list_acs_users" - ) params: Dict[str, Any] = {} if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/list_acs_users" + ) + res = self.client.get("/user_identities/list_acs_users", params=params) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -653,10 +650,6 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: :param user_identity_id: ID of the user identity from which you want to remove an access system user. :raises ValueError: At least one parameter must be provided.""" - if not any([acs_user_id is not None, user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/remove_acs_user" - ) params: Dict[str, Any] = {} if acs_user_id is not None: @@ -664,6 +657,11 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/remove_acs_user" + ) + self.client.delete("/user_identities/remove_acs_user", params=params) return None @@ -681,10 +679,6 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N :param user_identity_id: ID of the user identity from which you want to revoke access to a device. :raises ValueError: At least one parameter must be provided.""" - if not any([device_id is not None, user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/revoke_access_to_device" - ) params: Dict[str, Any] = {} if device_id is not None: @@ -692,6 +686,11 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/revoke_access_to_device" + ) + self.client.delete("/user_identities/revoke_access_to_device", params=params) return None @@ -723,18 +722,6 @@ def update( :param user_identity_key: Unique key for the user identity. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - user_identity_id is not None, - email_address is not None, - full_name is not None, - phone_number is not None, - user_identity_key is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /user_identities/update" - ) json_payload: Dict[str, Any] = {} if user_identity_id is not None: @@ -748,6 +735,11 @@ def update( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key + if not json_payload: + raise ValueError( + "At least one parameter is required for /user_identities/update" + ) + self.client.patch("/user_identities/update", json=json_payload) return None diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index 6efe646c..31627ad2 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -80,15 +80,16 @@ def get(self, *, user_identity_id: str) -> UnmanagedUserIdentity: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([user_identity_id is not None]): - raise ValueError( - "At least one parameter is required for /user_identities/unmanaged/get" - ) params: Dict[str, Any] = {} if user_identity_id is not None: params["user_identity_id"] = user_identity_id + if not params: + raise ValueError( + "At least one parameter is required for /user_identities/unmanaged/get" + ) + res = self.client.get("/user_identities/unmanaged/get", params=params) return UnmanagedUserIdentity.from_dict(res["user_identity"]) @@ -157,16 +158,6 @@ def update( :param user_identity_key: Unique key for the user identity. If not provided, the existing key will be preserved. :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - is_managed is not None, - user_identity_id is not None, - user_identity_key is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /user_identities/unmanaged/update" - ) json_payload: Dict[str, Any] = {} if is_managed is not None: @@ -176,6 +167,11 @@ def update( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key + if not json_payload: + raise ValueError( + "At least one parameter is required for /user_identities/unmanaged/update" + ) + self.client.patch("/user_identities/unmanaged/update", json=json_payload) return None diff --git a/seam/routes/webhooks.py b/seam/routes/webhooks.py index a0429242..8aa22a82 100644 --- a/seam/routes/webhooks.py +++ b/seam/routes/webhooks.py @@ -77,8 +77,6 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([url is not None, event_types is not None]): - raise ValueError("At least one parameter is required for /webhooks/create") json_payload: Dict[str, Any] = {} if url is not None: @@ -86,6 +84,9 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo if event_types is not None: json_payload["event_types"] = event_types + if not json_payload: + raise ValueError("At least one parameter is required for /webhooks/create") + res = self.client.post("/webhooks/create", json=json_payload) return Webhook.from_dict(res["webhook"]) @@ -99,13 +100,14 @@ def delete(self, *, webhook_id: str) -> None: :param webhook_id: ID of the webhook that you want to delete. :raises ValueError: At least one parameter must be provided.""" - if not any([webhook_id is not None]): - raise ValueError("At least one parameter is required for /webhooks/delete") params: Dict[str, Any] = {} if webhook_id is not None: params["webhook_id"] = webhook_id + if not params: + raise ValueError("At least one parameter is required for /webhooks/delete") + self.client.delete("/webhooks/delete", params=params) return None @@ -121,13 +123,14 @@ def get(self, *, webhook_id: str) -> Webhook: :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any([webhook_id is not None]): - raise ValueError("At least one parameter is required for /webhooks/get") params: Dict[str, Any] = {} if webhook_id is not None: params["webhook_id"] = webhook_id + if not params: + raise ValueError("At least one parameter is required for /webhooks/get") + res = self.client.get("/webhooks/get", params=params) return Webhook.from_dict(res["webhook"]) @@ -156,8 +159,6 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: :param webhook_id: ID of the webhook that you want to update. :raises ValueError: At least one parameter must be provided.""" - if not any([event_types is not None, webhook_id is not None]): - raise ValueError("At least one parameter is required for /webhooks/update") json_payload: Dict[str, Any] = {} if event_types is not None: @@ -165,6 +166,9 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: if webhook_id is not None: json_payload["webhook_id"] = webhook_id + if not json_payload: + raise ValueError("At least one parameter is required for /webhooks/update") + self.client.put("/webhooks/update", json=json_payload) return None diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index b48bfc91..09bbb529 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -150,23 +150,6 @@ def create( :returns: OK :raises ValueError: At least one parameter must be provided.""" - if not any( - [ - name is not None, - company_name is not None, - connect_partner_name is not None, - connect_webview_customization is not None, - is_sandbox is not None, - organization_id is not None, - webview_logo_shape is not None, - webview_primary_button_color is not None, - webview_primary_button_text_color is not None, - webview_success_message is not None, - ] - ): - raise ValueError( - "At least one parameter is required for /workspaces/create" - ) json_payload: Dict[str, Any] = {} if name is not None: @@ -194,6 +177,11 @@ def create( if webview_success_message is not None: json_payload["webview_success_message"] = webview_success_message + if not json_payload: + raise ValueError( + "At least one parameter is required for /workspaces/create" + ) + res = self.client.post("/workspaces/create", json=json_payload) return Workspace.from_dict(res["workspace"]) From 08f57a6bf4b6553e82f819c6c0134f73f88836b2 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 07:40:07 +0000 Subject: [PATCH 27/39] 3.0.0b2 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b97ba651..ff027174 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seam" -version = "3.0.0b1" +version = "3.0.0b2" description = "SDK for the Seam API written in Python." authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" diff --git a/uv.lock b/uv.lock index 1d822959..9fbd15ed 100644 --- a/uv.lock +++ b/uv.lock @@ -817,7 +817,7 @@ wheels = [ [[package]] name = "seam" -version = "3.0.0b1" +version = "3.0.0b2" source = { editable = "." } dependencies = [ { name = "httpx" }, From 623287b1aa77d377d9a221302f09c2a58cc737c2 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 07:41:01 +0000 Subject: [PATCH 28/39] 3.0.0b1 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ff027174..b97ba651 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seam" -version = "3.0.0b2" +version = "3.0.0b1" description = "SDK for the Seam API written in Python." authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" diff --git a/uv.lock b/uv.lock index 9fbd15ed..1d822959 100644 --- a/uv.lock +++ b/uv.lock @@ -817,7 +817,7 @@ wheels = [ [[package]] name = "seam" -version = "3.0.0b2" +version = "3.0.0b1" source = { editable = "." } dependencies = [ { name = "httpx" }, From ff0b51830d2a74c55825c83a7ab1358bee7a09e4 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 01:10:17 -0700 Subject: [PATCH 29/39] Fix prerelease versioning by mirroring PEP 440 tags to semver (#610) semantic-release computes semver versions, but uv normalizes prerelease versions to PEP 440 when cutting them, so the tag it produces (v3.0.0b1) is not valid semver. semantic-release silently skips tags it cannot parse, so every run on a prerelease branch reported the channel's first prerelease as the next version and tried to cut a version that had already been published. Mirror each PEP 440 prerelease tag onto its semver equivalent before running semantic-release. The mirrored tags are local to the workflow run, so the PEP 440 tags stay the only real ones and continue to match the published package version. Recording the channel a prerelease went out on, which semantic-release requires before it will treat a prerelease tag as released, stays in the Version workflow where the release is cut. It now keys the note to the PEP 440 tag that actually gets created, rather than to a semver tag name that never exists, and derives the channel from the normalized version so a release cut by hand is recorded the same way. Notes attach to commits rather than tags, so the mirrored tag picks the channel up. Claude-Session: https://claude.ai/code/session_01WKRv7ht6XA9Sm8brKctEva Co-authored-by: Claude --- .github/workflows/semantic-release.yml | 25 +++++++++++++++++++++++++ .github/workflows/version.yml | 26 +++++++++++++++++--------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml index b2e3aab9..b6c2c920 100644 --- a/.github/workflows/semantic-release.yml +++ b/.github/workflows/semantic-release.yml @@ -25,6 +25,31 @@ jobs: uses: actions/checkout@v7 with: fetch-depth: 0 + - name: Mirror prerelease tags as semver + run: | + # Prerelease tags use PEP 440 (v3.0.0b1) to match the published + # package version, but semantic-release only understands semver and + # silently ignores tags it cannot parse. Without this, every run + # reports the first prerelease of the channel as the next version. + # Mirror each PEP 440 prerelease tag onto its semver equivalent + # (v3.0.0-beta.1). These are local to the run and are never pushed; + # the PEP 440 tags remain the only real ones. The channel notes the + # Version workflow records against the tagged commit apply to the + # mirrored tag too, since notes attach to commits, not tags. + for tag in $(git tag --list 'v*'); do + semver="$( + printf '%s' "${tag#v}" | sed -E \ + -e 's/^([0-9]+\.[0-9]+\.[0-9]+)a([0-9]+)$/\1-alpha.\2/' \ + -e 's/^([0-9]+\.[0-9]+\.[0-9]+)b([0-9]+)$/\1-beta.\2/' \ + -e 's/^([0-9]+\.[0-9]+\.[0-9]+)rc([0-9]+)$/\1-rc.\2/' + )" + if [ "$semver" = "${tag#v}" ]; then + continue + fi + + git tag --force "v$semver" "$tag^{commit}" + echo "Mirrored $tag as v$semver." + done - name: Semantic release id: release uses: cycjimmy/semantic-release-action@v6 diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index afb09b8c..723815d3 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -31,20 +31,28 @@ jobs: passphrase: ${{ secrets.GPG_PASSPHRASE }} - name: Setup uses: ./.github/actions/setup + # uv normalizes the semver version semantic-release computes + # (3.0.0-beta.1) to PEP 440 (3.0.0b1), which is what gets committed, + # tagged and published. The Semantic Release workflow mirrors that tag + # back to semver so it can pick up where the last prerelease left off. - name: Cut ${{ github.event.inputs.version }} version run: | uv version "${{ github.event.inputs.version }}" just version + # semantic-release only treats a prerelease tag as released when a note + # records the channel it went out on, so record it here, once the release + # exists. The note is keyed to the commit rather than the tag, which is + # what lets the mirrored semver tag pick it up. - name: Record prerelease channel - env: - VERSION: ${{ github.event.inputs.version }} run: | - case "$VERSION" in - *-*) channel="${VERSION#*-}"; channel="${channel%%.*}" ;; + tag="v$(uv version --short)" + case "$(uv version --short)" in + *.*.*a[0-9]*) channel=alpha ;; + *.*.*b[0-9]*) channel=beta ;; + *.*.*rc[0-9]*) channel=rc ;; *) echo "Stable release, no channel note required."; exit 0 ;; esac - git fetch origin "+refs/notes/semantic-release:refs/notes/semantic-release" || true - git notes --ref semantic-release add --force \ - --message "{\"channels\":[\"$channel\"]}" "v$VERSION^{commit}" - git push origin refs/notes/semantic-release - echo "Recorded v$VERSION on the '$channel' channel." + git notes --ref "semantic-release-$tag" add --force \ + --message "{\"channels\":[\"$channel\"]}" "$tag^{commit}" + git push origin "refs/notes/semantic-release-$tag" + echo "Recorded $tag on the '$channel' channel." From 5ebe62f7d073a6b0530e1b32f30dd67ac47ce1b8 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 08:10:49 +0000 Subject: [PATCH 30/39] 3.0.0b3 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b97ba651..8c0ecc31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seam" -version = "3.0.0b1" +version = "3.0.0b3" description = "SDK for the Seam API written in Python." authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" diff --git a/uv.lock b/uv.lock index 1d822959..2f13d4a7 100644 --- a/uv.lock +++ b/uv.lock @@ -817,7 +817,7 @@ wheels = [ [[package]] name = "seam" -version = "3.0.0b1" +version = "3.0.0b3" source = { editable = "." } dependencies = [ { name = "httpx" }, From a536e3a0040e8b827cbd191b0b36aaab2e862a60 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 09:05:11 -0700 Subject: [PATCH 31/39] build: update the development dependencies (#612) Raise each to its current major: - black 24.4.2 -> 26.5.1 - pylint 3.2.2 -> 4.0.7, with astroid 3.2.4 -> 4.0.4 - pytest 8.4.2 -> 9.1.1 - pytest-cov 5.0.0 -> 7.1.0 - mypy 1.19.1 -> 2.3.0 - rstcheck 6.2.1 -> 6.3.0 The runtime dependencies are unchanged. Two of these mattered rather than merely moving. black 26 drops a blank line in seam/route.py, and it leaves generated code alone, so codegen still writes what is committed. pylint 3.2 could not resolve collections.abc under Python 3.14, which the lint job runs; pylint 4 can. pytest 9 collects the same 87 tests as pytest 8, and neither pylint 4 nor mypy 2 reports anything new. Claude-Session: https://claude.ai/code/session_01PpqwDhZmyikGbjmCPqFW2A Co-authored-by: Claude --- pyproject.toml | 14 +-- seam/route.py | 1 - uv.lock | 248 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 194 insertions(+), 69 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8c0ecc31..7ea9de9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,14 +19,14 @@ Repository = "https://github.com/seamapi/python" [dependency-groups] dev = [ - "black>=24.3.0,<25", - "pylint>=3.1.0,<4", - "pytest>=8.1.1,<9", - "pytest-cov>=5.0.0,<6", - "pytest-runner>=6.0.0,<7", + "black>=26.5.1,<27", + "pylint>=4.0.7,<5", + "pytest>=9.1.1,<10", + "pytest-cov>=7.1.0,<8", + "pytest-runner>=6.0.1,<7", "pytest-watch>=4.2.0,<5", - "rstcheck>=6.1.2,<7", - "mypy>=1.17.0,<2", + "rstcheck>=6.3.0,<7", + "mypy>=2.3.0,<3", ] [build-system] diff --git a/seam/route.py b/seam/route.py index 3a3a4b50..59f71fb8 100644 --- a/seam/route.py +++ b/seam/route.py @@ -1,6 +1,5 @@ from typing import Any, Callable, TypeVar, cast - F = TypeVar("F", bound=Callable) diff --git a/uv.lock b/uv.lock index 2f13d4a7..94fafe37 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,8 @@ version = 1 revision = 3 requires-python = ">=3.11" resolution-markers = [ - "python_full_version >= '3.12'", + "python_full_version >= '3.15'", + "python_full_version >= '3.12' and python_full_version < '3.15'", "python_full_version < '3.12'", ] @@ -37,13 +38,77 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] +[[package]] +name = "ast-serialize" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/a9/11851c3e02a3fea2ddc9932d1fdc7d2edaeecc0d2e11bc5f2a7fde2b0934/ast_serialize-0.8.0.tar.gz", hash = "sha256:6c37c43e4004dfb42d321ddedc569dc17ff4259296f3af577c9ea46a809bc010", size = 845638, upload-time = "2026-08-07T11:29:02.152Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/16/6e520b57cd8c75914b38c670ad4593d13c22911e4306cc7165dab8b0789b/ast_serialize-0.8.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:3d822605fa7bb326ef868d25fafced7fc660fa46d9b90c02ea86d5e2f5d325f7", size = 863924, upload-time = "2026-08-07T11:27:34.579Z" }, + { url = "https://files.pythonhosted.org/packages/03/e1/48802de9b22a2bcad42ec80601a17e3f69172fe4f590e6311bcc2b323aeb/ast_serialize-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:2efa40b068197d5efb62655b43baadb842ed71c4958cccd3e8b86a35726f0119", size = 1177662, upload-time = "2026-08-07T11:27:36.196Z" }, + { url = "https://files.pythonhosted.org/packages/38/d4/323438db76bded3a1f3523a3167b8325916b2ddceb2107a330c6ec9fcf4d/ast_serialize-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:db1b957291bca08c7e72f43a12357b2948e20775d970e3fc3dac0aa3160ab725", size = 1167072, upload-time = "2026-08-07T11:27:37.646Z" }, + { url = "https://files.pythonhosted.org/packages/77/82/53c5400b54144b56de8ed7f957fd1ccd97e42482009292ab46121d15f8dd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdc0d5b18ff8fb364e87923e47c0a91d0d69dbcaeaa274591f7fd26892cc3a3a", size = 1225497, upload-time = "2026-08-07T11:27:39.225Z" }, + { url = "https://files.pythonhosted.org/packages/44/5f/36c07327a8b91303fbf1382c7c3e8a2902072dbe1b9546138a5288e75ff0/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9da7330f3e235bf7da89b8d39205c6350fc0c08a85379743f2df9fff87d6d980", size = 1227101, upload-time = "2026-08-07T11:27:40.799Z" }, + { url = "https://files.pythonhosted.org/packages/9d/48/5adf5c67addc7ddb328122208c6d375a84cf154984f412b4087330a157bd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3186969ee66a9863b00acc6523ace44c56974eecb348a7ea4b228d9f0b80e19", size = 1424001, upload-time = "2026-08-07T11:27:42.708Z" }, + { url = "https://files.pythonhosted.org/packages/38/a1/70074dd3869d2b0e934f91891d8d6b734361cd3b80f85ca7ece2e668ecdd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40a57b73731be45da4fa41430c4d5dc94a24b3a4faba7b9e069978c0402064ea", size = 1245545, upload-time = "2026-08-07T11:27:44.4Z" }, + { url = "https://files.pythonhosted.org/packages/e3/be/53b9c0a8a6399950c2e3546bdfab96d2b299d5b114b47eb94fd3c49c4054/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5075b9da3ef807eda752502446dfecea3b381c4900b7e27a5d5f4f899eb39951", size = 1248961, upload-time = "2026-08-07T11:27:45.781Z" }, + { url = "https://files.pythonhosted.org/packages/eb/13/3651d3812548a2bda15e26e5dd51aadb48cf682d0865370255fcf0e367dd/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:293cc1c5bfa741f8e3fbe8175b9c07beee487c9a6fdbb25a5acad9f1df2d30a9", size = 1243877, upload-time = "2026-08-07T11:27:47.325Z" }, + { url = "https://files.pythonhosted.org/packages/21/a0/521f0bf000f675e9312a4aae2c8ba7a992405d072a85c485e08fd59433b9/ast_serialize-0.8.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0910c3442a75216dde0f102d854ba2aaa71d2482e0ee213630b9bf29584fba3", size = 1293903, upload-time = "2026-08-07T11:27:49.264Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7e/402fc902568aa2ee65865a3e151f000db0153da8ce6b1be4c9c349025f8d/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43dd6d596879bb1cb8a12cc9dae7bb10090a39a35883026c24f82488a195619a", size = 1401070, upload-time = "2026-08-07T11:27:50.947Z" }, + { url = "https://files.pythonhosted.org/packages/ff/7c/97d4b66c057f1706fc8be6dd532cc77c988794357c8f4ffdb6adabb39562/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8c9d537f59e936392cfd3597789d1390304dd659efc3c486ce7f40fb6b8a9f53", size = 1502602, upload-time = "2026-08-07T11:27:52.364Z" }, + { url = "https://files.pythonhosted.org/packages/89/6f/72cc3b71562001bba46e898ccfbf1844f7939b3e28912736206102f2e5a8/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f0190a33d7f97c65e9069f7a7f40499eea6b5cbe260c558378109caf20ce934b", size = 1495848, upload-time = "2026-08-07T11:27:53.803Z" }, + { url = "https://files.pythonhosted.org/packages/a0/53/d6f629d1e49308b2f363dae028baa213ec222c9106fa1f7f0d1f7b41499a/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:77308ae6c5cf5264cc0f01a7c556ec77a9e68eb1f61b093534d698139fdc3b14", size = 1556556, upload-time = "2026-08-07T11:27:55.342Z" }, + { url = "https://files.pythonhosted.org/packages/ee/22/340f35dd8dfc6d412d53dc20699ca014b8d228db923e8ed4759c512b162c/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8d53a23f27e1ed3a36b2d26fd2a1a6228c8e85a1ed62ff7cdb44bd610769f20a", size = 1417822, upload-time = "2026-08-07T11:27:56.712Z" }, + { url = "https://files.pythonhosted.org/packages/11/29/6dde5c13fbebc051d3a6df4ec0a6fd1d5359333cc1193f7f609f3410b4d8/ast_serialize-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa5e7cb08f96fed9121f77b224151e41caf88feab9d652bb46c78202b6fbeda", size = 1445153, upload-time = "2026-08-07T11:27:58.275Z" }, + { url = "https://files.pythonhosted.org/packages/62/c5/f473a8ed030f7a0ca24b9849cca184677a50c053867a7b808c2e1289bbd3/ast_serialize-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:fa70ed4dea0bb18b30a1789c77baa701d0ef30c474f2ccabdea61e25623a8827", size = 1063711, upload-time = "2026-08-07T11:27:59.793Z" }, + { url = "https://files.pythonhosted.org/packages/23/63/39e171fcd38ca057c2e1979d5ee81ac7a3502784abe3d83df7454f7a0978/ast_serialize-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d8b3c8eee4c1baef9d4e84d2a59a805501617127be42615cb48970b15b0892b6", size = 1103740, upload-time = "2026-08-07T11:28:01.405Z" }, + { url = "https://files.pythonhosted.org/packages/21/1c/d00762b399e7726d68d0a088cc946e3a4c60f1c6176f557608f672f627f3/ast_serialize-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:ac4f0a83c55a9b782f79ad55a5247b7db123c1db405959791c2ef886e9710c9f", size = 1076021, upload-time = "2026-08-07T11:28:02.947Z" }, + { url = "https://files.pythonhosted.org/packages/4c/11/911210c3c78923273a9211a2b6cfc4c8aa723b30dab3e1c8d19afb983b40/ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:86b8a1e6d90467345356098b040150e82fbc26d24a7a202224b13dc1f6264ca0", size = 1177715, upload-time = "2026-08-07T11:28:04.654Z" }, + { url = "https://files.pythonhosted.org/packages/77/89/6282881c8587606638db153cbe21e1e0c4d1f3970dee1aa0610a1c62a026/ast_serialize-0.8.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:39e92ff8e8cb45947fe9007174b2950e1fb098e6abd00266a13cd3bcf6675068", size = 1169347, upload-time = "2026-08-07T11:28:06.1Z" }, + { url = "https://files.pythonhosted.org/packages/97/78/a9f846a03a340ff3728c915f23338ca742742f3292700559cdb3ad999b1e/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85d8d18db5b2dfcb3b7e38a4d600ca35504c0ed8a6f75cd1c811e4ffe248a15", size = 1225916, upload-time = "2026-08-07T11:28:07.654Z" }, + { url = "https://files.pythonhosted.org/packages/c0/15/aba6ef8a988a6eceb6f0359589aac509e29ae2dba67fd9bfd5af0c3f13e7/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9830ff7e764f74d9eefb01170c61a9f0fd2c027dac5fcb72e064decd57d56371", size = 1227135, upload-time = "2026-08-07T11:28:09.504Z" }, + { url = "https://files.pythonhosted.org/packages/94/29/3f63d696ea7c5b8abadcecc3505be51bd900daaccc522ed8322fa5b05a93/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6479d9722a4cd21b578f5478074c41e6169f04811996ec881655560f703a5bba", size = 1425040, upload-time = "2026-08-07T11:28:11.044Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5d/0aac338604ff59df5774d4304307898982252f325ff7cafe31d52fedcb65/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a63bed264e818cd83eec11feed0f50aa162542b91132ef58afebc857182763a5", size = 1246278, upload-time = "2026-08-07T11:28:12.519Z" }, + { url = "https://files.pythonhosted.org/packages/23/ca/9f1ef795bb724719532bd86dbec11e5b66857d3fbe9b6772baec0191a6ed/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d187197d234aa45d6cfa2b096be5f666e8cc2e7eb3722d0ab8926293cf5720c", size = 1250029, upload-time = "2026-08-07T11:28:13.896Z" }, + { url = "https://files.pythonhosted.org/packages/dc/25/5e061372d2ed953b9ba3b9c4f73de3b8e9234cda3f6c088db4686801d0e1/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:2d39a56282cfcc0d8eeea37267c754be59c98d48505c23b1dae5c6011f3813dd", size = 1243575, upload-time = "2026-08-07T11:28:15.37Z" }, + { url = "https://files.pythonhosted.org/packages/a8/c1/ae7da218053120635a4ca802366c69f707203641af95372eeb83f70dfd52/ast_serialize-0.8.0-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7cc5f10386994c0f4844f1e6d6a97127e9b478660eb6dec2b257644f0acab64", size = 1294396, upload-time = "2026-08-07T11:28:16.813Z" }, + { url = "https://files.pythonhosted.org/packages/2e/89/271d1f49c5269fcddcc789ea3f25be401f6723fc1138aeda539f4d05516d/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6102f2f985c2e542be85cd857678ec9356fefa792b93cadfadd31139f5696f27", size = 1401987, upload-time = "2026-08-07T11:28:18.333Z" }, + { url = "https://files.pythonhosted.org/packages/55/be/4e7d77fcf571ac7cb5cf7115a20c36642bd7d29473b45dfaaefeb9618f90/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:3a8660fe66667b76a6e9dccd1d33e66b229fde3b308db991c041609226c005b6", size = 1502904, upload-time = "2026-08-07T11:28:20.039Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ae/ed1de2db7e019d4236fbc164ffa5ef9a6022a300a342bbf142d21b7c141e/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:e7266307e5fba39836edb79def8608887af48820508bff3c5f2941e1e04d1534", size = 1496967, upload-time = "2026-08-07T11:28:21.734Z" }, + { url = "https://files.pythonhosted.org/packages/92/89/5fea507fae5c5f18b7dc7f95e5c00956574b8c717b8fd2049c504fab0b18/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca7e6fd1ad845d1cc649dc2ecd499db2f8f46af5bf8da7b70dd858774cc038b", size = 1559041, upload-time = "2026-08-07T11:28:23.194Z" }, + { url = "https://files.pythonhosted.org/packages/42/71/478d69df21b64e064554a68134c94be304270316ca676a94e63c389a636a/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:2880350b13d3eae69a0d70bc1fb6c9bfaca4dbd0e20ba8cd1aa483080b56ff06", size = 1417367, upload-time = "2026-08-07T11:28:24.601Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2d/8962dc8d5b3a9dc27b36f9db199afa25264c741505469d9ec10ffbfd2ba7/ast_serialize-0.8.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:ab0f9a59f7d63d0d441b56b9a818b273705264352d5115cfee12e940e816d958", size = 1446178, upload-time = "2026-08-07T11:28:26.152Z" }, + { url = "https://files.pythonhosted.org/packages/4f/22/14d2ad4fd1d1bcd0dc687ca268e0630069f45162496260c0efb70ee0ea72/ast_serialize-0.8.0-cp315-abi3.abi3t-win32.whl", hash = "sha256:0485a25ef519c62e749ee3c1ad8070e591b380d67226349eb5a70b228dc1ac4a", size = 1063811, upload-time = "2026-08-07T11:28:27.864Z" }, + { url = "https://files.pythonhosted.org/packages/18/1d/84a327c0202a41aa5fdba3ade33904d6d8f3b9e6806fa83568d835395850/ast_serialize-0.8.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:bd84d60bca7079e741be4ac5dbe237751a59d7f6f9f0126b11880d63822cbe16", size = 1105518, upload-time = "2026-08-07T11:28:29.691Z" }, + { url = "https://files.pythonhosted.org/packages/8c/92/74556dec52fde85a2ad84ed159991b916241043788609c15d8b77e14570b/ast_serialize-0.8.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:057769b5921336eb2d9124f2a731b42ed05ffdac559b840dbdf6f3937cf153dc", size = 1076319, upload-time = "2026-08-07T11:28:31.282Z" }, + { url = "https://files.pythonhosted.org/packages/d1/5d/c650b1f2cc1e75193358da95a080261422e8cd10b66d7370b1688c9915c5/ast_serialize-0.8.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:a02cbed7d8bfdcdee88edaac12bd50d53d9953aaa2e1852ef078625be5f1c0b5", size = 852914, upload-time = "2026-08-07T11:28:32.929Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e3/6142e920fec6ef7bccabd8c24ed8ed99f8bdc6cb8b065e1df7c6a3b2d667/ast_serialize-0.8.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e1bd223df0f6c96b396975fa604cb33bce53d9b4a0185490be4c4a289f7c9c87", size = 1184007, upload-time = "2026-08-07T11:28:34.654Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e9/6e8be8df02b35d85e2b8809f7f1cfa290bdf5882b55127a539d049482db0/ast_serialize-0.8.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ddd3b61f45c132da66c5476b281891e08c1fd87fbdabe8a6973e1622efc85f06", size = 1177588, upload-time = "2026-08-07T11:28:36.318Z" }, + { url = "https://files.pythonhosted.org/packages/8c/80/7e0fd2e2e2aba257820db4a8657c4c356844d36b914b20a4af294bcfb902/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f9caa63fad8241257ae401b5ff0a64026c6adb36b8e86cbe8782d9ea505daf6", size = 1234575, upload-time = "2026-08-07T11:28:37.772Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/3bae0af06f9b1bae3001c44d64215f5b567877e7aae9ffd45db11c3a7647/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3926fa117b5e65019853a2969966d11c7175af377a3425991f3fe73784412405", size = 1236015, upload-time = "2026-08-07T11:28:39.14Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c4/ce2d41a1bc22508e82618901f7e10f2a5e2f9556553fea90624daf9875e2/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:485f1113af805e9e170b95ef993ca3fbd4f89c04bab25c58b4fc632d854801ab", size = 1432808, upload-time = "2026-08-07T11:28:40.664Z" }, + { url = "https://files.pythonhosted.org/packages/1a/90/f5058f209756dd70e958b7538aaa82d25d24944baf9ec8ae6f27b06fcacc/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccebbed24f1281062d5852353c72c47502955926cfcb8345ffb3a44d87ff3d3", size = 1256251, upload-time = "2026-08-07T11:28:42.223Z" }, + { url = "https://files.pythonhosted.org/packages/bf/32/7f77ea87fa0836daab706ed5cb7f903bb25fa26a77439011aee626af11d8/ast_serialize-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:252f883290d1cdb728eb7fe1d9a7221b88af5a329aae0bc91ddee4dafb820331", size = 1258574, upload-time = "2026-08-07T11:28:43.751Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5a/75b82ad2725b5e8e8c742732f9e76c6738a292d0709e1f60d10a973730b4/ast_serialize-0.8.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:96abc072ad29db8d02194afd47d68987322622787daceae82398d7b69f3ba2e6", size = 1254075, upload-time = "2026-08-07T11:28:45.28Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/8c20ed4eea805516a3fd23dd4a721ce28c64f50f0e4b359969f60a8c97a6/ast_serialize-0.8.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9118ad3e369727060b2696fc4078f250ecffca4248ba87f537f55cea9f9dce06", size = 1301018, upload-time = "2026-08-07T11:28:46.851Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5b/9f14430f12fe830b656fb38f8e2e05ee13b02a88967660bef46af0ab22a8/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f359df4bd921918af8bebd142a376c77511d7151cc8ba852760b587b5a4a54f3", size = 1409951, upload-time = "2026-08-07T11:28:48.312Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3d/084882eca93c842bd4262591a071ec7f825340644035e51501208cc5a8d4/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e94f9121d13fa36cbf21314783c77d05ae3a0868decd18cf5233fdcc6de49ac8", size = 1509544, upload-time = "2026-08-07T11:28:49.847Z" }, + { url = "https://files.pythonhosted.org/packages/ce/73/ea84852096c2036c61cc0b2f97b90242207419f534dc671060ee1c8e05cb/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:54f95b486018d262bcb387a9afd96f0da74508b442762b80c769454a6fbb3ee3", size = 1505671, upload-time = "2026-08-07T11:28:51.239Z" }, + { url = "https://files.pythonhosted.org/packages/cb/88/287b9a5300c1f2f651d259f670931b63110adc265b7613c885b44c5bc53d/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c38b915511e32bc718c49dbce98ff9af36bac0ad6a604f58000cd5e3aecdba7", size = 1563685, upload-time = "2026-08-07T11:28:53.112Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f3/1bc3a79afcf0c2a8d2c37182d0d659d1545a9d7f7f6dc9cf3e63d6c17135/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:9a2ef9cf12f2de4f1028c42c1dd7d775255e0fb3e5bb48896c97e35ef52366fe", size = 1427977, upload-time = "2026-08-07T11:28:54.418Z" }, + { url = "https://files.pythonhosted.org/packages/5c/cd/440c798957e14e31776bfeb024d8fafe0bb1d5b89c51c2f067e69938f7b0/ast_serialize-0.8.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f18048fe9f6dd266bd577cdec48bdcecb74faaa01fe941324435483b013ed2a", size = 1454335, upload-time = "2026-08-07T11:28:55.968Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4a/587eb36dcc240a54c8660f599464516b469ecad96f0dbdb6bccbedb50745/ast_serialize-0.8.0-cp39-abi3-win32.whl", hash = "sha256:31883542dd6c94d178f5db3d32fbd69c5eb88b3a7c018e7ac8cc0c45195ddbed", size = 1068858, upload-time = "2026-08-07T11:28:57.541Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a4/3e887bbd92164e183cb6e412c6a3e9198ddd446d7fe405958293ef5ef49c/ast_serialize-0.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:861794565b06337005c1447ef23103a3d5a627d08bdc827870d00d0b28ef5f51", size = 1111839, upload-time = "2026-08-07T11:28:59Z" }, + { url = "https://files.pythonhosted.org/packages/25/6c/b400476d3ceba681ab929787edc9554f6d88fcc69435eb681b00fc0457a5/ast_serialize-0.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:b2a5978662fd4db463dfb4b974d2b10ac6430b98f5333aabc7051909df3561d0", size = 1083655, upload-time = "2026-08-07T11:29:00.349Z" }, +] + [[package]] name = "astroid" -version = "3.2.4" +version = "4.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576, upload-time = "2024-07-20T12:57:43.26Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348, upload-time = "2024-07-20T12:57:40.886Z" }, + { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, ] [[package]] @@ -57,7 +122,7 @@ wheels = [ [[package]] name = "black" -version = "24.4.2" +version = "26.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -65,18 +130,31 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/47/c9997eb470a7f48f7aaddd3d9a828244a2e4199569e38128715c48059ac1/black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d", size = 642299, upload-time = "2024-04-26T00:32:15.305Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/f7/591d601c3046ceb65b97291dfe87fa25124cffac3d97aaaba89d0f0d7bdf/black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474", size = 1615013, upload-time = "2024-04-26T00:39:49.415Z" }, - { url = "https://files.pythonhosted.org/packages/c9/17/5e0036b265bbf6bc44970d93d48febcbc03701b671db3c9603fd43ebc616/black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c", size = 1436163, upload-time = "2024-04-26T00:40:20.267Z" }, - { url = "https://files.pythonhosted.org/packages/c5/48/34176b522e8cff4620a5d96c2e323ff2413f574870eb25efa8025885e028/black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb", size = 1803382, upload-time = "2024-04-26T00:34:38.665Z" }, - { url = "https://files.pythonhosted.org/packages/74/ce/e8eec1a77edbfa982bee3b5460dcdd4fe0e4e3165fc15d8ec44d04da7776/black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1", size = 1417802, upload-time = "2024-04-26T00:35:08.804Z" }, - { url = "https://files.pythonhosted.org/packages/f4/75/3a29de3bda4006cc280d833b5d961cf7df3810a21f49e7a63a7e551fb351/black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d", size = 1645176, upload-time = "2024-04-26T00:42:35.606Z" }, - { url = "https://files.pythonhosted.org/packages/be/b8/9c152301774fa62a265b035a8ede4d6280827904ea1af8c3be10a28d3187/black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04", size = 1446227, upload-time = "2024-04-26T00:40:35.195Z" }, - { url = "https://files.pythonhosted.org/packages/25/6d/eb15a1b155f755f43766cc473618c6e1de6555d6a1764965643f486dcf01/black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc", size = 1832011, upload-time = "2024-04-26T00:34:37.825Z" }, - { url = "https://files.pythonhosted.org/packages/43/24/942b22571b0171be7c6f701cdc3e3b7221f5b522ef02cf82503a547a657b/black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0", size = 1428800, upload-time = "2024-04-26T00:35:55.838Z" }, - { url = "https://files.pythonhosted.org/packages/0f/89/294c9a6b6c75a08da55e9d05321d0707e9418735e3062b12ef0f54c33474/black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c", size = 205925, upload-time = "2024-04-26T00:32:12.495Z" }, + { url = "https://files.pythonhosted.org/packages/4b/96/3c3e09f09f44a37aac36b178a279cd19aa7001bd796187a7b162a294c81f/black-26.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96ae2c733b2aabdd9986e2c5df628ff3473676cd1c5faded1ff496cf6d74083c", size = 1970639, upload-time = "2026-05-18T17:05:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/83/ea/5ad117b9ee3ecd933c712bcbae610006e5b7cc9f41c526cd7ed3b6c4124c/black-26.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e48b87e03bf109288e55cfceadcfa15ff5470aca2851a851950ed2926f450d7", size = 1792130, upload-time = "2026-05-18T17:05:12.983Z" }, + { url = "https://files.pythonhosted.org/packages/06/3a/7c448bc623fcdfa96672531beb5a616ea5e64f6975955254d7731ffb0ad9/black-26.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5119fa92ae61f786e8c3662fd60aece1d0a2dd5cca5d0c79417a95e7a4272a59", size = 1846134, upload-time = "2026-05-18T17:05:14.506Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5b/0b39b3a5917f0657ac014ad2edb58c139553a478adfe7f817abf1622ff6e/black-26.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:30d3c14661f2792e9142cce3eeeb1cbc175b3eb5f733be0c8eeb99651e52b0c3", size = 1478883, upload-time = "2026-05-18T17:05:16.542Z" }, + { url = "https://files.pythonhosted.org/packages/4c/48/dc222692e0f95030db1bbfb6c857e76858bad09058221ea7aae815255327/black-26.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:1ef92b76f7733f282fd096ea406200b5a286c42947412b0eaff3a74e3616cefe", size = 1277776, upload-time = "2026-05-18T17:05:18.029Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/7744b906703228264ef73bdd534df88ec1ef3de45c4e78f6d31b9e32d0c9/black-26.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ad6fa01f941920f54f2bbb35f3df7673428a0ef98a0b0840c2eaef3b110efa8", size = 2012518, upload-time = "2026-05-18T17:05:20.108Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c0/c5a3b1636dfd09c42534f2b3cf33506814f6d3e066fb0879ffa16c1ae860/black-26.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3915f256e75a2d7cf88d8953d37f780455dc586cc72dee059c528fe77f581217", size = 1816016, upload-time = "2026-05-18T17:05:21.84Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0e/36044316b65ca471d3bb6d3703fd06fb50c6b727c3562f6a5a3153634f88/black-26.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d98d4137277c75dfb898ec8d846c4fd68ba1e9cf77f95e2865c203dc18f4c3d", size = 1884150, upload-time = "2026-05-18T17:05:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/b3/33/dafc5808c2af43672912111d7c3354af1615f7e2be3bed7a878461abbe4d/black-26.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:a1dca32d9f1784af512a13410ec204c6f7f0aa9797a111c42e1c03449821c264", size = 1486825, upload-time = "2026-05-18T17:05:25.004Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/b965ee6ad2a311f28bdbf692def3ee9848d2ae289dab28b27657fcee3e78/black-26.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1037d5ac7b7b310b2632ad867ec8d0e4c4819dcdb0b820f63135da746a24e418", size = 1288646, upload-time = "2026-05-18T17:05:26.477Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020, upload-time = "2026-05-18T17:05:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335, upload-time = "2026-05-18T17:05:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614, upload-time = "2026-05-18T17:05:32.718Z" }, + { url = "https://files.pythonhosted.org/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925, upload-time = "2026-05-18T17:05:34.259Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883, upload-time = "2026-05-18T17:05:36.019Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, ] [[package]] @@ -488,41 +566,54 @@ wheels = [ [[package]] name = "mypy" -version = "1.19.1" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "ast-serialize" }, { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, { name = "pathspec" }, { 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" } +sdist = { url = "https://files.pythonhosted.org/packages/12/af/4e516a05d3ca2eb9283e9ec45b2c02225c1514dd6da49fd3c9eaa6639370/mypy-2.3.0.tar.gz", hash = "sha256:465965d41cd9a2726694e983e8ce7113259327bec798115d1e1dfa2a52fb666e", size = 3988104, upload-time = "2026-07-13T11:34:53.387Z" } wheels = [ - { 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" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, - { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, - { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b9/d75b3082b05f1b3028828aeb18e74ae5ab0a0936051bbf1f32f59f654747/mypy-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3419d00717afbc5265b50dd14b1278f29ea4884dd398ab67873489ac093fd329", size = 14838725, upload-time = "2026-07-13T11:32:44.655Z" }, + { url = "https://files.pythonhosted.org/packages/a9/50/79a65c6ea6e115bc73296038a4543b2d5c91f07912b918a2c616a2514bba/mypy-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cfca8ee88544090f86b6dcce05ec55d66eb48a762412ac2507810ba4bd793b6f", size = 13911128, upload-time = "2026-07-13T11:32:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/90/48/e11ed7716c26953ca321f726e452e374dbf81a6f2b8b212ec02af29b6b8f/mypy-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75cbb4b9ef04a0c84a957f07abc4504fbf64b8dcc145675101f2d3a78a4b1d6a", size = 14146742, upload-time = "2026-07-13T11:33:03.313Z" }, + { url = "https://files.pythonhosted.org/packages/06/72/6807565b1c4861ef66f7fdd98b51c61556356eab80235717b46c53bb8627/mypy-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:982e3d53dd23d0a4cef67dd66791fdbede0cf38f9eb617bf47663554c51e1e36", size = 15081418, upload-time = "2026-07-13T11:31:13.899Z" }, + { url = "https://files.pythonhosted.org/packages/00/80/1ea14c5d80e589e415973db3e47c78c2219a305b808b2b506395342c1d79/mypy-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85c5385b93012ffa3b31479ab579aef5415f4f3a32c6cf1ae07a984d2a0ff461", size = 15328164, upload-time = "2026-07-13T11:31:35.723Z" }, + { url = "https://files.pythonhosted.org/packages/37/28/8223157404a3d51920078459c37f80fbdc590e1d8ea049dc5ce48643022a/mypy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:13b1b16e2fa39f3b2e33fb1c468abc7a69369fa2e886b4b87b5afc81472325cd", size = 11136472, upload-time = "2026-07-13T11:27:37.018Z" }, + { url = "https://files.pythonhosted.org/packages/6f/cc/ea27e5959c5f258585a756b252031f3b313583d81b5064b2bebc41d3706b/mypy-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:b5cd2f027a972a4a5f2278a11fac9747f5f81a53a30b714d74950b6807e55568", size = 10135800, upload-time = "2026-07-13T11:30:08.92Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/0e7e592619e2133596a47cdd642534b0456545c218430bd3b9d8fefdd1b1/mypy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d53fc67b9d28a43c6199077f49fea0f05839e36cf6158500331c9549225e5a5", size = 15026523, upload-time = "2026-07-13T11:34:49.206Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/1e1731df090a857df2807177a4626863e5ac0f0256513c35780efe53986f/mypy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fbc00cee7bdbb9291979ddc9d08034a29dfcda4932628c9bbc28c1edd589df0c", size = 14032189, upload-time = "2026-07-13T11:33:57.168Z" }, + { url = "https://files.pythonhosted.org/packages/44/95/cab921f4a806e171f34113e6181dd23c55358ccf6a80741269ef594a410e/mypy-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04e617030eca5221909c8b7d8d7fd1c637948199aa2100b2ad9813feb07e1491", size = 14198696, upload-time = "2026-07-13T11:32:12.767Z" }, + { url = "https://files.pythonhosted.org/packages/66/80/e6d008bb19fe446e3662d85e0e2717bf9f2d611a2164fb29d6e067dbf46c/mypy-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56c184d2c20ca6b6378d58d1960270a767f41f5e44acbbd27f05effef4f4e1d7", size = 15286904, upload-time = "2026-07-13T11:34:27.594Z" }, + { url = "https://files.pythonhosted.org/packages/db/83/94397c9293608a364aa03e8084fb34ede4ae976a260384b9b52929308135/mypy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3961a4a34b05f7c74b0f05aa51fbfe99a2d1e126038df40318d15c8f558b7ef3", size = 15528342, upload-time = "2026-07-13T11:34:07.819Z" }, + { url = "https://files.pythonhosted.org/packages/cf/96/d8b37d819adec6cfccfb1fd3afc1735d94717ddeafb45536db9c6943e09b/mypy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1942b9314d4c784b8ea1dbab4972603290e5dd5630f06675f13aec97526bc4c", size = 11218346, upload-time = "2026-07-13T11:28:27.745Z" }, + { url = "https://files.pythonhosted.org/packages/2b/cd/cd9f725b19b19e5b530a154cf9bcf9e94279c5d55b3c34fb42b3aa48ea1b/mypy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:be51653d7669d7d7955d613b8d0bb57d5b652eaf71a873ddf65ac87254dd2595", size = 10204525, upload-time = "2026-07-13T11:31:02.552Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ae/f7d056eb0294586a572d0d0d89580ec633c064db520f11d37d5a2fb833bd/mypy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:91ad22a52ae2c7e621c2f67c94d5a17f66b3209a4cff5cf8a573579835c69e97", size = 14947298, upload-time = "2026-07-13T11:27:47.734Z" }, + { url = "https://files.pythonhosted.org/packages/32/d5/db3e7af01e7844d21662c6ddc1f7825ec7cb4053f0391ac02faf3638396f/mypy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99ac767cc5d3b64c8d0ae226ead10c96694f94e4e7da1668642225dcd4e75aac", size = 13950768, upload-time = "2026-07-13T11:27:57.726Z" }, + { url = "https://files.pythonhosted.org/packages/d9/fb/43c031f0190513d1ec248ed037eceb742ddd2a4d74bbf406658a28173837/mypy-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6d2c484742a4d7b0ed6d07b143375624d3b899c5749c7b3c947f56261f48a6", size = 14151586, upload-time = "2026-07-13T11:29:18.615Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c3/f8b2ffc60883084da91be51af58e88a7ffd4ff9795acb7d902ff88d31eb1/mypy-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7da939dd335cfd2ad788bdfd081c9f4e47634ab995e5a45eb15fd1e5bc052f8b", size = 15227411, upload-time = "2026-07-13T11:30:29.904Z" }, + { url = "https://files.pythonhosted.org/packages/83/2e/16b917fc7adcf03f1aadddfc93aab804ffb234b1ab09c0ffd6d92a5d34a2/mypy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7247eb2824f996722a949530183394921ca71deb9680052a338cf53cff7925c2", size = 15478790, upload-time = "2026-07-13T11:33:14.686Z" }, + { url = "https://files.pythonhosted.org/packages/c0/88/aaa65a93c73d0cdae7e42f8adb302bf6885bb281302084f99d0290a35347/mypy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:75b0984bb3cbd76bb5c9291a8671f7ae66ca3b51c7584c358fc2e923259f0757", size = 11234919, upload-time = "2026-07-13T11:33:39.28Z" }, + { url = "https://files.pythonhosted.org/packages/35/19/b40de63f1a80e63bc2d40f0679a6a8dbd34e95176c8122119bdf406aa552/mypy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:d78fcf900b59cb7e82cb7e3a235e31b462d9333d92285bd1e4952d355b8ffba1", size = 10201510, upload-time = "2026-07-13T11:31:52.619Z" }, + { url = "https://files.pythonhosted.org/packages/a4/58/fa0ae047da911f540284009b4f44b96fe09d83c076d7c103e9d645f46303/mypy-2.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ea317b060ce83e26050f8f9e4d7d6bf44ed7597c8ff9990bccffbb9d1d8522db", size = 14941909, upload-time = "2026-07-13T11:32:34.332Z" }, + { url = "https://files.pythonhosted.org/packages/15/14/2ba1d61452d7c2a7fe12741e8d374e52b183476b07aa7f9e2a0d02b0720a/mypy-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:094af99f92638aa92852326188b85a89e50f4a472f44827c03362228482f0762", size = 13967581, upload-time = "2026-07-13T11:30:00.587Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5a/483fb9e5ffbbb1a28dccc7b0a13d141b17ac769b6c9f488c0a0c63698962/mypy-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de121747278144fc9ae7caa2e978cf5df12aebc82933182f5b3b86081a30baef", size = 14168807, upload-time = "2026-07-13T11:28:48.6Z" }, + { url = "https://files.pythonhosted.org/packages/ae/77/70d7a10732063beb74ad713682cf871e88f5c5fa39bfc8beff8a524bf9cb/mypy-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37fa4de896a84e2dc9200d91e614c22563b43d1a266789d4bbac7b22ebe6192b", size = 15200144, upload-time = "2026-07-13T11:31:25.283Z" }, + { url = "https://files.pythonhosted.org/packages/56/72/766218ac783be4fdfcd699b90037b63017348a3e86fb2c1fbfb18302637d/mypy-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f1b3a98dfd21058bc759bb3337d5d1f61d0fdf9f3cf9c00f4291790fb5427bff", size = 15460389, upload-time = "2026-07-13T11:29:29.077Z" }, + { url = "https://files.pythonhosted.org/packages/38/4e/8a9db7411ecb8ec0cb1fd05dba432f28bafffcd38b4e887714a4a0506689/mypy-2.3.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:944c665d984157cb96a679dfb7a4a81dd1d36b24b9c284b699514e6e626b82d4", size = 7753664, upload-time = "2026-07-13T11:29:08.147Z" }, + { url = "https://files.pythonhosted.org/packages/65/4c/c3f8bfd6ed0e5e38b5a244403b27f821d433443df5a15a278417c10a3a3c/mypy-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:4359424140d985192c778c1ce2c114a10c1ca58a381ed79cfa70d37df94b299f", size = 11417237, upload-time = "2026-07-13T11:33:47.467Z" }, + { url = "https://files.pythonhosted.org/packages/3c/00/89a32eaf5ccf174bc4f90db0eaea5d70636c01b8d49f384bdab2e8834390/mypy-2.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:3dd0bed92c4bdec57c42505b96416fb9e6a5aa7be84d2809bcd5f2ecec2860d7", size = 10389252, upload-time = "2026-07-13T11:31:43.81Z" }, + { url = "https://files.pythonhosted.org/packages/31/56/104f93d69aa9f339b6b9d3b0a7faa699b8b466c942cf3ae86cc2a2ec0915/mypy-2.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:691fdc37132b1ae628d834f672e74de83462d9fb4aff621835767fb43a8dd373", size = 16385495, upload-time = "2026-07-13T11:29:49.818Z" }, + { url = "https://files.pythonhosted.org/packages/d2/03/f1d2123313f55efafdd27706960f43a771c62f1b68426c76043f3ab9ebf3/mypy-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:aec15d465d477558fd842757b487849007311cf3897849cdda0e3162ac0ac556", size = 15098155, upload-time = "2026-07-13T11:30:40.301Z" }, + { url = "https://files.pythonhosted.org/packages/e5/5d/d5f9200399b445e81726c4f23becee33f233aee81c72680b1ef3a258b641/mypy-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b352b7e49f5e6576009e8df730e1ff4f915cb565b851b396d2ffe2f5a6f5da88", size = 15514155, upload-time = "2026-07-13T11:34:38.569Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ce/69977c555f08faa3190cfde44189b89dbd56861b1ab97aa18fc5f3a2e4a3/mypy-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c6c6bf687b17f90dbfcad95b960d32eaa0154c00da45f03ab50bf8952e047fe", size = 16766351, upload-time = "2026-07-13T11:33:29.195Z" }, + { url = "https://files.pythonhosted.org/packages/bc/92/6648b6caa3ab9e00f9ac0c2a78307805f873dd48139b24a6f6f7c3667bbf/mypy-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f4ed18f111bfe2d599bca7468e7f9251042c1c2118f762c8de2766a56d773c60", size = 17043490, upload-time = "2026-07-13T11:30:53.927Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ab/0dc91d80f3f016634c68d451f294a97320fe903a9b6f90b9e57b3f7f1717/mypy-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0b025a93cffb9781d231f232be07a17912f35f10a313c24f301c81e842870654", size = 12146869, upload-time = "2026-07-13T11:29:38.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/b5/4c964d02634ba81f4d1c84838e5c5b18ab06d13ed568960f5d6318495ccc/mypy-2.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:adebc76aab4f3495a88b41d48aa4aff0c03f2822501da76625afcca5975f19e5", size = 10965113, upload-time = "2026-07-13T11:28:07.056Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fa/fdc54fe583ba3cafbcedfb70eeeaf03849f75b1827a07096c7bd996f582d/mypy-2.3.0-py3-none-any.whl", hash = "sha256:6b1cdb579446b60432432b2b2403a6201b4b475a004d7f488511c9ba177c9e88", size = 2753292, upload-time = "2026-07-13T11:33:18.48Z" }, ] [[package]] @@ -698,7 +789,7 @@ wheels = [ [[package]] name = "pylint" -version = "3.2.2" +version = "4.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astroid" }, @@ -709,14 +800,14 @@ dependencies = [ { name = "platformdirs" }, { name = "tomlkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/4c/b561478a1ccb91e9b02965cb999d2281894d43e68c0bf3777d023af15f11/pylint-3.2.2.tar.gz", hash = "sha256:d068ca1dfd735fb92a07d33cb8f288adc0f6bc1287a139ca2425366f7cbe38f8", size = 1505895, upload-time = "2024-05-20T07:22:43.252Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/92/98dace02f2d11b88160354c53944f77ea7327aa78bce1c75971e7aaa4347/pylint-4.0.7.tar.gz", hash = "sha256:9b2d1d15791c84b77a4fe2aafe8f0d9570717e2dea06d53b19c105cf60275a52", size = 1594770, upload-time = "2026-08-09T19:13:23.289Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/23/7a546224d2931cda031ee3cddc9e723650ad8e491d7c64efbab97e43e16d/pylint-3.2.2-py3-none-any.whl", hash = "sha256:3f8788ab20bb8383e06dd2233e50f8e08949cfd9574804564803441a4946eab4", size = 519092, upload-time = "2024-05-20T07:22:40.191Z" }, + { url = "https://files.pythonhosted.org/packages/e0/b0/3a8040e53df6c5c1e04b0e23ed53fdbeb64f333723a334d313fba2f581ce/pylint-4.0.7-py3-none-any.whl", hash = "sha256:be4a3111557a614411ed1fc89347ce4a8e1013a59e1f33d11485227a02e3304d", size = 539710, upload-time = "2026-08-09T19:13:21.228Z" }, ] [[package]] name = "pytest" -version = "8.4.2" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -725,22 +816,23 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] name = "pytest-cov" -version = "5.0.0" +version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/67/00efc8d11b630c56f15f4ad9c7f9223f1e5ec275aaae3fa9118c6a223ad2/pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857", size = 63042, upload-time = "2024-03-24T20:16:34.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990, upload-time = "2024-03-24T20:16:32.444Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] [[package]] @@ -776,6 +868,40 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + [[package]] name = "rich" version = "15.0.0" @@ -791,15 +917,15 @@ wheels = [ [[package]] name = "rstcheck" -version = "6.2.1" +version = "6.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "rstcheck-core" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/c1/ebec613b5e9225a9f5655bc17ce07b0e27e5a8fb302a1c088be5567d9f6f/rstcheck-6.2.1.tar.gz", hash = "sha256:e4d173950b023eb12c2b9d2348a8c62bef46612bbc7b29e1e57d37320ed0a891", size = 23609, upload-time = "2024-03-23T09:31:42.864Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/3e/54182f5a310e0e2eb287170eb590bb93011161e28e51aa499120ab5c9318/rstcheck-6.3.0.tar.gz", hash = "sha256:583072b43939627b1b0f4492e8af2587092efc1fb5e9f0228fa2df9ea3b44873", size = 30887, upload-time = "2026-07-28T19:49:07.054Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/b3/5556afc6e83b162dc29f84e0c8a10f6a72af70845552698acd6bb3b927cd/rstcheck-6.2.1-py3-none-any.whl", hash = "sha256:b450943707d8ca053f5c6b9f103ee595f4926a064203e5e579172aefb3fe2c12", size = 8526, upload-time = "2024-03-23T09:31:40.716Z" }, + { url = "https://files.pythonhosted.org/packages/44/13/8bdf4e9f028e147ebabd8e093d30b0bc6e70ebbcfa9dbd9b93861e7315cd/rstcheck-6.3.0-py3-none-any.whl", hash = "sha256:3c1d8753b5dc01c33d114711552b2c4855fde00e0713f5a8341d7aaab2777058", size = 8999, upload-time = "2026-07-28T19:49:05.664Z" }, ] [[package]] @@ -846,14 +972,14 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "black", specifier = ">=24.3.0,<25" }, - { name = "mypy", specifier = ">=1.17.0,<2" }, - { name = "pylint", specifier = ">=3.1.0,<4" }, - { name = "pytest", specifier = ">=8.1.1,<9" }, - { name = "pytest-cov", specifier = ">=5.0.0,<6" }, - { name = "pytest-runner", specifier = ">=6.0.0,<7" }, + { name = "black", specifier = ">=26.5.1,<27" }, + { name = "mypy", specifier = ">=2.3.0,<3" }, + { name = "pylint", specifier = ">=4.0.7,<5" }, + { name = "pytest", specifier = ">=9.1.1,<10" }, + { name = "pytest-cov", specifier = ">=7.1.0,<8" }, + { name = "pytest-runner", specifier = ">=6.0.1,<7" }, { name = "pytest-watch", specifier = ">=4.2.0,<5" }, - { name = "rstcheck", specifier = ">=6.1.2,<7" }, + { name = "rstcheck", specifier = ">=6.3.0,<7" }, ] [[package]] From aa15ecf658d15cccbba26affdf860e086f9f08dd Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 09:39:21 -0700 Subject: [PATCH 32/39] fix: pin the Black target version to the minimum supported Python (#613) Black infers its target versions from `requires-python`. Because that bound is open-ended (`>=3.11`), it expanded to every version Black knows about, including py315: target_version: ['py311', 'py312', 'py313', 'py314', 'py315'] Black's safety check re-parses its output with the stdlib `ast` module using the highest target version, which no interpreter from 3.11 to 3.14 can do. That made every lint job emit: Warning: Python 3.14 cannot parse code formatted for Python 3.15. Formatting was never affected, since Black only emits syntax common to all targets and so was already formatting for the 3.11 floor; `--diff` output is byte-identical between py311 and py315. What was lost is the AST equivalence check that guards against Black corrupting code. Pinning the floor explicitly restores that check and silences the warning with no formatting change. This needs bumping alongside any future increase to `requires-python`. Claude-Session: https://claude.ai/code/session_01X5qn7KvBztnwaRTFoL9KNo Co-authored-by: Claude --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7ea9de9a..2797790c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,9 @@ module-root = "" source-exclude = ["**/*_test.py"] wheel-exclude = ["**/*_test.py"] +[tool.black] +target-version = ["py311"] + [tool.pytest.ini_options] norecursedirs = [ "node_modules" From 7872dd772c10358ba5261e7586078901a00adc8c Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 16:40:01 +0000 Subject: [PATCH 33/39] 3.0.0b4 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2797790c..a676f255 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seam" -version = "3.0.0b3" +version = "3.0.0b4" description = "SDK for the Seam API written in Python." authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" diff --git a/uv.lock b/uv.lock index 94fafe37..8289a1de 100644 --- a/uv.lock +++ b/uv.lock @@ -943,7 +943,7 @@ wheels = [ [[package]] name = "seam" -version = "3.0.0b3" +version = "3.0.0b4" source = { editable = "." } dependencies = [ { name = "httpx" }, From 1219392c368b1e6c487fcca694495c59c6f557b5 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 10:18:30 -0700 Subject: [PATCH 34/39] feat: implement the URL search params serialization standard (#611) Port @seamapi/url-search-params-serializer to Python. It defines how the Seam SDKs and other API consumers serialize objects to URL search params, and the Seam API parses them with the corresponding parser. Output is byte-for-byte identical to the reference implementation: - Values are encoded with the application/x-www-form-urlencoded serializer, which differs from urllib in its treatment of "*" and "~". - Params are sorted by name, compared by UTF-16 code unit. - Floats are formatted using the ECMAScript Number::toString algorithm, which differs from repr for integral floats and around the exponent notation thresholds. The serialization defines the name and value of each param, where every value is a string, and leaves rendering the query string to URLSearchParams. UrlSearchParams is that layer here. Python has a single absence value, and the standard needs both: a param set to None is omitted, while a param set to NULL is serialized to an empty value, which the API reads as null. Nothing calls this yet. Serializing the params of a request is a separate change. Claude-Session: https://claude.ai/code/session_01PpqwDhZmyikGbjmCPqFW2A Co-authored-by: Claude --- README.rst | 54 +++ seam/__init__.py | 7 + seam/null.py | 62 +++ seam/url_search_params_serializer.py | 435 +++++++++++++++++++++ test/null_test.py | 22 ++ test/url_search_params_serializer_test.py | 454 ++++++++++++++++++++++ 6 files changed, 1034 insertions(+) create mode 100644 seam/null.py create mode 100644 seam/url_search_params_serializer.py create mode 100644 test/null_test.py create mode 100644 test/url_search_params_serializer_test.py diff --git a/README.rst b/README.rst index a031c5ce..70517550 100644 --- a/README.rst +++ b/README.rst @@ -73,6 +73,8 @@ Contents * `Configuring the httpx client`_ + * `Serializing URL search params`_ + * `Development and Testing`_ * `Quickstart`_ @@ -518,6 +520,58 @@ precedence over the defaults the SDK sets: }, ) +Serializing URL search params +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Seam API parses URL search params as complex types. +If you call it with your own HTTP client, +``serialize_url_search_params`` is exported for that purpose: + +.. code-block:: python + + import httpx + from seam import serialize_url_search_params + + httpx.get( + "https://connect.getseam.com/devices/list", + params=serialize_url_search_params({"device_ids": ["device1", "device2"]}), + headers={"Authorization": "Bearer your-api-key"}, + ) + +The serialization defines the name and value of each search param, +where every value is a string. +``UrlSearchParams`` holds those pairs and renders the query string, +as `URLSearchParams`_ does for the `reference implementation`_: + +.. code-block:: python + + from seam import UrlSearchParams, update_url_search_params + + search_params = UrlSearchParams() + + update_url_search_params(search_params, {"device_ids": ["device1", "device2"]}) + + list(search_params) + # => [('device_ids', 'device1'), ('device_ids', 'device2')] + + str(search_params) + # => 'device_ids=device1&device_ids=device2' + +Pass either the query string or the pairs to your HTTP client. +A client may percent-encode a few characters differently than +``URLSearchParams`` does, e.g. httpx escapes ``*`` and unescapes ``~``, +which the Seam API reads as the same params either way. + +A param set to ``None`` is omitted, while a param set to ``seam.NULL`` +is serialized to an empty value, which the Seam API reads as null. +A param that cannot be represented raises a ``seam.UnserializableParamError``. + +The Seam API parses these params with the corresponding `parser`_. + +.. _URLSearchParams: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams +.. _reference implementation: https://github.com/seamapi/url-search-params-serializer +.. _parser: https://github.com/seamapi/url-search-params-parser + Development and Testing ----------------------- diff --git a/seam/__init__.py b/seam/__init__.py index 30e17b6c..4c912626 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -15,3 +15,10 @@ ) from .seam_webhook import SeamWebhook from svix.webhooks import WebhookVerificationError as SeamWebhookVerificationError +from .null import NULL, Null +from .url_search_params_serializer import ( + UnserializableParamError, + UrlSearchParams, + serialize_url_search_params, + update_url_search_params, +) diff --git a/seam/null.py b/seam/null.py new file mode 100644 index 00000000..42c3b481 --- /dev/null +++ b/seam/null.py @@ -0,0 +1,62 @@ +"""The explicit null sentinel used by request params. + +Python has a single absence value, ``None``, but the Seam API distinguishes +an omitted param from a param explicitly set to null. For example, in an +update request, an omitted param leaves the current value unchanged, +while a null param unsets the current value. + +Since sending null is rarely intended and unsetting a value cannot be undone, +``None`` means the safe option of omitting the param. +Sending null is explicit and always spelled :data:`NULL`. +""" + +from typing import Any + + +class Null: + """Type of the :data:`NULL` sentinel.""" + + _instance = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super().__new__(cls) + return cls._instance + + def __repr__(self): + return "NULL" + + def __bool__(self): + return False + + +NULL = Null() +"""Sentinel for a param explicitly set to null. + +Params set to this sentinel are serialized to null, +whereas params set to ``None`` are omitted: + +.. code-block:: python + + from seam import NULL, serialize_url_search_params + + serialize_url_search_params({"name": NULL, "limit": 20}) + # => 'limit=20&name=' + + serialize_url_search_params({"name": None, "limit": 20}) + # => 'limit=20' + +Use it wherever the Seam API documents null as a meaningful value, e.g., +to unset a value in an update request, or to filter by an unset value. +""" + + +def is_null(value: Any) -> bool: + """Returns whether a value is the :data:`NULL` sentinel. + + :param value: The value to check + :type value: Any + + :returns: Whether the value is the ``NULL`` sentinel""" + + return isinstance(value, Null) diff --git a/seam/url_search_params_serializer.py b/seam/url_search_params_serializer.py new file mode 100644 index 00000000..973fc3df --- /dev/null +++ b/seam/url_search_params_serializer.py @@ -0,0 +1,435 @@ +"""Serializes Python objects to URL search params. + +This is a Python port of the `@seamapi/url-search-params-serializer +`_ reference +implementation, which defines the standard for how the Seam SDKs and other +Seam API consumers serialize objects to URL search params in HTTP GET requests. + +Output is byte-for-byte identical to the reference implementation: +values are encoded with the ``application/x-www-form-urlencoded`` serializer, +params are sorted by name, and numbers are formatted using the +ECMAScript ``Number::toString`` algorithm. + +Type mapping between the reference implementation and this port: + +- JavaScript ``undefined`` is ``None``, or simply an absent key. +- JavaScript ``null`` is :data:`seam.NULL `. + Python has a single absence value, so ``None`` means the safe option of + omitting the param and sending null is always explicit. +- JavaScript ``string`` is ``str``. +- JavaScript ``boolean`` is ``bool``. +- JavaScript ``number`` is ``float`` or ``int``. +- JavaScript ``bigint`` is ``int``. + Python integers are arbitrary precision, so ``int`` covers both cases + and is always serialized in full without exponent notation. +- JavaScript ``Date`` and ``Temporal.Instant`` are + :class:`datetime.datetime`. + A naive ``datetime`` is interpreted as UTC. + Since ``Date`` has millisecond precision, microseconds are truncated. +- JavaScript ``Array`` is ``list`` or ``tuple``. + Unordered collections such as ``set`` are unsupported + because they would not serialize deterministically. +- A JavaScript plain object is any ``Mapping``, e.g., a ``dict``. +""" + +import datetime +import math +import string +from collections.abc import Mapping +from decimal import Decimal +from typing import Any, Iterator, List, Optional, Sequence, Tuple, Union +from urllib.parse import parse_qsl + +from .null import is_null + +Params = Mapping[str, Any] + + +class UnserializableParamError(Exception): + """Exception raised when a param could not be serialized. + + :ivar name: Name of the param that could not be serialized + :vartype name: str + """ + + def __init__(self, name: str, message: str): + """ + :param name: Name of the param that could not be serialized + :type name: str + :param message: Description of why the param could not be serialized + :type message: str + """ + + super().__init__(f"Could not serialize parameter: '{name}' {message}") + self.name = name + + +class UrlSearchParams: + """A mutable collection of URL search params. + + Implements the parts of the `URLSearchParams + `_ + interface needed to serialize params to a query string. + Unlike a ``dict``, a name may appear more than once, + which is how arrays are serialized. + """ + + def __init__( + self, + init: Optional[Union[str, Params, Sequence[Tuple[str, str]]]] = None, + ): + """ + :param init: A query string, a mapping of names to values, + or a sequence of name-value pairs + :type init: Optional[Union[str, Mapping[str, Any], Sequence[Tuple[str, str]]]] + """ + + self._pairs: List[Tuple[str, str]] = [] + + if init is None: + return + + if isinstance(init, str): + query = init[1:] if init.startswith("?") else init + self._pairs = list(parse_qsl(query, keep_blank_values=True)) + return + + items = init.items() if isinstance(init, Mapping) else init + self._pairs = [(str(name), str(value)) for name, value in items] + + def append(self, name: str, value: str) -> None: + """Appends a name-value pair, keeping any existing pairs with this name. + + :param name: Name of the param + :type name: str + :param value: Value of the param + :type value: str + """ + + self._pairs.append((name, value)) + + def set(self, name: str, value: str) -> None: + """Sets the value associated with a name. + + Replaces the first pair with this name and removes any others. + Appends a new pair if no pair with this name exists. + + :param name: Name of the param + :type name: str + :param value: Value of the param + :type value: str + """ + + if not self.has(name): + self.append(name, value) + return + + pairs: List[Tuple[str, str]] = [] + is_set = False + + for pair in self._pairs: + if pair[0] != name: + pairs.append(pair) + elif not is_set: + pairs.append((name, value)) + is_set = True + + self._pairs = pairs + + def get(self, name: str) -> Optional[str]: + """Returns the value of the first pair with this name. + + :param name: Name of the param + :type name: str + + :returns: The value, or ``None`` if no pair with this name exists + """ + + for existing_name, value in self._pairs: + if existing_name == name: + return value + + return None + + def get_all(self, name: str) -> List[str]: + """Returns the values of all pairs with this name, in insertion order. + + :param name: Name of the param + :type name: str + + :returns: The values""" + + return [value for existing_name, value in self._pairs if existing_name == name] + + def has(self, name: str) -> bool: + """Returns whether a pair with this name exists. + + :param name: Name of the param + :type name: str + + :returns: Whether a pair with this name exists""" + + return any(existing_name == name for existing_name, _ in self._pairs) + + def delete(self, name: str) -> None: + """Removes all pairs with this name. + + :param name: Name of the param + :type name: str + """ + + self._pairs = [pair for pair in self._pairs if pair[0] != name] + + def sort(self) -> None: + """Sorts all pairs by name. + + Sorting is stable, so the relative order of pairs + with the same name is preserved. + Names are compared by UTF-16 code units to match the + `URLSearchParams.sort() + `_ + specification. + """ + + self._pairs.sort(key=lambda pair: pair[0].encode("utf-16-be")) + + def to_string(self) -> str: + """Serializes all pairs to a query string. + + :returns: The query string, without a leading ``?``""" + + return "&".join( + f"{_encode_form_component(name)}={_encode_form_component(value)}" + for name, value in self._pairs + ) + + def __str__(self) -> str: + return self.to_string() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.to_string()!r})" + + def __len__(self) -> int: + return len(self._pairs) + + def __iter__(self) -> Iterator[Tuple[str, str]]: + return iter(self._pairs) + + +def serialize_url_search_params(params: Params) -> str: + """Serializes params to a URL search param query string. + + :param params: The params to serialize + :type params: Mapping[str, Any] + + :returns: The query string, without a leading ``?`` + + :raises UnserializableParamError: If any param could not be serialized + """ + + search_params = UrlSearchParams() + update_url_search_params(search_params, params) + + return search_params.to_string() + + +def update_url_search_params(search_params: UrlSearchParams, params: Params) -> None: + """Updates existing URL search params with serialized params. + + Existing params are preserved unless overwritten by a serialized param. + All params are sorted by name. + + :param search_params: The URL search params to update + :type search_params: UrlSearchParams + :param params: The params to serialize + :type params: Mapping[str, Any] + + :raises UnserializableParamError: If any param could not be serialized + """ + + _nested_update_url_search_params(search_params, params, []) + search_params.sort() + + +def _nested_update_url_search_params( + search_params: UrlSearchParams, params: Params, path: List[str] +) -> None: + for key, value in params.items(): + if not isinstance(key, str): + raise UnserializableParamError( + repr(key), + f"is a {type(key).__name__} which is unsupported as a parameter name", + ) + + if "." in key: + raise UnserializableParamError( + key, + 'contains one or more dots "." in its name which is unsupported', + ) + + current_path = [*path, key] + + if isinstance(value, Mapping): + _nested_update_url_search_params(search_params, value, current_path) + continue + + name = ".".join(current_path) + + if value is None: + continue + + if isinstance(value, str) and len(value) == 0: + continue + + if isinstance(value, (list, tuple)): + _update_url_search_params_from_array(search_params, name, value) + continue + + search_params.set(name, _serialize(name, value)) + + +def _update_url_search_params_from_array( + search_params: UrlSearchParams, name: str, values: Sequence[Any] +) -> None: + if len(values) == 0: + search_params.set(name, "") + return + + if len(values) == 1 and _is_empty_string(values[0]): + raise UnserializableParamError( + name, + "is a single element array containing the empty string which is unsupported", + ) + + if any(_is_empty_string(value) for value in values): + raise UnserializableParamError( + name, + "is an array containing the empty string which is unsupported", + ) + + if any(value is None or is_null(value) for value in values): + raise UnserializableParamError( + name, + "is an array containing null or undefined values which is unsupported", + ) + + for value in values: + search_params.append(name, _serialize(name, value)) + + +def _serialize(name: str, value: Any) -> str: + if is_null(value): + return "" + + if isinstance(value, str): + return value + + if isinstance(value, bool): + return "true" if value else "false" + + if isinstance(value, int): + return str(value) + + if isinstance(value, float): + return _format_number(name, value) + + if isinstance(value, datetime.datetime): + return _format_datetime(value) + + raise UnserializableParamError(name, f"is a {type(value).__name__}") + + +def _is_empty_string(value: Any) -> bool: + return isinstance(value, str) and len(value) == 0 + + +def _format_datetime(value: datetime.datetime) -> str: + if value.tzinfo is None: + value = value.replace(tzinfo=datetime.timezone.utc) + + utc_value = value.astimezone(datetime.timezone.utc) + milliseconds = utc_value.microsecond // 1000 + + return ( + f"{utc_value.year:04d}-{utc_value.month:02d}-{utc_value.day:02d}" + f"T{utc_value.hour:02d}:{utc_value.minute:02d}:{utc_value.second:02d}" + f".{milliseconds:03d}Z" + ) + + +def _format_number(name: str, value: float) -> str: + if math.isnan(value): + raise UnserializableParamError(name, "is NaN") + + if math.isinf(value): + raise UnserializableParamError( + name, "is Infinity" if value > 0 else "is -Infinity" + ) + + if value == 0: + return "0" + + sign = "-" if value < 0 else "" + _, digit_tuple, exponent = Decimal(repr(abs(value))).as_tuple() + + # The shortest digit string that round-trips, and the position of the + # decimal point relative to it, as required by the ECMAScript + # Number::toString algorithm. + digits = "".join(str(digit) for digit in digit_tuple) + point = int(exponent) + len(digits) + digits = digits.rstrip("0") + + return sign + _format_digits(digits, point) + + +def _format_digits(digits: str, point: int) -> str: + """Formats digits and a decimal point position per ECMAScript Number::toString. + + :param digits: Significant digits, without trailing zeros + :type digits: str + :param point: Position of the decimal point relative to the digits + :type point: int + + :returns: The formatted number""" + + count = len(digits) + + if count <= point <= 21: + return digits + "0" * (point - count) + + if 0 < point <= 21: + return f"{digits[:point]}.{digits[point:]}" + + if -6 < point <= 0: + return f"0.{'0' * -point}{digits}" + + exponent = point - 1 + exponent_sign = "+" if exponent >= 0 else "-" + mantissa = digits if count == 1 else f"{digits[0]}.{digits[1:]}" + + return f"{mantissa}e{exponent_sign}{abs(exponent)}" + + +_FORM_SAFE_CHARACTERS = frozenset(f"{string.ascii_letters}{string.digits}*-._") + + +def _encode_form_component(value: str) -> str: + """Percent-encodes a string using the ``application/x-www-form-urlencoded`` serializer. + + :param value: The string to encode + :type value: str + + :returns: The encoded string""" + + encoded = [] + + for byte in value.encode("utf-8"): + character = chr(byte) + if character in _FORM_SAFE_CHARACTERS: + encoded.append(character) + elif character == " ": + encoded.append("+") + else: + encoded.append(f"%{byte:02X}") + + return "".join(encoded) diff --git a/test/null_test.py b/test/null_test.py new file mode 100644 index 00000000..83db727c --- /dev/null +++ b/test/null_test.py @@ -0,0 +1,22 @@ +from seam.null import NULL, Null, is_null + + +def test_null_is_a_singleton(): + assert Null() is NULL + assert is_null(NULL) + assert is_null(Null()) + + +def test_null_is_not_none(): + assert NULL is not None + assert not is_null(None) + assert not is_null("") + assert not is_null(0) + + +def test_null_is_falsy(): + assert not NULL + + +def test_null_repr(): + assert repr(NULL) == "NULL" diff --git a/test/url_search_params_serializer_test.py b/test/url_search_params_serializer_test.py new file mode 100644 index 00000000..48c94e04 --- /dev/null +++ b/test/url_search_params_serializer_test.py @@ -0,0 +1,454 @@ +from collections import OrderedDict +from datetime import date, datetime, timedelta, timezone + +import pytest + +from seam.null import NULL +from seam.url_search_params_serializer import ( + UnserializableParamError, + UrlSearchParams, + serialize_url_search_params, + update_url_search_params, +) + + +def test_serializes_empty_object(): + assert serialize_url_search_params({}) == "" + + +def test_serializes_string(): + assert serialize_url_search_params({"foo": "d"}) == "foo=d" + assert serialize_url_search_params({"foo": "null"}) == "foo=null" + assert serialize_url_search_params({"foo": "None"}) == "foo=None" + assert serialize_url_search_params({"foo": "undefined"}) == "foo=undefined" + assert serialize_url_search_params({"foo": "0"}) == "foo=0" + + +def test_removes_the_empty_string(): + # Serializing the empty string would conflict with NULL. + assert serialize_url_search_params({"foo": ""}) == "" + assert serialize_url_search_params({"foo": "d", "bar": ""}) == "foo=d" + + +def test_serializes_int(): + assert serialize_url_search_params({"foo": 1}) == "foo=1" + assert serialize_url_search_params({"foo": 0}) == "foo=0" + assert serialize_url_search_params({"foo": -42}) == "foo=-42" + + +def test_serializes_arbitrary_precision_int(): + assert ( + serialize_url_search_params({"foo": 9007199254740993}) == "foo=9007199254740993" + ) + assert ( + serialize_url_search_params({"foo": 123456789012345678901234567890}) + == "foo=123456789012345678901234567890" + ) + + +def test_serializes_float(): + assert serialize_url_search_params({"foo": 23.8}) == "foo=23.8" + assert serialize_url_search_params({"foo": -23.8}) == "foo=-23.8" + assert serialize_url_search_params({"foo": 0.30000000000000004}) == ( + "foo=0.30000000000000004" + ) + + +def test_serializes_float_using_the_ecmascript_number_format(): + # A float is serialized exactly as JavaScript would serialize the number, + # which is not always the same as the Python repr. + assert serialize_url_search_params({"foo": 1.0}) == "foo=1" + assert serialize_url_search_params({"foo": -0.0}) == "foo=0" + assert serialize_url_search_params({"foo": 100.0}) == "foo=100" + assert serialize_url_search_params({"foo": 1e16}) == "foo=10000000000000000" + assert serialize_url_search_params({"foo": 1e20}) == "foo=100000000000000000000" + assert serialize_url_search_params({"foo": 1e21}) == "foo=1e%2B21" + assert serialize_url_search_params({"foo": 0.0001}) == "foo=0.0001" + assert serialize_url_search_params({"foo": 1e-6}) == "foo=0.000001" + assert serialize_url_search_params({"foo": 1e-7}) == "foo=1e-7" + assert serialize_url_search_params({"foo": 5e-324}) == "foo=5e-324" + assert serialize_url_search_params({"foo": 1.7976931348623157e308}) == ( + "foo=1.7976931348623157e%2B308" + ) + + +def test_serializes_bool(): + assert serialize_url_search_params({"foo": True}) == "foo=true" + assert serialize_url_search_params({"foo": False}) == "foo=false" + assert serialize_url_search_params({"foo": True, "bar": False}) == ( + "bar=false&foo=true" + ) + + +def test_removes_none_params(): + assert serialize_url_search_params({"bar": None}) == "" + assert serialize_url_search_params({"foo": 1, "bar": None}) == "foo=1" + + +def test_serializes_null_params(): + assert serialize_url_search_params({"bar": NULL}) == "bar=" + assert serialize_url_search_params({"foo": 1, "bar": NULL}) == "bar=&foo=1" + + +def test_removes_none_params_at_any_depth(): + assert serialize_url_search_params({"foo": {"bar": None, "baz": 1}}) == "foo.baz=1" + assert serialize_url_search_params({"foo": {"bar": None}}) == "" + + +def test_serializes_empty_array_params(): + assert serialize_url_search_params({"bar": []}) == "bar=" + assert serialize_url_search_params({"foo": 1, "bar": []}) == "bar=&foo=1" + assert serialize_url_search_params({"bar": ()}) == "bar=" + + +def test_serializes_array_params_with_one_value(): + assert serialize_url_search_params({"bar": ["a"]}) == "bar=a" + assert serialize_url_search_params({"foo": 1, "bar": ["a"]}) == "bar=a&foo=1" + + +def test_serializes_array_params_with_many_values(): + assert serialize_url_search_params({"foo": 1, "bar": ["a", "2"]}) == ( + "bar=a&bar=2&foo=1" + ) + assert serialize_url_search_params( + {"foo": 1, "bar": ["null", "2", "undefined"]} + ) == ("bar=null&bar=2&bar=undefined&foo=1") + + +def test_serializes_tuple_params(): + assert serialize_url_search_params({"bar": ("a", "2")}) == "bar=a&bar=2" + + +def test_serializes_array_params_with_mixed_values(): + assert serialize_url_search_params( + {"bar": [1, "a", True, datetime(1970, 1, 1, tzinfo=timezone.utc)]} + ) == ("bar=1&bar=a&bar=true&bar=1970-01-01T00%3A00%3A00.000Z") + + +def test_serializes_datetime(): + assert serialize_url_search_params( + {"foo": 1, "now": datetime(2025, 2, 24, 18, 44, 39, tzinfo=timezone.utc)} + ) == ("foo=1&now=2025-02-24T18%3A44%3A39.000Z") + + +def test_serializes_datetime_with_milliseconds(): + assert serialize_url_search_params( + { + "now": datetime( + 2025, 2, 24, 18, 44, 39, microsecond=123000, tzinfo=timezone.utc + ) + } + ) == ("now=2025-02-24T18%3A44%3A39.123Z") + + +def test_truncates_datetime_microseconds(): + assert serialize_url_search_params( + { + "now": datetime( + 2025, 2, 24, 18, 44, 39, microsecond=123999, tzinfo=timezone.utc + ) + } + ) == ("now=2025-02-24T18%3A44%3A39.123Z") + + +def test_serializes_datetime_as_utc(): + assert serialize_url_search_params( + {"now": datetime(2025, 2, 24, 13, 44, 39, tzinfo=timezone(timedelta(hours=-5)))} + ) == ("now=2025-02-24T18%3A44%3A39.000Z") + + +def test_serializes_naive_datetime_as_utc(): + assert serialize_url_search_params({"now": datetime(2025, 2, 24, 18, 44, 39)}) == ( + "now=2025-02-24T18%3A44%3A39.000Z" + ) + + +def test_serializes_datetime_before_the_epoch(): + assert serialize_url_search_params( + {"then": datetime(1969, 12, 31, 23, 59, 59, tzinfo=timezone.utc)} + ) == ("then=1969-12-31T23%3A59%3A59.000Z") + + +def test_serializes_dicts(): + assert serialize_url_search_params({"foo": 1, "bar": {"baz": "a"}}) == ( + "bar.baz=a&foo=1" + ) + + assert serialize_url_search_params({"foo": 1, "bar": {"baz": {"x": {"z": 1}}}}) == ( + "bar.baz.x.z=1&foo=1" + ) + + assert serialize_url_search_params( + {"foo": 1, "bar": {"baz": {"x": {"z": NULL}}}} + ) == ("bar.baz.x.z=&foo=1") + + assert serialize_url_search_params({"foo": 1, "bar": {"baz": [1, "a"]}}) == ( + "bar.baz=1&bar.baz=a&foo=1" + ) + + assert serialize_url_search_params({"foo": {}, "bar": 2}) == "bar=2" + + assert serialize_url_search_params({"foo": {"x": {}}, "bar": 2}) == "bar=2" + + assert serialize_url_search_params( + {"foo": {}, "bar": {"baz": {"x": {"z": NULL, "t": {}}, "q": {}}}} + ) == ("bar.baz.x.z=") + + +def test_serializes_dict_subclasses(): + assert serialize_url_search_params( + {"foo": OrderedDict([("bar", 1), ("baz", 2)])} + ) == ("foo.bar=1&foo.baz=2") + + +def test_sorts_params_by_name(): + assert serialize_url_search_params({"b": 1, "a": 2, "c": 3}) == "a=2&b=1&c=3" + assert serialize_url_search_params({"b": 1, "A": 2, "a": 3, "B": 4}) == ( + "A=2&B=4&a=3&b=1" + ) + assert serialize_url_search_params({"a10": 1, "a2": 2, "a1": 3}) == ( + "a1=3&a10=1&a2=2" + ) + assert serialize_url_search_params({"zz": 1, "a": {"z": 2, "b": 3}}) == ( + "a.b=3&a.z=2&zz=1" + ) + assert serialize_url_search_params({"ab": 1, "a": {"b": 2}}) == "a.b=2&ab=1" + + +def test_sorts_params_by_utf_16_code_unit(): + assert serialize_url_search_params({"￿": 1, "\U0001f600": 2}) == ( + "%F0%9F%98%80=2&%EF%BF%BF=1" + ) + + +def test_sorting_preserves_array_order(): + assert serialize_url_search_params({"b": ["3", "1", "2"], "a": 1}) == ( + "a=1&b=3&b=1&b=2" + ) + + +def test_encodes_params_as_form_urlencoded(): + assert serialize_url_search_params({"foo": "a b"}) == "foo=a+b" + assert serialize_url_search_params({"foo": "a+b"}) == "foo=a%2Bb" + assert serialize_url_search_params({"foo": "a~b"}) == "foo=a%7Eb" + assert serialize_url_search_params({"foo": "a*b"}) == "foo=a*b" + assert serialize_url_search_params({"foo": "abcXYZ019*-._"}) == "foo=abcXYZ019*-._" + assert serialize_url_search_params({"foo": "a&b=c?d#e/f"}) == ( + "foo=a%26b%3Dc%3Fd%23e%2Ff" + ) + assert serialize_url_search_params({"foo": "100%"}) == "foo=100%25" + assert serialize_url_search_params({"foo": "a\nb"}) == "foo=a%0Ab" + + +def test_encodes_unicode_params(): + assert serialize_url_search_params({"foo": "héllo wörld"}) == ( + "foo=h%C3%A9llo+w%C3%B6rld" + ) + assert serialize_url_search_params({"foo": "日本語"}) == ( + "foo=%E6%97%A5%E6%9C%AC%E8%AA%9E" + ) + assert serialize_url_search_params({"🔒": "a"}) == "%F0%9F%94%92=a" + assert serialize_url_search_params({"a b": 1}) == "a+b=1" + + +def test_cannot_serialize_keys_containing_a_dot(): + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo.bar": 1}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": {"bar.baz": 1}}) + + +def test_cannot_serialize_non_string_keys(): + with pytest.raises(UnserializableParamError): + serialize_url_search_params({1: "a"}) + + +def test_cannot_serialize_functions(): + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": lambda: None}) + + +def test_cannot_serialize_number_pointers(): + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": float("inf")}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": float("-inf")}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": float("nan")}) + + +def test_cannot_serialize_arbitrary_objects(): + class Device: + def __init__(self): + self.device_id = "a" + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": Device()}) + + +def test_cannot_serialize_date(): + # A date is not an instant, so it has no unambiguous serialization. + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": date(2025, 2, 24)}) + + +def test_cannot_serialize_sets(): + # A set would not serialize deterministically. + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": {"a", "b"}}) + + +def test_cannot_serialize_array_params_with_unserializable_values(): + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": [""]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", None]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", NULL]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", ["s"]]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", []]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", [""]]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", {}]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", {"x": 2}]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"bar": ["a", lambda: None]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": 1, "bar": ["", "a", ""]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": 1, "bar": ["", "a", "2"]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": 1, "bar": ["", "", ""]}) + + with pytest.raises(UnserializableParamError): + serialize_url_search_params({"foo": [1, float("nan")]}) + + +def test_unserializable_param_error_message(): + with pytest.raises(UnserializableParamError) as error: + serialize_url_search_params({"foo": {"bar.baz": 1}}) + + assert str(error.value) == ( + "Could not serialize parameter: 'bar.baz' contains one or more dots" + ' "." in its name which is unsupported' + ) + assert error.value.name == "bar.baz" + + +def test_unserializable_param_error_message_uses_the_full_path(): + with pytest.raises(UnserializableParamError) as error: + serialize_url_search_params({"foo": {"bar": float("nan")}}) + + assert str(error.value) == "Could not serialize parameter: 'foo.bar' is NaN" + + +def test_update_url_search_params(): + search_params = UrlSearchParams() + update_url_search_params(search_params, {"foo": "d", "bar": 2}) + + assert search_params.to_string() == "bar=2&foo=d" + + +def test_update_url_search_params_preserves_existing_params(): + search_params = UrlSearchParams([("foo", "bar")]) + update_url_search_params( + search_params, + {"name": "Dax", "age": 27, "is_admin": True, "tags": ["cars", "planes"]}, + ) + + assert search_params.to_string() == ( + "age=27&foo=bar&is_admin=true&name=Dax&tags=cars&tags=planes" + ) + + +def test_update_url_search_params_overwrites_existing_params(): + search_params = UrlSearchParams([("foo", "a"), ("bar", "x"), ("foo", "b")]) + update_url_search_params(search_params, {"foo": "new"}) + + assert search_params.to_string() == "bar=x&foo=new" + + +def test_update_url_search_params_appends_array_params(): + search_params = UrlSearchParams([("foo", "old")]) + update_url_search_params(search_params, {"foo": [1, 2]}) + + assert search_params.to_string() == "foo=old&foo=1&foo=2" + + +def test_update_url_search_params_keeps_existing_params_for_absent_values(): + for value in [None, "", {}]: + search_params = UrlSearchParams([("foo", "a")]) + update_url_search_params(search_params, {"foo": value}) + + assert search_params.to_string() == "foo=a" + + +def test_url_search_params_from_query_string(): + search_params = UrlSearchParams("?a=1&b=hello+world&c=%F0%9F%94%92&d") + + assert search_params.get("a") == "1" + assert search_params.get("b") == "hello world" + assert search_params.get("c") == "🔒" + assert search_params.get("d") == "" + assert search_params.to_string() == "a=1&b=hello+world&c=%F0%9F%94%92&d=" + + +def test_url_search_params_from_dict(): + assert UrlSearchParams({"a": "1", "b": "2"}).to_string() == "a=1&b=2" + + +def test_url_search_params_append_and_get(): + search_params = UrlSearchParams() + search_params.append("foo", "a") + search_params.append("foo", "b") + + assert search_params.get("foo") == "a" + assert search_params.get_all("foo") == ["a", "b"] + assert search_params.get("bar") is None + assert search_params.get_all("bar") == [] + assert len(search_params) == 2 + assert list(search_params) == [("foo", "a"), ("foo", "b")] + + +def test_url_search_params_set(): + search_params = UrlSearchParams([("foo", "a"), ("bar", "x"), ("foo", "b")]) + search_params.set("foo", "c") + + assert list(search_params) == [("foo", "c"), ("bar", "x")] + + search_params.set("baz", "y") + + assert search_params.get("baz") == "y" + + +def test_url_search_params_has_and_delete(): + search_params = UrlSearchParams([("foo", "a"), ("foo", "b")]) + + assert search_params.has("foo") + + search_params.delete("foo") + + assert not search_params.has("foo") + assert len(search_params) == 0 + + +def test_url_search_params_str(): + assert str(UrlSearchParams([("foo", "a b")])) == "foo=a+b" From 84b54858d4db24074428a1d871174b28230a5399 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 17:19:22 +0000 Subject: [PATCH 35/39] 3.0.0b5 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a676f255..de70fce4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seam" -version = "3.0.0b4" +version = "3.0.0b5" description = "SDK for the Seam API written in Python." authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" diff --git a/uv.lock b/uv.lock index 8289a1de..c38fbd89 100644 --- a/uv.lock +++ b/uv.lock @@ -943,7 +943,7 @@ wheels = [ [[package]] name = "seam" -version = "3.0.0b4" +version = "3.0.0b5" source = { editable = "." } dependencies = [ { name = "httpx" }, From 52cfee7f2927f880ebf3b12c880d2d7971e0bd2a Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 11:30:11 -0700 Subject: [PATCH 36/39] feat: apply the URL search params standard and type nullable params (#614) * feat: serialize request params with the URL search params standard The Seam API parses URL search params as complex types, so the SDK has to build the query string itself. Serialize any mapping passed as params and set the result on the url, rather than letting httpx encode the params with its own rules, which represent arrays and nested objects differently. Replace the NULL sentinel with null in request bodies as well, so a param set to NULL is sent as null on either transport, and document how NULL tells an explicitly null param apart from an omitted one. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PpqwDhZmyikGbjmCPqFW2A * Apply suggestions from code review Co-authored-by: Evan Sosenko * feat: type nullable params with the explicit null sentinel Consume the blueprint isNullable flag so a param the Seam API documents as nullable is typed to accept NULL, and a param that is merely optional is not. Optional params are omitted by passing None, where sending null would unset a value instead, so accepting the sentinel everywhere would invite exactly the mistake it exists to prevent. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PpqwDhZmyikGbjmCPqFW2A * test: name the search param tests for what they do Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PpqwDhZmyikGbjmCPqFW2A --------- Co-authored-by: Claude --- README.rst | 57 +++++- codegen/layouts/partials/method-signature.hbs | 2 +- codegen/layouts/route.hbs | 3 + codegen/lib/class-model.ts | 1 + codegen/lib/handlebars-helpers.ts | 6 + codegen/lib/layouts/route.ts | 8 + codegen/lib/routes.ts | 1 + seam/client.py | 18 ++ seam/null.py | 25 +++ seam/routes/access_codes.py | 5 +- seam/routes/access_codes_unmanaged.py | 5 +- seam/routes/access_grants.py | 25 +-- seam/routes/access_grants_unmanaged.py | 5 +- seam/routes/access_methods.py | 5 +- seam/routes/acs_credentials.py | 5 +- seam/routes/acs_encoders.py | 5 +- seam/routes/acs_entrances.py | 9 +- seam/routes/acs_users.py | 9 +- seam/routes/action_attempts.py | 5 +- seam/routes/connect_webviews.py | 5 +- seam/routes/connected_accounts.py | 5 +- seam/routes/devices.py | 13 +- seam/routes/devices_unmanaged.py | 5 +- seam/routes/spaces.py | 5 +- seam/routes/thermostats.py | 53 +++--- seam/routes/thermostats_schedules.py | 9 +- seam/routes/user_identities.py | 37 ++-- seam/routes/user_identities_unmanaged.py | 5 +- seam/routes/workspaces.py | 5 +- test/conftest.py | 24 ++- test/headers_test.py | 3 +- test/null_test.py | 36 +++- test/nullable_param_test.py | 52 ++++++ test/search_params_test.py | 176 ++++++++++++++++++ 34 files changed, 518 insertions(+), 114 deletions(-) create mode 100644 test/nullable_param_test.py create mode 100644 test/search_params_test.py diff --git a/README.rst b/README.rst index 70517550..d93f018a 100644 --- a/README.rst +++ b/README.rst @@ -47,6 +47,8 @@ Contents * `Action Attempts`_ + * `Setting a Param to Null`_ + * `Pagination`_ * `Manually fetch pages with the next_page_cursor`_ @@ -280,6 +282,56 @@ For example: except SeamActionAttemptTimeoutError as e: print("Door took too long to unlock") +Setting a Param to Null +~~~~~~~~~~~~~~~~~~~~~~~ + +The Seam API tells an omitted param apart from one explicitly set to null. +In an update request, an omitted param leaves the current value unchanged, +while a null param unsets it. + +Python has a single nil value `None` which represents an undefined parameter. This SDK provides an explicit null value to send in requests. +A param set to ``None`` is omitted, and a param set to ``NULL`` is sent as `null`: + +.. code-block:: python + + from seam import NULL, Seam + + seam = Seam() + + # Leaves the name unchanged. + seam.devices.update(device_id="your-device-id", name=None) + + # Unsets the name. + seam.devices.update(device_id="your-device-id", name=NULL) + +Because unsetting a value cannot be undone, ``None`` means the safe option of +omitting the param, and sending null is always explicit. +This is why a param is never sent as null by default, +even though ``None`` is the natural way to spell null in Python. + +``NULL`` behaves the same way in a request body and in a URL search param. +Its type is exported as ``Null`` for annotating your own code: + +.. code-block:: python + + from typing import Optional, Union + + from seam import NULL, Null + + name: Optional[Union[str, Null]] = NULL + +Only params the Seam API documents as nullable accept ``NULL``. +The generated method signatures say which ones those are, +so a type checker rejects ``NULL`` anywhere else: + +.. code-block:: python + + # name is nullable, so it may be unset. + seam.devices.update(device_id="your-device-id", name=NULL) + + # is_managed is not, so this fails the type check. + seam.devices.update(device_id="your-device-id", is_managed=NULL) + Pagination ~~~~~~~~~~ @@ -562,8 +614,9 @@ A client may percent-encode a few characters differently than ``URLSearchParams`` does, e.g. httpx escapes ``*`` and unescapes ``~``, which the Seam API reads as the same params either way. -A param set to ``None`` is omitted, while a param set to ``seam.NULL`` -is serialized to an empty value, which the Seam API reads as null. +A param set to ``None`` is omitted, while a param set to ``NULL`` +is serialized to an empty value, which the Seam API reads as null, +as described in `Setting a Param to Null`_. A param that cannot be represented raises a ``seam.UnserializableParamError``. The Seam API parses these params with the corresponding `parser`_. diff --git a/codegen/layouts/partials/method-signature.hbs b/codegen/layouts/partials/method-signature.hbs index 10977db0..4e58dbd1 100644 --- a/codegen/layouts/partials/method-signature.hbs +++ b/codegen/layouts/partials/method-signature.hbs @@ -1 +1 @@ -{{name}}(self{{#if params}}, *{{else}}{{#if (eq returnType "ActionAttempt")}}, *{{/if}}{{/if}}{{#each params}}, {{name}}: {{#if required}}{{type}}{{else}}Optional[{{type}}] = None{{/if}}{{/each}}{{#if (eq returnType "ActionAttempt")}}, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None{{/if}}) -> {{returnType}} \ No newline at end of file +{{name}}(self{{#if params}}, *{{else}}{{#if (eq returnType "ActionAttempt")}}, *{{/if}}{{/if}}{{#each params}}, {{name}}: {{#if required}}{{nullableType type isNullable}}{{else}}Optional[{{nullableType type isNullable}}] = None{{/if}}{{/each}}{{#if (eq returnType "ActionAttempt")}}, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None{{/if}}) -> {{returnType}} \ No newline at end of file diff --git a/codegen/layouts/route.hbs b/codegen/layouts/route.hbs index afaf2b43..32540f82 100644 --- a/codegen/layouts/route.hbs +++ b/codegen/layouts/route.hbs @@ -2,6 +2,9 @@ from typing import Optional, Any, List, Dict, Union import abc from ..client import SeamHttpClient from ..route import route_metadata +{{#if importNull}} +from ..null import Null +{{/if}} {{#if resourceClasses}} from ..resources import ({{#each resourceClasses}}{{this}}{{#unless @last}},{{/unless}}{{/each}}) {{/if}} diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index 9ecca230..d9333b65 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -4,6 +4,7 @@ export interface ClassMethodParameter { name: string type: string + isNullable: boolean description: string isDeprecated: boolean deprecationMessage: string diff --git a/codegen/lib/handlebars-helpers.ts b/codegen/lib/handlebars-helpers.ts index dd4abeda..4b8a4227 100644 --- a/codegen/lib/handlebars-helpers.ts +++ b/codegen/lib/handlebars-helpers.ts @@ -53,6 +53,12 @@ export const indent = (value: string, spaces: number): string => export const pythonIdentifier = (name: string): string => PYTHON_KEYWORDS.has(name) ? `${name}_` : name +// A param the API documents as nullable may be set to the NULL sentinel, which +// the client serializes to null. Params that are merely optional may not: they +// are omitted by passing None, and sending null would unset a value instead. +export const nullableType = (type: string, isNullable: boolean): string => + isNullable ? `Union[${type}, Null]` : type + export const isListType = (type: string): boolean => type.startsWith('List[') export const listItemType = (type: string): string => type.slice(5, -1) diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index be784fdc..00c57863 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -23,6 +23,7 @@ export interface MethodLayoutContext { params: Array<{ name: string type: string + isNullable: boolean description: string isDeprecated: boolean deprecationMessage: string @@ -53,6 +54,7 @@ export interface RouteLayoutContext { module: string }> importResolveActionAttempt: boolean + importNull: boolean methods: MethodLayoutContext[] } @@ -83,6 +85,7 @@ export const getMethodLayoutContext = ( params: sortClassMethodParameters(method.parameters).map((parameter) => ({ name: parameter.name, type: parameter.type, + isNullable: parameter.isNullable, description: parameter.description, isDeprecated: parameter.isDeprecated, deprecationMessage: parameter.deprecationMessage, @@ -108,6 +111,10 @@ export const setRouteLayoutContext = (cls: ClassModel): RouteLayoutContext => { const abstractClassName = `Abstract${cls.name}` const methods = cls.methods.map(getMethodLayoutContext) + const importNull = methods.some(({ params }) => + params.some(({ isNullable }) => isNullable), + ) + return { className: cls.name, abstractClassName, @@ -131,6 +138,7 @@ export const setRouteLayoutContext = (cls: ClassModel): RouteLayoutContext => { module: `${cls.namespace}_${identifier.namespace}`, })), importResolveActionAttempt, + importNull, methods, } } diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 2d5f33d1..cbc0a98a 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -99,6 +99,7 @@ export const routes = ( parameters: endpoint.request.parameters.map((parameter) => ({ name: parameter.name, type: mapParameterToPythonType(parameter), + isNullable: parameter.isNullable, description: parameter.description, isDeprecated: parameter.isDeprecated, deprecationMessage: parameter.deprecationMessage, diff --git a/seam/client.py b/seam/client.py index 723037f8..f78fefdf 100644 --- a/seam/client.py +++ b/seam/client.py @@ -1,3 +1,4 @@ +from collections.abc import Mapping from typing import Any, Dict, Optional from importlib.metadata import version import abc @@ -12,6 +13,8 @@ SeamHttpInvalidInputError, SeamHttpUnauthorizedError, ) +from .null import replace_null +from .url_search_params_serializer import serialize_url_search_params SDK_HEADERS = { "seam-sdk-name": "seamapi/python", @@ -102,6 +105,12 @@ def delete(self, url, json=None, **kwargs) -> Any: return self.request("DELETE", url, json=json, **kwargs) def request(self, method, url, *args, **kwargs) -> Any: + if isinstance(kwargs.get("params"), Mapping): + url = with_search_params(url, kwargs.pop("params")) + + if "json" in kwargs: + kwargs["json"] = replace_null(kwargs["json"]) + response = super().request(method, url, *args, **kwargs) return self._handle_response(response) @@ -142,6 +151,15 @@ def _handle_error_response(self, response: Response): raise SeamHttpApiError(error_details, status_code, request_id) +def with_search_params(url: Any, params: Mapping[str, Any]) -> Any: + query = serialize_url_search_params(params) + + if not query: + return url + + return httpx.URL(url, query=query.encode()) + + def is_api_error_response(response: Response) -> bool: try: content_type = response.headers.get("content-type", "") diff --git a/seam/null.py b/seam/null.py index 42c3b481..66a1cbd6 100644 --- a/seam/null.py +++ b/seam/null.py @@ -10,6 +10,7 @@ Sending null is explicit and always spelled :data:`NULL`. """ +from collections.abc import Mapping, Sequence from typing import Any @@ -60,3 +61,27 @@ def is_null(value: Any) -> bool: :returns: Whether the value is the ``NULL`` sentinel""" return isinstance(value, Null) + + +def replace_null(value: Any) -> Any: + """Returns a copy of a value with every :data:`NULL` sentinel replaced by ``None``. + + The sentinel only distinguishes an explicit null from an omitted param + within this SDK. Once a request body is being serialized, the param is + known to be present, so the sentinel becomes the null that JSON has. + + :param value: The value to copy + :type value: Any + + :returns: The value with each ``NULL`` sentinel replaced by ``None``""" + + if is_null(value): + return None + + if isinstance(value, Mapping): + return {key: replace_null(item) for key, item in value.items()} + + if isinstance(value, Sequence) and not isinstance(value, (str, bytes)): + return [replace_null(item) for item in value] + + return value diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index 82231033..3f5053f5 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import AccessCode from .access_codes_simulate import AbstractAccessCodesSimulate, AccessCodesSimulate from .access_codes_unmanaged import AbstractAccessCodesUnmanaged, AccessCodesUnmanaged @@ -205,7 +206,7 @@ def list( customer_key: Optional[str] = None, device_id: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identifier_key: Optional[str] = None, ) -> List[AccessCode]: @@ -704,7 +705,7 @@ def list( customer_key: Optional[str] = None, device_id: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identifier_key: Optional[str] = None, ) -> List[AccessCode]: diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index d8f35bc6..8a6e67d4 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import UnmanagedAccessCode @@ -71,7 +72,7 @@ def list( *, device_id: str, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identifier_key: Optional[str] = None, ) -> List[UnmanagedAccessCode]: @@ -253,7 +254,7 @@ def list( *, device_id: str, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identifier_key: Optional[str] = None, ) -> List[UnmanagedAccessCode]: diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index dfad2ccd..fbc7685a 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import AccessGrant, Batch from .access_grants_unmanaged import ( AbstractAccessGrantsUnmanaged, @@ -27,10 +28,10 @@ def create( acs_entrance_ids: Optional[List[str]] = None, customization_profile_id: Optional[str] = None, device_ids: Optional[List[str]] = None, - ends_at: Optional[str] = None, + ends_at: Optional[Union[str, Null]] = None, location: Optional[Dict[str, Any]] = None, location_ids: Optional[List[str]] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, reservation_key: Optional[str] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, @@ -130,14 +131,14 @@ def list( *, access_code_id: Optional[str] = None, access_grant_ids: Optional[List[str]] = None, - access_grant_key: Optional[str] = None, + access_grant_key: Optional[Union[str, Null]] = None, acs_entrance_id: Optional[str] = None, acs_system_id: Optional[str] = None, customer_key: Optional[str] = None, device_id: Optional[str] = None, limit: Optional[float] = None, location_id: Optional[str] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, reservation_key: Optional[str] = None, space_id: Optional[str] = None, user_identity_id: Optional[str] = None, @@ -194,8 +195,8 @@ def update( *, access_grant_id: Optional[str] = None, access_grant_key: Optional[str] = None, - ends_at: Optional[str] = None, - name: Optional[str] = None, + ends_at: Optional[Union[str, Null]] = None, + name: Optional[Union[str, Null]] = None, starts_at: Optional[str] = None, ) -> None: """Updates an existing Access Grant's time window. @@ -237,10 +238,10 @@ def create( acs_entrance_ids: Optional[List[str]] = None, customization_profile_id: Optional[str] = None, device_ids: Optional[List[str]] = None, - ends_at: Optional[str] = None, + ends_at: Optional[Union[str, Null]] = None, location: Optional[Dict[str, Any]] = None, location_ids: Optional[List[str]] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, reservation_key: Optional[str] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, @@ -434,14 +435,14 @@ def list( *, access_code_id: Optional[str] = None, access_grant_ids: Optional[List[str]] = None, - access_grant_key: Optional[str] = None, + access_grant_key: Optional[Union[str, Null]] = None, acs_entrance_id: Optional[str] = None, acs_system_id: Optional[str] = None, customer_key: Optional[str] = None, device_id: Optional[str] = None, limit: Optional[float] = None, location_id: Optional[str] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, reservation_key: Optional[str] = None, space_id: Optional[str] = None, user_identity_id: Optional[str] = None, @@ -551,8 +552,8 @@ def update( *, access_grant_id: Optional[str] = None, access_grant_key: Optional[str] = None, - ends_at: Optional[str] = None, - name: Optional[str] = None, + ends_at: Optional[Union[str, Null]] = None, + name: Optional[Union[str, Null]] = None, starts_at: Optional[str] = None, ) -> None: """Updates an existing Access Grant's time window. diff --git a/seam/routes/access_grants_unmanaged.py b/seam/routes/access_grants_unmanaged.py index 78eb8918..843709e8 100644 --- a/seam/routes/access_grants_unmanaged.py +++ b/seam/routes/access_grants_unmanaged.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import UnmanagedAccessGrant @@ -25,7 +26,7 @@ def list( acs_entrance_id: Optional[str] = None, acs_system_id: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, reservation_key: Optional[str] = None, user_identity_id: Optional[str] = None, ) -> List[UnmanagedAccessGrant]: @@ -113,7 +114,7 @@ def list( acs_entrance_id: Optional[str] = None, acs_system_id: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, reservation_key: Optional[str] = None, user_identity_id: Optional[str] = None, ) -> List[UnmanagedAccessGrant]: diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index 8c9ca9ee..915c4773 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ActionAttempt, AccessMethod, Batch from .access_methods_unmanaged import ( AbstractAccessMethodsUnmanaged, @@ -120,7 +121,7 @@ def list( acs_entrance_id: Optional[str] = None, device_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, space_id: Optional[str] = None, ) -> List[AccessMethod]: """Lists all access methods, usually filtered by Access Grant. @@ -393,7 +394,7 @@ def list( acs_entrance_id: Optional[str] = None, device_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, space_id: Optional[str] = None, ) -> List[AccessMethod]: """Lists all access methods, usually filtered by Access Grant. diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index 68a56846..ceff9409 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import AcsCredential, AcsEntrance @@ -107,7 +108,7 @@ def list( created_before: Optional[str] = None, is_multi_phone_sync_credential: Optional[bool] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_. @@ -382,7 +383,7 @@ def list( created_before: Optional[str] = None, is_multi_phone_sync_credential: Optional[bool] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_. diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index 194008aa..5b6d9c8a 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ActionAttempt, AcsEncoder from .acs_encoders_simulate import AbstractAcsEncodersSimulate, AcsEncodersSimulate from ..modules.action_attempts import resolve_action_attempt @@ -57,7 +58,7 @@ def list( acs_system_ids: Optional[List[str]] = None, acs_encoder_ids: Optional[List[str]] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, ) -> List[AcsEncoder]: """Returns a list of all `encoders `_. @@ -220,7 +221,7 @@ def list( acs_system_ids: Optional[List[str]] = None, acs_encoder_ids: Optional[List[str]] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, ) -> List[AcsEncoder]: """Returns a list of all `encoders `_. diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 353929ca..ddb863d4 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import AcsEntrance, AcsCredential, ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -49,8 +50,8 @@ def list( connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, limit: Optional[int] = None, - location_id: Optional[str] = None, - page_cursor: Optional[str] = None, + location_id: Optional[Union[str, Null]] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, ) -> List[AcsEntrance]: @@ -200,8 +201,8 @@ def list( connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, limit: Optional[int] = None, - location_id: Optional[str] = None, - page_cursor: Optional[str] = None, + location_id: Optional[Union[str, Null]] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, ) -> List[AcsEntrance]: diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 6e905b5e..1da621eb 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import AcsUser, AcsEntrance @@ -103,7 +104,7 @@ def list( acs_system_id: Optional[str] = None, created_before: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identity_email_address: Optional[str] = None, user_identity_id: Optional[str] = None, @@ -231,7 +232,7 @@ def unsuspend( def update( self, *, - access_schedule: Optional[Dict[str, Any]] = None, + access_schedule: Optional[Union[Dict[str, Any], Null]] = None, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, email: Optional[str] = None, @@ -444,7 +445,7 @@ def list( acs_system_id: Optional[str] = None, created_before: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identity_email_address: Optional[str] = None, user_identity_id: Optional[str] = None, @@ -691,7 +692,7 @@ def unsuspend( def update( self, *, - access_schedule: Optional[Dict[str, Any]] = None, + access_schedule: Optional[Union[Dict[str, Any], Null]] = None, acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, email: Optional[str] = None, diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index 977ad01b..8f0c221c 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -33,7 +34,7 @@ def list( action_attempt_ids: Optional[List[str]] = None, device_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, ) -> List[ActionAttempt]: """Returns a list of the `action attempts `_ that you specify as an array of ``action_attempt_id``s. @@ -105,7 +106,7 @@ def list( action_attempt_ids: Optional[List[str]] = None, device_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, ) -> List[ActionAttempt]: """Returns a list of the `action attempts `_ that you specify as an array of ``action_attempt_id``s. diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index 2310a432..053ed685 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ConnectWebview @@ -84,7 +85,7 @@ def list( custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identifier_key: Optional[str] = None, ) -> List[ConnectWebview]: @@ -253,7 +254,7 @@ def list( custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identifier_key: Optional[str] = None, ) -> List[ConnectWebview]: diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index 1d8a6a6f..7c50bfd2 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ConnectedAccount from .connected_accounts_simulate import ( AbstractConnectedAccountsSimulate, @@ -51,7 +52,7 @@ def list( custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, user_identifier_key: Optional[str] = None, @@ -196,7 +197,7 @@ def list( custom_metadata_has: Optional[Dict[str, Any]] = None, customer_key: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, user_identifier_key: Optional[str] = None, diff --git a/seam/routes/devices.py b/seam/routes/devices.py index 8fe8841c..e1b6dfbb 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import Device, DeviceProvider from .devices_simulate import AbstractDevicesSimulate, DevicesSimulate from .devices_unmanaged import AbstractDevicesUnmanaged, DevicesUnmanaged @@ -51,10 +52,10 @@ def list( device_types: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, + unstable_location_id: Optional[Union[str, Null]] = None, user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `devices `_. @@ -126,7 +127,7 @@ def update( backup_access_code_pool_enabled: Optional[bool] = None, custom_metadata: Optional[Dict[str, Any]] = None, is_managed: Optional[bool] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, properties: Optional[Dict[str, Any]] = None, ) -> None: """Updates a specified `device `_. @@ -212,10 +213,10 @@ def list( device_types: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, - unstable_location_id: Optional[str] = None, + unstable_location_id: Optional[Union[str, Null]] = None, user_identifier_key: Optional[str] = None, ) -> List[Device]: """Returns a list of all `devices `_. @@ -353,7 +354,7 @@ def update( backup_access_code_pool_enabled: Optional[bool] = None, custom_metadata: Optional[Dict[str, Any]] = None, is_managed: Optional[bool] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, properties: Optional[Dict[str, Any]] = None, ) -> None: """Updates a specified `device `_. diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 4b8b5792..12987d60 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import UnmanagedDevice @@ -40,7 +41,7 @@ def list( device_types: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedDevice]: """Returns a list of all `unmanaged devices `_. @@ -156,7 +157,7 @@ def list( device_types: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedDevice]: """Returns a list of all `unmanaged devices `_. diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index b72ea972..56f80f37 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import Space, Batch @@ -129,7 +130,7 @@ def list( *, customer_key: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_key: Optional[str] = None, ) -> List[Space]: @@ -465,7 +466,7 @@ def list( *, customer_key: Optional[str] = None, limit: Optional[float] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_key: Optional[str] = None, ) -> List[Space]: diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index b5ec077f..231090d3 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ActionAttempt, Device from .thermostats_daily_programs import ( AbstractThermostatsDailyPrograms, @@ -89,7 +90,7 @@ def create_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, ) -> None: """Creates a `climate preset `_ for a specified `thermostat `_. @@ -306,10 +307,10 @@ def set_temperature_threshold( self, *, device_id: str, - lower_limit_celsius: Optional[float] = None, - lower_limit_fahrenheit: Optional[float] = None, - upper_limit_celsius: Optional[float] = None, - upper_limit_fahrenheit: Optional[float] = None, + lower_limit_celsius: Optional[Union[float, Null]] = None, + lower_limit_fahrenheit: Optional[Union[float, Null]] = None, + upper_limit_celsius: Optional[Union[float, Null]] = None, + upper_limit_fahrenheit: Optional[Union[float, Null]] = None, ) -> None: """Sets a `temperature threshold `_ for a specified thermostat. Seam emits a ``thermostat.temperature_threshold_exceeded`` event and adds a warning on a thermostat if it reports a temperature outside the threshold range. @@ -341,7 +342,7 @@ def update_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, ) -> None: """Updates a specified `climate preset `_ for a specified `thermostat `_. @@ -377,13 +378,13 @@ def update_weekly_program( self, *, device_id: str, - friday_program_id: Optional[str] = None, - monday_program_id: Optional[str] = None, - saturday_program_id: Optional[str] = None, - sunday_program_id: Optional[str] = None, - thursday_program_id: Optional[str] = None, - tuesday_program_id: Optional[str] = None, - wednesday_program_id: Optional[str] = None, + friday_program_id: Optional[Union[str, Null]] = None, + monday_program_id: Optional[Union[str, Null]] = None, + saturday_program_id: Optional[Union[str, Null]] = None, + sunday_program_id: Optional[Union[str, Null]] = None, + thursday_program_id: Optional[Union[str, Null]] = None, + tuesday_program_id: Optional[Union[str, Null]] = None, + wednesday_program_id: Optional[Union[str, Null]] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Updates the thermostat weekly program for a thermostat device. To configure a weekly program, specify the ID of the daily program that you want to use for each day of the week. When you update a weekly program, the set of programs that you specify overwrites any previous weekly program for the thermostat. @@ -554,7 +555,7 @@ def create_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, ) -> None: """Creates a `climate preset `_ for a specified `thermostat `_. @@ -1012,10 +1013,10 @@ def set_temperature_threshold( self, *, device_id: str, - lower_limit_celsius: Optional[float] = None, - lower_limit_fahrenheit: Optional[float] = None, - upper_limit_celsius: Optional[float] = None, - upper_limit_fahrenheit: Optional[float] = None, + lower_limit_celsius: Optional[Union[float, Null]] = None, + lower_limit_fahrenheit: Optional[Union[float, Null]] = None, + upper_limit_celsius: Optional[Union[float, Null]] = None, + upper_limit_fahrenheit: Optional[Union[float, Null]] = None, ) -> None: """Sets a `temperature threshold `_ for a specified thermostat. Seam emits a ``thermostat.temperature_threshold_exceeded`` event and adds a warning on a thermostat if it reports a temperature outside the threshold range. @@ -1071,7 +1072,7 @@ def update_climate_preset( heating_set_point_fahrenheit: Optional[float] = None, hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, - name: Optional[str] = None, + name: Optional[Union[str, Null]] = None, ) -> None: """Updates a specified `climate preset `_ for a specified `thermostat `_. @@ -1145,13 +1146,13 @@ def update_weekly_program( self, *, device_id: str, - friday_program_id: Optional[str] = None, - monday_program_id: Optional[str] = None, - saturday_program_id: Optional[str] = None, - sunday_program_id: Optional[str] = None, - thursday_program_id: Optional[str] = None, - tuesday_program_id: Optional[str] = None, - wednesday_program_id: Optional[str] = None, + friday_program_id: Optional[Union[str, Null]] = None, + monday_program_id: Optional[Union[str, Null]] = None, + saturday_program_id: Optional[Union[str, Null]] = None, + sunday_program_id: Optional[Union[str, Null]] = None, + thursday_program_id: Optional[Union[str, Null]] = None, + tuesday_program_id: Optional[Union[str, Null]] = None, + wednesday_program_id: Optional[Union[str, Null]] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Updates the thermostat weekly program for a thermostat device. To configure a weekly program, specify the ID of the daily program that you want to use for each day of the week. When you update a weekly program, the set of programs that you specify overwrites any previous weekly program for the thermostat. diff --git a/seam/routes/thermostats_schedules.py b/seam/routes/thermostats_schedules.py index d708149e..7c4787ad 100644 --- a/seam/routes/thermostats_schedules.py +++ b/seam/routes/thermostats_schedules.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ThermostatSchedule @@ -16,7 +17,7 @@ def create( ends_at: str, starts_at: str, is_override_allowed: Optional[bool] = None, - max_override_period_minutes: Optional[int] = None, + max_override_period_minutes: Optional[Union[int, Null]] = None, name: Optional[str] = None, ) -> ThermostatSchedule: """Creates a new `thermostat schedule `_ for a specified `thermostat `_. @@ -83,7 +84,7 @@ def update( climate_preset_key: Optional[str] = None, ends_at: Optional[str] = None, is_override_allowed: Optional[bool] = None, - max_override_period_minutes: Optional[int] = None, + max_override_period_minutes: Optional[Union[int, Null]] = None, name: Optional[str] = None, starts_at: Optional[str] = None, ) -> None: @@ -125,7 +126,7 @@ def create( ends_at: str, starts_at: str, is_override_allowed: Optional[bool] = None, - max_override_period_minutes: Optional[int] = None, + max_override_period_minutes: Optional[Union[int, Null]] = None, name: Optional[str] = None, ) -> ThermostatSchedule: """Creates a new `thermostat schedule `_ for a specified `thermostat `_. @@ -272,7 +273,7 @@ def update( climate_preset_key: Optional[str] = None, ends_at: Optional[str] = None, is_override_allowed: Optional[bool] = None, - max_override_period_minutes: Optional[int] = None, + max_override_period_minutes: Optional[Union[int, Null]] = None, name: Optional[str] = None, starts_at: Optional[str] = None, ) -> None: diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index ab7442b2..a9107bc6 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import ( UserIdentity, InstantKey, @@ -51,10 +52,10 @@ def create( self, *, acs_system_ids: Optional[List[str]] = None, - email_address: Optional[str] = None, - full_name: Optional[str] = None, - phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None, + email_address: Optional[Union[str, Null]] = None, + full_name: Optional[Union[str, Null]] = None, + phone_number: Optional[Union[str, Null]] = None, + user_identity_key: Optional[Union[str, Null]] = None, ) -> UserIdentity: """Creates a new `user identity `_. @@ -137,7 +138,7 @@ def list( created_before: Optional[str] = None, credential_manager_acs_system_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identity_ids: Optional[List[str]] = None, ) -> List[UserIdentity]: @@ -229,10 +230,10 @@ def update( self, *, user_identity_id: str, - email_address: Optional[str] = None, - full_name: Optional[str] = None, - phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None, + email_address: Optional[Union[str, Null]] = None, + full_name: Optional[Union[str, Null]] = None, + phone_number: Optional[Union[str, Null]] = None, + user_identity_key: Optional[Union[str, Null]] = None, ) -> None: """Updates a specified `user identity `_. @@ -312,10 +313,10 @@ def create( self, *, acs_system_ids: Optional[List[str]] = None, - email_address: Optional[str] = None, - full_name: Optional[str] = None, - phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None, + email_address: Optional[Union[str, Null]] = None, + full_name: Optional[Union[str, Null]] = None, + phone_number: Optional[Union[str, Null]] = None, + user_identity_key: Optional[Union[str, Null]] = None, ) -> UserIdentity: """Creates a new `user identity `_. @@ -487,7 +488,7 @@ def list( created_before: Optional[str] = None, credential_manager_acs_system_id: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, user_identity_ids: Optional[List[str]] = None, ) -> List[UserIdentity]: @@ -704,10 +705,10 @@ def update( self, *, user_identity_id: str, - email_address: Optional[str] = None, - full_name: Optional[str] = None, - phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None, + email_address: Optional[Union[str, Null]] = None, + full_name: Optional[Union[str, Null]] = None, + phone_number: Optional[Union[str, Null]] = None, + user_identity_key: Optional[Union[str, Null]] = None, ) -> None: """Updates a specified `user identity `_. diff --git a/seam/routes/user_identities_unmanaged.py b/seam/routes/user_identities_unmanaged.py index 31627ad2..b177177a 100644 --- a/seam/routes/user_identities_unmanaged.py +++ b/seam/routes/user_identities_unmanaged.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import UnmanagedUserIdentity @@ -24,7 +25,7 @@ def list( *, created_before: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedUserIdentity]: """Returns a list of all unmanaged `user identities `_ (where is_managed = false). @@ -104,7 +105,7 @@ def list( *, created_before: Optional[str] = None, limit: Optional[int] = None, - page_cursor: Optional[str] = None, + page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedUserIdentity]: """Returns a list of all unmanaged `user identities `_ (where is_managed = false). diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index 09bbb529..01ee6655 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -2,6 +2,7 @@ import abc from ..client import SeamHttpClient from ..route import route_metadata +from ..null import Null from ..resources import Workspace, ActionAttempt from ..modules.action_attempts import resolve_action_attempt @@ -14,7 +15,7 @@ def create( *, name: str, company_name: Optional[str] = None, - connect_partner_name: Optional[str] = None, + connect_partner_name: Optional[Union[str, Null]] = None, connect_webview_customization: Optional[Dict[str, Any]] = None, is_sandbox: Optional[bool] = None, organization_id: Optional[str] = None, @@ -116,7 +117,7 @@ def create( *, name: str, company_name: Optional[str] = None, - connect_partner_name: Optional[str] = None, + connect_partner_name: Optional[Union[str, Null]] = None, connect_webview_customization: Optional[Dict[str, Any]] = None, is_sandbox: Optional[bool] = None, organization_id: Optional[str] = None, diff --git a/test/conftest.py b/test/conftest.py index 39a47693..0af77f8d 100755 --- a/test/conftest.py +++ b/test/conftest.py @@ -62,6 +62,8 @@ def recording_server(responses): content type inferred from the body, e.g. to serve malformed JSON. Yields the endpoint along with the list of requests received so far. + Each request records the ``method``, the raw request ``target``, the + ``path`` and ``query`` it splits into, the ``headers``, and the ``body``. """ requests = [] @@ -70,21 +72,17 @@ def recording_server(responses): class Handler(BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" - # pylint: disable-next=invalid-name - def do_GET(self): - self._handle_request() - - # pylint: disable-next=invalid-name - def do_POST(self): - self._handle_request() - def _handle_request(self): content_length = int(self.headers.get("content-length", 0)) raw_body = self.rfile.read(content_length) + path, _, query = self.path.partition("?") requests.append( { - "path": self.path, + "method": self.command, + "target": self.path, + "path": path, + "query": query, "headers": {k.lower(): v for k, v in self.headers.items()}, "body": json.loads(raw_body) if raw_body else None, } @@ -112,6 +110,14 @@ def _handle_request(self): def log_message(self, *args): pass + # Every verb the SDK sends is recorded and answered the same way. + # pylint: disable=invalid-name + do_GET = _handle_request + do_POST = _handle_request + do_PUT = _handle_request + do_PATCH = _handle_request + do_DELETE = _handle_request + server = ThreadingHTTPServer(("localhost", 0), Handler) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() diff --git a/test/headers_test.py b/test/headers_test.py index dfb040e0..ffdf34e6 100644 --- a/test/headers_test.py +++ b/test/headers_test.py @@ -17,7 +17,8 @@ def test_seam_sends_default_headers(recording_server): assert len(requests) == 1 [request] = requests - assert request["path"] == f"/devices/get?device_id={device_id}" + assert request["path"] == "/devices/get" + assert request["query"] == f"device_id={device_id}" assert request["body"] is None assert request["headers"]["seam-sdk-name"] == "seamapi/python" diff --git a/test/null_test.py b/test/null_test.py index 83db727c..e329bb3a 100644 --- a/test/null_test.py +++ b/test/null_test.py @@ -1,4 +1,4 @@ -from seam.null import NULL, Null, is_null +from seam.null import NULL, Null, is_null, replace_null def test_null_is_a_singleton(): @@ -20,3 +20,37 @@ def test_null_is_falsy(): def test_null_repr(): assert repr(NULL) == "NULL" + + +def test_replace_null_replaces_the_sentinel_with_none(): + assert replace_null(NULL) is None + + +def test_replace_null_recurses_into_dicts_and_lists(): + assert replace_null( + { + "name": NULL, + "properties": {"code": NULL, "kind": "lock"}, + "codes": [NULL, "1234", [NULL]], + "pairs": (NULL, "1234"), + } + ) == { + "name": None, + "properties": {"code": None, "kind": "lock"}, + "codes": [None, "1234", [None]], + "pairs": [None, "1234"], + } + + +def test_replace_null_leaves_other_values_alone(): + values = [None, "", 0, False, "NULL", {"a": 1}, ["b"]] + + assert replace_null(values) == values + + +def test_replace_null_does_not_mutate_its_argument(): + body = {"name": NULL, "codes": [NULL]} + + replace_null(body) + + assert body == {"name": NULL, "codes": [NULL]} diff --git a/test/nullable_param_test.py b/test/nullable_param_test.py new file mode 100644 index 00000000..9557020d --- /dev/null +++ b/test/nullable_param_test.py @@ -0,0 +1,52 @@ +"""Tests that generated params accept the NULL sentinel where the API allows it. + +These assertions are about types as much as behavior: the SDK is type checked, +so a nullable param losing its ``Null`` type, or a param that is merely +optional gaining one, fails the type check rather than any assertion here. +""" + +from seam import NULL, Seam + +DEVICE = {"device": {"device_id": "device1"}} + + +def test_a_nullable_param_is_sent_as_null(recording_server): + with recording_server([(200, DEVICE)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + # The API documents name as nullable, so it may be unset. + seam.devices.update(device_id="device1", name=NULL) + + [request] = requests + + assert request["body"] == {"device_id": "device1", "name": None} + + +def test_a_nullable_number_param_is_sent_as_null(recording_server): + with recording_server([(200, {})]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.thermostats.set_temperature_threshold( + device_id="device1", + lower_limit_celsius=NULL, + upper_limit_celsius=20.5, + ) + + [request] = requests + + assert request["body"] == { + "device_id": "device1", + "lower_limit_celsius": None, + "upper_limit_celsius": 20.5, + } + + +def test_an_omitted_param_is_not_sent(recording_server): + with recording_server([(200, DEVICE)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.devices.update(device_id="device1", name=None) + + [request] = requests + + assert request["body"] == {"device_id": "device1"} diff --git a/test/search_params_test.py b/test/search_params_test.py new file mode 100644 index 00000000..79545daf --- /dev/null +++ b/test/search_params_test.py @@ -0,0 +1,176 @@ +from typing import Any, Dict, List + +import pytest + +from seam import NULL, Seam, UnserializableParamError + +DEVICE = {"device": {"device_id": "device1"}} +DEVICES: Dict[str, List[Any]] = {"devices": []} + + +def test_client_serializes_search_params(recording_server): + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.get( + "/devices/list", + params={ + "device_ids": ["device1", "device2"], + "custom_metadata_has": {"tag": "front", "floor": 2}, + "limit": 20, + }, + ) + + [request] = requests + + assert request["method"] == "GET" + assert request["path"] == "/devices/list" + assert request["query"] == ( + "custom_metadata_has.floor=2" + "&custom_metadata_has.tag=front" + "&device_ids=device1" + "&device_ids=device2" + "&limit=20" + ) + + +def test_client_does_not_reencode_the_serialized_search_params(recording_server): + """The serializer and httpx disagree on exactly two characters. + + httpx escapes ``*`` and leaves ``~`` alone, so a query it encodes is not + the one the serializer produced. Setting the query on the url keeps ours. + """ + + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.get("/devices/list", params={"search": "a *~ b"}) + + [request] = requests + + assert request["query"] == "search=a+*%7E+b" + + +def test_client_omits_search_params_set_to_none(recording_server): + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.get("/devices/list", params={"search": None, "limit": 20}) + + [request] = requests + + assert request["query"] == "limit=20" + + +def test_client_serializes_search_params_set_to_null(recording_server): + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.get("/devices/list", params={"search": NULL, "limit": 20}) + + [request] = requests + + assert request["query"] == "limit=20&search=" + + +def test_client_sends_no_query_string_without_search_params(recording_server): + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.get("/devices/list", params={}) + seam.client.get("/devices/list", params={"search": None}) + seam.client.get("/devices/list") + + for request in requests: + assert request["target"] == "/devices/list" + + +def test_client_serializes_search_params_of_every_verb(recording_server): + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.get("/devices/list", params={"device_ids": ["device1"]}) + seam.client.delete("/access_codes/delete", params={"sync": True}) + + assert [(request["method"], request["query"]) for request in requests] == [ + ("GET", "device_ids=device1"), + ("DELETE", "sync=true"), + ] + + +def test_client_passes_search_params_it_did_not_serialize_to_httpx(recording_server): + """Params that are not a mapping are left for httpx to encode. + + A caller who serialized the params themselves, e.g. to the pairs of a + ``UrlSearchParams``, has already chosen how they are represented. + """ + + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.get("/devices/list", params=[("device_ids", "device1")]) + + [request] = requests + + assert request["query"] == "device_ids=device1" + + +def test_client_rejects_a_search_param_it_cannot_serialize(recording_server): + with recording_server([(200, DEVICES)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + with pytest.raises(UnserializableParamError): + seam.client.get("/devices/list", params={"search": object()}) + + assert requests == [] + + +def test_client_serializes_null_in_a_json_body_to_null(recording_server): + with recording_server([(200, DEVICE)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.post( + "/devices/update", + json={ + "device_id": "device1", + "name": NULL, + "properties": {"code": NULL}, + "codes": [NULL, "1234"], + }, + ) + + [request] = requests + + assert request["method"] == "POST" + assert request["body"] == { + "device_id": "device1", + "name": None, + "properties": {"code": None}, + "codes": [None, "1234"], + } + + +def test_client_leaves_a_json_body_without_null_unchanged(recording_server): + body = {"device_id": "device1", "name": "Front Door", "limit": 20, "sync": True} + + with recording_server([(200, DEVICE)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.client.post("/devices/update", json=body) + + [request] = requests + + assert request["body"] == body + + +def test_client_serializes_the_search_params_of_a_generated_route(recording_server): + with recording_server([(200, DEVICE)]) as (endpoint, requests): + seam = Seam.from_api_key("seam_apikey_token", endpoint=endpoint) + + seam.devices.get(name="Front Door") + + [request] = requests + + assert request["method"] == "GET" + assert request["path"] == "/devices/get" + assert request["query"] == "name=Front+Door" From d8d070e31875bfc859c636c3d582ea32c1f29015 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 18:30:52 +0000 Subject: [PATCH 37/39] 3.0.0b6 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index de70fce4..9cdd4bd4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "seam" -version = "3.0.0b5" +version = "3.0.0b6" description = "SDK for the Seam API written in Python." authors = [{ name = "Seam Labs, Inc.", email = "engineering@getseam.com" }] license = "MIT" diff --git a/uv.lock b/uv.lock index c38fbd89..523558be 100644 --- a/uv.lock +++ b/uv.lock @@ -943,7 +943,7 @@ wheels = [ [[package]] name = "seam" -version = "3.0.0b5" +version = "3.0.0b6" source = { editable = "." } dependencies = [ { name = "httpx" }, From c826e9f237f5ce489229be939b2bcdcc0ba565b0 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 11:51:13 -0700 Subject: [PATCH 38/39] Add migration guide for seam v2 to v3 upgrade (#615) * docs: add v2 to v3 migration guide Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GK2tFMid8Mus3fsRRwgKBY * ci: Format code * docs: drop removed endpoint params section and add v1 to v2 guide Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GK2tFMid8Mus3fsRRwgKBY --------- Co-authored-by: Claude Co-authored-by: Seam Bot --- MIGRATION.md | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 MIGRATION.md diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 00000000..6f501a6f --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,263 @@ +# Migrating from seam v2 to v3 + +This guide covers upgrading from `seam` v2.x to v3 of the [Seam Python SDK](https://github.com/seamapi/python). + +Version 3 replaces the underlying HTTP library, adds client-side validation and explicit null support, and regenerates the API surface against the latest Seam API. Most application code — authentication, method names, resource models, action attempts, and pagination — works unchanged. The breaking changes are concentrated in client configuration and error handling. + +## Installation + +While v3 is in prerelease, install it explicitly: + +```sh +pip install --pre seam +# or pin a specific beta +pip install 'seam==3.0.0b6' +``` + +## Summary of breaking changes + +| Change | Affects you if... | +| ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| [Python 3.11+ required](#python-311-or-later-is-required) | You run Python 3.10 | +| [httpx replaces niquests](#httpx-replaces-niquests) | You pass `niquests_options`, catch `niquests` exceptions, or touch `seam.client` directly | +| [`retries` takes an `httpx_retries.Retry`](#retry-configuration-uses-httpx-retries) | You pass a custom `retries` option | +| [Endpoints validate parameters client-side](#client-side-parameter-validation) | You call endpoints with no parameters, or rely on the server's 400 response | +| [`lts_version` removed](#lts_version-is-removed) | You read `Seam.lts_version` or the `seam-lts-version` header | +| [Preferred HTTP methods and URL search params](#endpoints-use-preferred-http-methods) | You inspect traffic in a proxy, mock server, or firewall rules | + +## Python 3.11 or later is required + +Version 2 supported Python 3.10. Version 3 requires Python >= 3.11 and is tested on Python 3.11 through 3.14. + +## httpx replaces niquests + +The SDK's HTTP layer is now [httpx](https://www.python-httpx.org/) instead of [niquests](https://niquests.readthedocs.io/). This surfaces in three places. + +### The `niquests_options` option is renamed to `httpx_options` + +Options are now passed to the underlying `httpx.Client`, so both the option name and its contents change. For example, connection pool limits: + +```python +# v2 +seam = Seam( + api_key="your-api-key", + niquests_options={"pool_connections": 20, "pool_maxsize": 25}, +) + +# v3 +from httpx import Limits + +seam = Seam( + api_key="your-api-key", + httpx_options={ + "limits": Limits(max_connections=25, max_keepalive_connections=20), + }, +) +``` + +This applies to `Seam()`, `Seam.from_api_key()`, `Seam.from_personal_access_token()`, and `SeamWithoutWorkspace`. + +### Transport-level exceptions are httpx exceptions + +Requests that time out now raise `httpx.TimeoutException` instead of `niquests.exceptions.Timeout`, and connection failures raise httpx transport errors (`httpx.ConnectError`, etc.) instead of niquests/urllib3 ones. + +```python +# v2 +import niquests + +try: + seam.devices.list() +except niquests.exceptions.Timeout: + ... + +# v3 +import httpx + +try: + seam.devices.list() +except httpx.TimeoutException: + ... +``` + +Seam API errors are unchanged: `SeamHttpApiError`, `SeamHttpInvalidInputError`, and `SeamHttpUnauthorizedError` are raised exactly as in v2. + +### `seam.client` is an httpx.Client + +If you access the client directly, it is now an `httpx.Client` subclass rather than a niquests `Session`. Notably, response hooks are registered via `event_hooks` instead of `hooks`. + +## Retry configuration uses httpx-retries + +The `retries` option now takes a `Retry` object from [httpx-retries](https://will-ockmore.github.io/httpx-retries/) instead of `urllib3.util.retry.Retry`. The class is re-exported from `seam` for convenience: + +```python +# v2 +from urllib3.util.retry import Retry + +seam = Seam(api_key="your-api-key", retries=Retry(total=3)) + +# v3 +from seam import Seam, Retry + +seam = Seam( + api_key="your-api-key", + retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[503]), +) +``` + +The default retry policy is now explicit and documented. Out of the box, the SDK makes up to three attempts: the initial request and two retries. Retries are limited to `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE` requests that fail because of a transport error, timeout, HTTP 429 response, or HTTP 5xx response. `POST` and `PATCH` requests are never retried. Retries use exponential backoff with jitter, and a `Retry-After` header is honored instead of the calculated backoff. + +In v2, the default was urllib3's implicit `Retry()` (connection-level retries only, with no retries on HTTP status codes such as 429 or 5xx). If you depended on requests never being retried on 429/5xx, pass an explicit policy, e.g. `retries=Retry(total=0)`. + +## Client-side parameter validation + +Endpoints that require at least one parameter now raise `ValueError` locally instead of sending the request and letting the server reject it: + +```python +# v2: raises SeamHttpInvalidInputError after a round trip to the server +# v3: raises ValueError("At least one parameter is required for /locks/get") +seam.locks.get() +``` + +`create_paginator` is validated the same way. It raises `ValueError` when given a non-paginated endpoint, and when given an endpoint that requires parameters without any: + +```python +# v3: raises ValueError - /devices/get is not paginated +seam.create_paginator(seam.devices.get) +``` + +If you catch `SeamHttpInvalidInputError` around calls that could be sent with no parameters, also handle `ValueError` (or fix the call site). + +## `lts_version` is removed + +The `Seam.lts_version` / `SeamWithoutWorkspace.lts_version` attribute and the `seam-lts-version` request header no longer exist. There is no replacement; use the package version instead: + +```python +from importlib.metadata import version + +version("seam") +``` + +## Endpoints use preferred HTTP methods + +In v2, every endpoint was called with `POST` and a JSON body. In v3, endpoints use the HTTP method the Seam API prefers: + +- Read endpoints (`get`, `list`, and friends) use `GET`, with parameters sent as URL search params serialized per [Seam's URL search params standard](https://github.com/seamapi/url-search-params-serializer). +- Update endpoints use `PATCH` or `PUT`. +- Delete endpoints use `DELETE`. +- Create and action endpoints (`create`, `lock_door`, etc.) remain `POST`. + +Method signatures, arguments, and return values are unchanged — this only matters if something outside your code observes the HTTP traffic: proxy or firewall rules that allowlist methods, request logging, or test mocks registered against `POST` routes. Note the interaction with the new retry defaults: because reads are now `GET`, they are retried by default, which they were not in v2 (as `POST`). + +If you call the Seam API with your own HTTP client, the serializer used for `GET` params is exported: + +```python +import httpx +from seam import serialize_url_search_params + +httpx.get( + "https://connect.getseam.com/devices/list", + params=serialize_url_search_params({"device_ids": ["device1", "device2"]}), + headers={"Authorization": "Bearer your-api-key"}, +) +``` + +## New in v3 + +These are additions, not breaking changes, but they are worth adopting while you migrate. + +### Explicit null with `NULL` + +The Seam API distinguishes an omitted parameter from one explicitly set to null: in an update request, an omitted parameter leaves the current value unchanged, while a null parameter unsets it. Version 2 had no way to send null — `None` always meant "omit". Version 3 keeps that behavior for `None` and adds a `NULL` sentinel for sending an explicit null: + +```python +from seam import NULL, Seam + +seam = Seam() + +# Leaves the name unchanged (same as v2). +seam.devices.update(device_id="your-device-id", name=None) + +# Unsets the name (new in v3). +seam.devices.update(device_id="your-device-id", name=NULL) +``` + +Only parameters the Seam API documents as nullable are typed to accept `NULL`, so a type checker will flag misuse. The sentinel's type is exported as `Null` for annotating your own code. + +### New exports + +`seam` now exports `NULL`, `Null`, `Retry` (from httpx-retries), `UrlSearchParams`, `serialize_url_search_params`, `update_url_search_params`, and `UnserializableParamError`, alongside everything exported in v2. + +## Migration checklist + +1. Upgrade your runtime to Python 3.11 or later. +2. Update the dependency: `seam>=3,<4` (or a pinned `3.0.0bN` while in prerelease). +3. Rename `niquests_options` to `httpx_options` and translate its contents to `httpx.Client` options. +4. Replace `urllib3.util.retry.Retry` with `seam.Retry` (httpx-retries) in any `retries` argument, and review the new default retry policy. +5. Replace handling of `niquests`/`urllib3` exceptions with the `httpx` equivalents (`httpx.TimeoutException`, `httpx.ConnectError`, ...). Seam error classes are unchanged. +6. Remove any use of `lts_version` or the `seam-lts-version` header. +7. Handle `ValueError` from endpoints and `create_paginator` where calls might carry no parameters. +8. If proxies, firewalls, or test mocks assume all requests are `POST`, update them for `GET`/`PATCH`/`PUT`/`DELETE`. +9. Optionally, adopt `NULL` where you need to unset nullable values. + +# Migrating from seam v1 to v2 + +If you are still on v1.x, migrate to v2 first (or apply both guides together). Version 2 is a much smaller upgrade than v3: client configuration, authentication, endpoint methods, and error handling are all unchanged. The breaking changes are in resource objects and one class rename. + +## Summary of breaking changes + +| Change | Affects you if... | +| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| [Nested resource properties are typed objects](#nested-resource-properties-are-typed-objects) | You treat nested properties as dicts, or rely on unknown-attribute reads | +| [`SeamMultiWorkspace` renamed to `SeamWithoutWorkspace`](#seammultiworkspace-is-renamed-to-seamwithoutworkspace) | You use `SeamMultiWorkspace` | + +## Nested resource properties are typed objects + +In v1, nested properties on resources — for example `device.properties` or `action_attempt.result` — were dict subclasses with attribute access layered on top. In v2, they hydrate as typed dataclasses scoped to their parent resource, such as `Device.Properties` and `ActionAttempt.Result`, so IDEs and type checkers can see their fields. + +Attribute access and dictionary-style _reads_ keep working: + +```python +device = seam.devices.get(device_id="your-device-id") + +device.properties.locked # still works +device.properties["locked"] # still works +device.properties.get("online") # still works +"locked" in device.properties # still works +``` + +What breaks: + +- **They are no longer dicts.** `isinstance(device.properties, dict)` is now `False`, and mutation (`device.properties["x"] = ...`) and dict-only methods such as `.items()` and `.values()` are gone. Iterate over `.keys()` and index instead. +- **Typoed attributes raise `AttributeError`.** In v1, reading an unknown attribute silently returned (and inserted) an empty mapping, so typos went unnoticed and were truthy-checked as empty dicts. In v2 they fail loudly — code that probed for optional fields via bare attribute access should use `.get("field")` or `hasattr`. +- **Undocumented nested fields are stripped.** API fields not (yet) in the SDK's generated types are dropped during hydration instead of being passed through. If you depend on a field the SDK does not model, upgrade the SDK to a version that includes it. + +Free-form record properties, such as `custom_metadata`, remain plain mappings and are not affected. + +## `SeamMultiWorkspace` is renamed to `SeamWithoutWorkspace` + +The client for personal access tokens without a workspace is renamed; there is no compatibility alias. Its constructor, options, and methods are otherwise identical: + +```python +# v1 +from seam import SeamMultiWorkspace + +seam = SeamMultiWorkspace(personal_access_token="your-personal-access-token") + +# v2 +from seam import SeamWithoutWorkspace + +seam = SeamWithoutWorkspace(personal_access_token="your-personal-access-token") +``` + +The abstract base class is likewise renamed from `AbstractSeamMultiWorkspace` to `AbstractSeamWithoutWorkspace`. + +## New in v2 + +Version 2.2 also reads authentication from the environment: `SEAM_PERSONAL_ACCESS_TOKEN` and `SEAM_WORKSPACE_ID` are picked up when no explicit credentials are passed (`SEAM_API_KEY` was already supported in v1). Setting both `SEAM_API_KEY` and `SEAM_PERSONAL_ACCESS_TOKEN` is an error. + +## Migration checklist + +1. Update the dependency: `seam>=2,<3`. +2. Rename `SeamMultiWorkspace` to `SeamWithoutWorkspace` (and `AbstractSeamMultiWorkspace` to `AbstractSeamWithoutWorkspace`). +3. Replace dict-style mutation and `.items()`/`.values()`/`isinstance(..., dict)` usage on nested resource properties with attribute access or `.keys()` iteration. +4. Replace bare attribute probes for optional nested fields with `.get()` or `hasattr` — unknown attributes now raise `AttributeError`. From 491b2c4a64b95b5b45731cc2b9e60978e59c545f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 18:53:59 +0000 Subject: [PATCH 39/39] docs: reference stable v3.0.0 instead of prereleases in migration guide Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GK2tFMid8Mus3fsRRwgKBY --- MIGRATION.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 6f501a6f..edbe6d99 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -6,12 +6,8 @@ Version 3 replaces the underlying HTTP library, adds client-side validation and ## Installation -While v3 is in prerelease, install it explicitly: - ```sh -pip install --pre seam -# or pin a specific beta -pip install 'seam==3.0.0b6' +pip install --upgrade 'seam>=3,<4' ``` ## Summary of breaking changes @@ -190,7 +186,7 @@ Only parameters the Seam API documents as nullable are typed to accept `NULL`, s ## Migration checklist 1. Upgrade your runtime to Python 3.11 or later. -2. Update the dependency: `seam>=3,<4` (or a pinned `3.0.0bN` while in prerelease). +2. Update the dependency: `seam>=3,<4`. 3. Rename `niquests_options` to `httpx_options` and translate its contents to `httpx.Client` options. 4. Replace `urllib3.util.retry.Retry` with `seam.Retry` (httpx-retries) in any `retries` argument, and review the new default retry policy. 5. Replace handling of `niquests`/`urllib3` exceptions with the `httpx` equivalents (`httpx.TimeoutException`, `httpx.ConnectError`, ...). Seam error classes are unchanged.