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..2f9e5640 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -5,12 +5,11 @@
"dockerfile": "Dockerfile",
"args": {
"NODE_VERSION": "24",
- "POETRY_VERSION": "1.8.2",
- "VARIANT": "3.12"
+ "UV_VERSION": "0.8.17",
+ "VARIANT": "3.14"
}
},
"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..e6a5cbdb 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -6,11 +6,7 @@ inputs:
python_version:
description: The Python version.
required: false
- default: '3.12'
- poetry_version:
- description: The Poetry version.
- required: false
- default: '1.8.2'
+ default: '3.14'
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/_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/_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/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/.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/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
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 9bde1873..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: |
- poetry version "${{ github.event.inputs.version }}"
+ 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."
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/.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/MIGRATION.md b/MIGRATION.md
new file mode 100644
index 00000000..edbe6d99
--- /dev/null
+++ b/MIGRATION.md
@@ -0,0 +1,259 @@
+# 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
+
+```sh
+pip install --upgrade 'seam>=3,<4'
+```
+
+## 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`.
+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`.
diff --git a/README.rst b/README.rst
index 65cad433..d93f018a 100644
--- a/README.rst
+++ b/README.rst
@@ -47,9 +47,11 @@ Contents
* `Action Attempts`_
+ * `Setting a Param to Null`_
+
* `Pagination`_
- * `Manually fetch pages with the nextPageCursor`_
+ * `Manually fetch pages with the next_page_cursor`_
* `Resume pagination`_
@@ -69,7 +71,11 @@ Contents
* `Setting the request timeout`_
- * `Configuring the niquests session`_
+ * `Configuring retries`_
+
+ * `Configuring the httpx client`_
+
+ * `Serializing URL search params`_
* `Development and Testing`_
@@ -276,14 +282,64 @@ 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
~~~~~~~~~~
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
@@ -470,22 +526,105 @@ 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
+^^^^^^^^^^^^^^^^^^^
+
+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.
-Configuring the niquests session
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+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:
-For control the options above do not cover, pass ``niquests_options``.
-These are handed to the underlying niquests ``Session`` and take
+.. code-block:: python
+
+ from seam import Seam, Retry
+
+ 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),
+ },
)
+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 ``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`_.
+
+.. _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
-----------------------
@@ -496,7 +635,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 +660,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 +700,7 @@ Run tests on changes with
Publishing
~~~~~~~~~~
-New versions are created with `poetry version`_.
+New versions are created with `uv version`_.
Automatic
^^^^^^^^^
@@ -576,7 +715,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 +723,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/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/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/partials/resource-dataclass.hbs b/codegen/layouts/partials/resource-dataclass.hbs
index 8a923753..7375389b 100644
--- a/codegen/layouts/partials/resource-dataclass.hbs
+++ b/codegen/layouts/partials/resource-dataclass.hbs
@@ -16,9 +16,8 @@
{{/each}}
{{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
{{/unless}}
{{memberIndent}} return cls(
diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs
index c009161a..b615e7f8 100644
--- a/codegen/layouts/partials/route-method.hbs
+++ b/codegen/layouts/partials/route-method.hbs
@@ -1,13 +1,19 @@
+ @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}}"""
- json_payload = {}
+ {{payloadVar}}: Dict[str, Any] = {}
{{#each params}}
if {{name}} is not None:
- json_payload["{{name}}"] = {{name}}
+ {{../payloadVar}}["{{name}}"] = {{name}}
{{/each}}
+{{#if hasRequiredParameters}}
- {{#unless (eq returnType "None")}}res = {{/unless}}self.client.post("{{path}}", json=json_payload)
+ 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")}}
wait_for_action_attempt = (
diff --git a/codegen/layouts/route.hbs b/codegen/layouts/route.hbs
index 1793adfb..32540f82 100644
--- a/codegen/layouts/route.hbs
+++ b/codegen/layouts/route.hbs
@@ -1,6 +1,10 @@
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 9937221a..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
@@ -14,6 +15,9 @@ export interface ClassMethodParameter {
export interface ClassMethod {
methodName: string
path: string
+ preferredMethod: string
+ hasRequiredParameters: boolean
+ hasPagination: boolean
description: string
responseDescription: string
isDeprecated: boolean
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/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/layouts/route.ts b/codegen/lib/layouts/route.ts
index 8e57326c..00c57863 100644
--- a/codegen/lib/layouts/route.ts
+++ b/codegen/lib/layouts/route.ts
@@ -11,6 +11,11 @@ import {
export interface MethodLayoutContext {
name: string
path: string
+ httpVerb: string
+ payloadVar: string
+ payloadArg: string
+ hasRequiredParameters: boolean
+ hasPagination: boolean
description: string
responseDescription: string
isDeprecated: boolean
@@ -18,6 +23,7 @@ export interface MethodLayoutContext {
params: Array<{
name: string
type: string
+ isNullable: boolean
description: string
isDeprecated: boolean
deprecationMessage: string
@@ -48,14 +54,30 @@ export interface RouteLayoutContext {
module: string
}>
importResolveActionAttempt: boolean
+ importNull: boolean
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),
+ hasRequiredParameters: method.hasRequiredParameters,
+ hasPagination: method.hasPagination,
description: method.description,
responseDescription: method.responseDescription,
isDeprecated: method.isDeprecated,
@@ -63,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,
@@ -88,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,
@@ -111,6 +138,7 @@ export const setRouteLayoutContext = (cls: ClassModel): RouteLayoutContext => {
module: `${cls.namespace}_${identifier.namespace}`,
})),
importResolveActionAttempt,
+ importNull,
methods,
}
}
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/codegen/lib/routes.ts b/codegen/lib/routes.ts
index 0cd065d2..cbc0a98a 100644
--- a/codegen/lib/routes.ts
+++ b/codegen/lib/routes.ts
@@ -89,6 +89,9 @@ export const routes = (
cls.methods.push({
methodName: endpoint.name,
path: endpoint.path,
+ preferredMethod: endpoint.request.preferredMethod,
+ hasRequiredParameters: endpoint.request.hasRequiredParameters,
+ hasPagination: endpoint.hasPagination,
description: endpoint.description,
responseDescription: endpoint.response.description,
isDeprecated: endpoint.isDeprecated,
@@ -96,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/justfile b/justfile
index d55b9a0b..ed397262 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 test
@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/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"
}
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/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/pyproject.toml b/pyproject.toml
index c644d3a8..9cdd4bd4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,34 +1,48 @@
-[tool.poetry]
+[project]
name = "seam"
-version = "2.2.0"
+version = "3.0.0b6"
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.11"
+dependencies = [
+ "httpx>=0.23.0,<1",
+ "httpx-retries>=0.6.0,<1",
+ "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>=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.3.0,<7",
+ "mypy>=2.3.0,<3",
+]
[build-system]
-requires = ["poetry>=1.8"]
-build-backend = "poetry.masonry.api"
+requires = ["uv_build>=0.12.0,<0.13.0"]
+build-backend = "uv_build"
+
+[tool.uv]
+required-version = ">=0.12.0,<0.13.0"
+
+[tool.uv.build-backend]
+module-root = ""
+source-exclude = ["**/*_test.py"]
+wheel-exclude = ["**/*_test.py"]
+
+[tool.black]
+target-version = ["py311"]
[tool.pytest.ini_options]
norecursedirs = [
diff --git a/seam/__init__.py b/seam/__init__.py
index 6e62d98d..4c912626 100644
--- a/seam/__init__.py
+++ b/seam/__init__.py
@@ -1,8 +1,8 @@
# flake8: noqa
-# type: ignore
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 (
@@ -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/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 644b7ad5..f78fefdf 100644
--- a/seam/client.py
+++ b/seam/client.py
@@ -1,28 +1,32 @@
+from collections.abc import Mapping
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
-from .constants import DEFAULT_TIMEOUT, LTS_VERSION
+import httpx
+from httpx import Response
+from httpx_retries import Retry, RetryTransport
+
+from .constants import DEFAULT_TIMEOUT
from .exceptions import (
SeamHttpApiError,
SeamHttpInvalidInputError,
SeamHttpUnauthorizedError,
)
+from .null import replace_null
+from .url_search_params_serializer import serialize_url_search_params
SDK_HEADERS = {
"seam-sdk-name": "seamapi/python",
"seam-sdk-version": version("seam"),
- "seam-lts-version": LTS_VERSION,
}
-DEFAULT_RETRIES = Retry()
-
-NIQUESTS_TIMEOUT_DEFAULT = (
- signature(requests.Session.post).parameters["timeout"].default
+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,
)
@@ -36,55 +40,82 @@ 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):
+ 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,
- **kwargs
+ 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", {})
+ self._retry_policy = DEFAULT_RETRIES if retries is None else retries
super().__init__(**options)
- self.base_url = base_url
-
- self.timeout = timeout
-
headers = {**auth_headers, **custom_headers, **SDK_HEADERS}
self.headers.update(headers)
- def request(self, method, url, *args, **kwargs):
- url = urljoin(self.base_url, url)
+ 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
+ # type and indexing the returned payload does not type check.
+ def get(self, url, **kwargs) -> Any:
+ return self.request("GET", url, **kwargs)
+
+ def post(self, url, data=None, json=None, **kwargs) -> Any:
+ return self.request("POST", url, data=data, json=json, **kwargs)
- if kwargs.get("timeout", NIQUESTS_TIMEOUT_DEFAULT) == NIQUESTS_TIMEOUT_DEFAULT:
- kwargs["timeout"] = self.timeout
+ 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)
+
+ 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)
- def _handle_response(self, response: requests.Response):
+ def _handle_response(self, response: Response):
if not 200 <= response.status_code < 300:
self._handle_error_response(response)
@@ -93,7 +124,7 @@ def _handle_response(self, response: requests.Response):
return response.text
- def _handle_error_response(self, response: requests.Response):
+ def _handle_error_response(self, response: Response):
status_code = response.status_code
request_id = response.headers.get("seam-request-id")
@@ -120,7 +151,16 @@ def _handle_error_response(self, response: requests.Response):
raise SeamHttpApiError(error_details, status_code, request_id)
-def is_api_error_response(response: requests.Response) -> bool:
+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", "")
@@ -130,7 +170,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/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/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/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/modules/action_attempts.py b/seam/modules/action_attempts.py
index 0f4da8b3..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
@@ -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/null.py b/seam/null.py
new file mode 100644
index 00000000..66a1cbd6
--- /dev/null
+++ b/seam/null.py
@@ -0,0 +1,87 @@
+"""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 collections.abc import Mapping, Sequence
+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)
+
+
+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/paginator.py b/seam/paginator.py
index fedb86d9..62aa3a9b 100644
--- a/seam/paginator.py
+++ b/seam/paginator.py
@@ -1,6 +1,7 @@
-from typing import Callable, Dict, Any, Tuple, Generator, List
+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 Response, JSONDecodeError
from .pagination import Pagination
@@ -17,7 +18,7 @@ def __init__(
self,
client: SeamHttpClient,
request: Callable,
- params: Dict[str, Any] = None,
+ params: Optional[Dict[str, Any]] = None,
):
"""
Initializes the Paginator.
@@ -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)
@@ -74,7 +75,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)
@@ -95,6 +96,8 @@ def flatten(self) -> Generator[Any, None, None]:
def _cache_pagination(self, response: Response, page_key: str) -> None:
"""Extracts pagination dict from response, creates Pagination object, and caches it."""
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/resources/access_code.py b/seam/resources/access_code.py
index 00fc66a5..7991193d 100644
--- a/seam/resources/access_code.py
+++ b/seam/resources/access_code.py
@@ -86,17 +86,17 @@ 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]
@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 +146,31 @@ class ModifiedFields(ResourceMapping):
:ivar to: The new value of the field."""
field: str
- from_: str
- to: str
+ from_: Optional[str]
+ to: Optional[str]
@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]
@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 +216,13 @@ 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]
@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 +242,13 @@ 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]
@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 +259,12 @@ 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]
@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 +304,25 @@ class ModifiedFields(ResourceMapping):
:ivar to: The new value of the field."""
field: str
- from_: str
- to: str
+ from_: Optional[str]
+ to: Optional[str]
@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]]
@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 +335,32 @@ 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
@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..1d442c5f 100644
--- a/seam/resources/access_grant.py
+++ b/seam/resources/access_grant.py
@@ -64,10 +64,10 @@ class Errors(ResourceMapping):
created_at: str
error_code: str
message: str
- missing_device_ids: List[str]
+ missing_device_ids: Optional[List[str]]
@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 +101,12 @@ 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]
@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 +125,13 @@ 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]
@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 +140,14 @@ 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]]
@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 +178,15 @@ 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
@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),
@@ -234,7 +234,7 @@ class FailedDevices(ResourceMapping):
message: str
@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 +244,15 @@ 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]
@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,20 +269,20 @@ 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
@@ -290,7 +290,7 @@ def from_dict(cls, d: Dict[str, Any]):
workspace_id: str
@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..d7614ada 100644
--- a/seam/resources/access_method.py
+++ b/seam/resources/access_method.py
@@ -60,7 +60,7 @@ class Errors(ResourceMapping):
message: str
@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 +91,12 @@ 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]
@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 +113,12 @@ 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]
@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 +126,13 @@ 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]
@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 +161,10 @@ class Warnings(ResourceMapping):
created_at: str
message: str
warning_code: str
- original_access_method_id: str
+ original_access_method_id: Optional[str]
@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 +173,26 @@ 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
@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..ca32527b 100644
--- a/seam/resources/acs_access_group.py
+++ b/seam/resources/acs_access_group.py
@@ -53,11 +53,11 @@ 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
@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),
@@ -79,7 +79,7 @@ class Errors(ResourceMapping):
message: str
@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 +119,14 @@ 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]
@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 +149,14 @@ 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]
@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 +168,13 @@ 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]
@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),
@@ -205,7 +205,7 @@ class Warnings(ResourceMapping):
warning_code: str
@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 +214,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
@@ -230,7 +230,7 @@ def from_dict(cls, d: Dict[str, Any]):
workspace_id: str
@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..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 `_.
@@ -88,15 +104,15 @@ 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]]
@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),
@@ -123,7 +139,7 @@ class Errors(ResourceMapping):
message: str
@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 +167,17 @@ 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]]
@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),
@@ -182,57 +198,71 @@ 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: 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),
+ new_code=d.get("new_code", None),
+ original_code=d.get("original_code", None),
)
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]
+ akiles_metadata: Optional[AkilesMetadata]
+ 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
@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),
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/acs_encoder.py b/seam/resources/acs_encoder.py
index 88bc5a75..c1e4f936 100644
--- a/seam/resources/acs_encoder.py
+++ b/seam/resources/acs_encoder.py
@@ -52,7 +52,7 @@ class Errors(ResourceMapping):
message: str
@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),
@@ -68,7 +68,7 @@ def from_dict(cls, d: Dict[str, Any]):
workspace_id: str
@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..491e6677 100644
--- a/seam/resources/acs_entrance.py
+++ b/seam/resources/acs_entrance.py
@@ -81,23 +81,23 @@ class Actions(ResourceMapping):
:ivar name: Name of the gadget action."""
- id: str
- name: str
+ id: Optional[str]
+ name: Optional[str]
@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]
@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 +120,14 @@ 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]
@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 +154,16 @@ 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]
@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 +184,12 @@ 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]
@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 +203,10 @@ 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]
@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 +218,10 @@ 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]
@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),
)
@@ -242,7 +242,7 @@ class Errors(ResourceMapping):
message: str
@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 +259,12 @@ 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]
@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 +283,13 @@ 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]
@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 +317,17 @@ 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]
@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 +355,15 @@ 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]
@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 +392,11 @@ 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]
@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 +406,12 @@ 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]]
@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),
@@ -434,7 +434,7 @@ class Warnings(ResourceMapping):
warning_code: str
@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 +443,32 @@ 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]
@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..976a5070 100644
--- a/seam/resources/acs_system.py
+++ b/seam/resources/acs_system.py
@@ -69,10 +69,10 @@ class Errors(ResourceMapping):
created_at: str
error_code: str
message: str
- is_bridge_error: bool
+ is_bridge_error: Optional[bool]
@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 +87,10 @@ class Location(ResourceMapping):
:ivar time_zone: Time zone in which the `access control system `_ is located.
"""
- time_zone: str
+ time_zone: Optional[str]
@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 +106,12 @@ 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]
@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 +133,10 @@ class Warnings(ResourceMapping):
created_at: str
message: str
warning_code: str
- misconfigured_acs_entrance_ids: List[str]
+ misconfigured_acs_entrance_ids: Optional[List[str]]
@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 +146,29 @@ 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
@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..3c794ab2 100644
--- a/seam/resources/acs_user.py
+++ b/seam/resources/acs_user.py
@@ -72,11 +72,11 @@ 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
@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),
@@ -98,7 +98,7 @@ class Errors(ResourceMapping):
message: str
@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 +146,17 @@ 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]
@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 +188,17 @@ 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]
@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 +213,14 @@ 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]
@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 +243,10 @@ 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]
@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 +259,11 @@ 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]
@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),
@@ -284,41 +284,41 @@ class Warnings(ResourceMapping):
warning_code: str
@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
@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..0c0ff07e 100644
--- a/seam/resources/action_attempt.py
+++ b/seam/resources/action_attempt.py
@@ -30,7 +30,7 @@ class Error(ResourceMapping):
type: str
@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),
@@ -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):
@@ -169,21 +174,21 @@ 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]
@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 +204,15 @@ 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]
@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),
@@ -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 `_.
@@ -299,15 +320,15 @@ 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]]
@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),
@@ -334,7 +355,7 @@ class Errors(ResourceMapping):
message: str
@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 +383,17 @@ 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]]
@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),
@@ -395,57 +416,71 @@ 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: 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),
+ new_code=d.get("new_code", None),
+ original_code=d.get("original_code", None),
)
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]
+ akiles_metadata: Optional[AkilesMetadata]
+ 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
@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),
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,25 +538,47 @@ 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.
"""
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]
+ new_code: Optional[str]
+ original_code: Optional[str]
+ original_access_method_id: Optional[str]
@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),
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 `_.
@@ -539,15 +596,15 @@ 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]]
@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),
@@ -575,7 +632,7 @@ class Errors(ResourceMapping):
message: str
@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 +660,17 @@ 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]]
@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 +704,11 @@ 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]
@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 +722,24 @@ 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]
@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]
@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 +756,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]
+ akiles_metadata: Optional[AkilesMetadata]
+ 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]]
+ access_code: Optional[Dict[str, Any]]
+ noise_threshold: Optional[Dict[str, Any]]
@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=(
@@ -763,6 +823,11 @@ def from_dict(cls, d: Dict[str, 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,16 +879,18 @@ def from_dict(cls, d: Dict[str, 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
action_type: str
- error: Error
- result: Result
+ error: Optional[Error]
+ result: Optional[Result]
status: str
@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..02ae27b1 100644
--- a/seam/resources/batch.py
+++ b/seam/resources/batch.py
@@ -134,33 +134,33 @@ 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]]]
@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..c4ec26b1 100644
--- a/seam/resources/client_session.py
+++ b/seam/resources/client_session.py
@@ -44,17 +44,17 @@ 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
@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..582894a2 100644
--- a/seam/resources/connect_webview.py
+++ b/seam/resources/connect_webview.py
@@ -59,25 +59,25 @@ 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
@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..c27e4e7f 100644
--- a/seam/resources/connected_account.py
+++ b/seam/resources/connected_account.py
@@ -81,13 +81,13 @@ 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]
@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 +99,23 @@ def from_dict(cls, d: Dict[str, Any]):
),
)
- sites: List[Sites]
+ sites: Optional[List[Sites]]
@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]
@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 +144,14 @@ 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]
@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 +193,13 @@ 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]
@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 +211,10 @@ def from_dict(cls, d: Dict[str, Any]):
),
)
- sites: List[Sites]
+ sites: Optional[List[Sites]]
@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 +222,10 @@ 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]
@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 +238,26 @@ 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]
@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..d49625df 100644
--- a/seam/resources/customer_portal.py
+++ b/seam/resources/customer_portal.py
@@ -29,7 +29,7 @@ class CustomerPortal:
workspace_id: str
@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..827983a7 100644
--- a/seam/resources/device.py
+++ b/seam/resources/device.py
@@ -95,11 +95,11 @@ class DeviceManufacturer(ResourceMapping):
"""
display_name: str
- image_url: str
+ image_url: Optional[str]
manufacturer: str
@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 +121,11 @@ class DeviceProvider(ResourceMapping):
device_provider_name: str
display_name: str
- image_url: str
+ image_url: Optional[str]
provider_category: str
@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 +152,13 @@ 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]
@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),
@@ -174,19 +174,23 @@ 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: str
- time_zone: str
- timezone: str
+ location_name: Optional[str]
+ room_name: Optional[str]
+ time_zone: Optional[str]
+ timezone: Optional[str]
@classmethod
- def from_dict(cls, d: Dict[str, Any]):
+ 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.
@@ -422,16 +428,16 @@ class Battery(ResourceMapping):
level: float
@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
@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"))
@@ -451,7 +457,7 @@ class Appearance(ResourceMapping):
name: str
@classmethod
- def from_dict(cls, d: Dict[str, Any]):
+ def from_dict(cls, d: Any):
return cls(
name=d.get("name", None),
)
@@ -469,7 +475,7 @@ class Battery(ResourceMapping):
status: str
@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 +500,16 @@ 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]
@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 +545,21 @@ 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]
@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]
@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 +574,10 @@ 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]
@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 +594,13 @@ 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]
@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 +628,17 @@ 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]
@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 +656,10 @@ class AssaAbloyVostioMetadata(ResourceMapping):
:ivar encoder_name: Encoder name for an ASSA ABLOY Vostio system."""
- encoder_name: str
+ encoder_name: Optional[str]
@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 +682,16 @@ 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]
@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 +720,16 @@ 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]
@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 +748,11 @@ 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]
@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 +768,12 @@ 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]
@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),
@@ -794,16 +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: Dict[str, Any]):
- # This shape documents no properties, so there is nothing to read.
- # pylint: disable=unused-argument
- return cls()
-
@dataclass
class PredefinedTimeSlots(ResourceMapping):
"""Predefined time slots for a dormakaba Oracode device.
@@ -828,19 +824,19 @@ 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]
@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,23 +854,19 @@ 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[str]
+ 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]
@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"))
- 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),
@@ -895,11 +887,11 @@ 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]
@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 +908,12 @@ 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]
@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 +928,11 @@ 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]
@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 +947,11 @@ 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]
@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 +969,12 @@ 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]
@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 +997,15 @@ 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]
@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 +1063,30 @@ 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]
@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 +1123,13 @@ 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]
@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 +1156,16 @@ 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]
@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 +1186,12 @@ 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]
@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 +1208,12 @@ 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]
@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 +1253,11 @@ 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]
@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 +1271,11 @@ class Humidity(ResourceMapping):
:ivar value: Value of latest humidity reading for a Minut device."""
- time: str
- value: float
+ time: Optional[str]
+ value: Optional[float]
@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 +1289,11 @@ class Pressure(ResourceMapping):
:ivar value: Value of latest pressure reading for a Minut device."""
- time: str
- value: float
+ time: Optional[str]
+ value: Optional[float]
@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 +1307,11 @@ class Sound(ResourceMapping):
:ivar value: Value of latest sound reading for a Minut device."""
- time: str
- value: float
+ time: Optional[str]
+ value: Optional[float]
@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 +1326,24 @@ class Temperature(ResourceMapping):
:ivar value: Value of latest temperature reading for a Minut device.
"""
- time: str
- value: float
+ time: Optional[str]
+ value: Optional[float]
@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]
@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 +1372,12 @@ 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]
@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),
@@ -1406,20 +1398,29 @@ 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.
- device_custom_name: str
- device_name: str
- display_name: str
- nest_device_id: str
+ :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: 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),
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
@@ -1437,14 +1438,14 @@ 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]
@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 +1469,14 @@ 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]
@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 +1504,16 @@ 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]
@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 +1532,11 @@ 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]
@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 +1565,18 @@ 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]
@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 +1612,17 @@ 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]
@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 +1644,12 @@ 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]
@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 +1666,12 @@ 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]
@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),
@@ -1687,21 +1688,27 @@ 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: 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]
+ enforced_setpoint_range_celsius: Optional[List[float]]
+ product_type: Optional[str]
@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),
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),
)
@@ -1717,13 +1724,13 @@ 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]
@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 +1746,11 @@ 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]
@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 +1774,16 @@ 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]
@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 +1830,16 @@ 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]
@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 +1861,26 @@ 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]
@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]]
@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 +1906,11 @@ 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]
@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 +1928,13 @@ 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]
@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 +1948,10 @@ class VisionlineMetadata(ResourceMapping):
:ivar encoder_id: Encoder ID for an ASSA ABLOY Visionline system."""
- encoder_id: str
+ encoder_id: Optional[str]
@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 +1976,17 @@ 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]
@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),
@@ -1991,6 +1998,32 @@ def from_dict(cls, d: Dict[str, 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.
@@ -2002,11 +2035,11 @@ 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]
@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),
@@ -2022,7 +2055,7 @@ class KeypadBattery(ResourceMapping):
level: float
@classmethod
- def from_dict(cls, d: Dict[str, Any]):
+ def from_dict(cls, d: Any):
return cls(
level=d.get("level", None),
)
@@ -2064,7 +2097,7 @@ class TimePairs(ResourceMapping):
start_time: str
@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 +2105,16 @@ 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]
@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),
@@ -2134,7 +2167,7 @@ class TimePairs(ResourceMapping):
start_time: str
@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 +2175,16 @@ 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]
@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),
@@ -2210,7 +2243,7 @@ class Errors(ResourceMapping):
message: str
@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 +2255,15 @@ 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
@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 +2326,12 @@ 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]
@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 +2342,20 @@ 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]
@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 +2430,36 @@ 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]
@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]
@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 +2534,36 @@ 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]
@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]
@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 +2605,13 @@ 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]
@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),
@@ -2616,7 +2649,7 @@ class Periods(ResourceMapping):
starts_at_time: str
@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 +2657,13 @@ 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
@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 +2697,16 @@ 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]
@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 +2718,118 @@ 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]
+ yacan_metadata: Optional[YacanMetadata]
+ 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]
@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"))
@@ -3043,6 +3081,11 @@ def from_dict(cls, d: Dict[str, 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(
@@ -3194,11 +3237,11 @@ 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]
@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 +3252,46 @@ 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
@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..f58fb9d9 100644
--- a/seam/resources/device_provider.py
+++ b/seam/resources/device_provider.py
@@ -57,33 +57,33 @@ 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]
@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..775e8a11 100644
--- a/seam/resources/instant_key.py
+++ b/seam/resources/instant_key.py
@@ -38,12 +38,12 @@ 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]
@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,8 +52,8 @@ 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
@@ -61,7 +61,7 @@ def from_dict(cls, d: Dict[str, Any]):
workspace_id: str
@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..9918d3c3 100644
--- a/seam/resources/noise_threshold.py
+++ b/seam/resources/noise_threshold.py
@@ -28,11 +28,11 @@ 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
@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..b1faa8c7 100644
--- a/seam/resources/pagination.py
+++ b/seam/resources/pagination.py
@@ -15,11 +15,11 @@ 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]
@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..74b1963b 100644
--- a/seam/resources/phone.py
+++ b/seam/resources/phone.py
@@ -43,7 +43,7 @@ class Errors(ResourceMapping):
message: str
@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 +76,21 @@ 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]
@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]
@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 +105,23 @@ 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]
@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
+ ]
@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(
@@ -150,7 +154,7 @@ class Warnings(ResourceMapping):
warning_code: str
@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 +167,13 @@ 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
@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..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.
@@ -200,12 +196,12 @@ 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]
@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 +220,13 @@ 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]
@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 +246,13 @@ 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]
@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 +271,12 @@ 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]]
@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),
@@ -303,7 +299,7 @@ class AccessCodeErrors(ResourceMapping):
message: str
@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),
@@ -326,7 +322,7 @@ class AccessCodeWarnings(ResourceMapping):
warning_code: str
@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),
@@ -349,7 +345,7 @@ class ConnectedAccountErrors(ResourceMapping):
message: str
@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),
@@ -372,7 +368,7 @@ class ConnectedAccountWarnings(ResourceMapping):
warning_code: str
@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),
@@ -395,7 +391,7 @@ class DeviceErrors(ResourceMapping):
message: str
@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),
@@ -418,7 +414,7 @@ class DeviceWarnings(ResourceMapping):
warning_code: str
@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),
@@ -441,7 +437,7 @@ class AcsSystemErrors(ResourceMapping):
message: str
@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),
@@ -464,7 +460,7 @@ class AcsSystemWarnings(ResourceMapping):
warning_code: str
@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),
@@ -484,106 +480,104 @@ class Reason(ResourceMapping):
reason_code: str
@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]
+ 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]
+ 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]
@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(
@@ -663,7 +657,6 @@ def from_dict(cls, d: Dict[str, 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: Dict[str, 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/space.py b/seam/resources/space.py
index 2712f62b..65e68978 100644
--- a/seam/resources/space.py
+++ b/seam/resources/space.py
@@ -42,13 +42,13 @@ 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]
@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),
@@ -68,7 +68,7 @@ class Geolocation(ResourceMapping):
longitude: float
@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 +76,18 @@ 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
@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..0c1c262d 100644
--- a/seam/resources/thermostat_daily_program.py
+++ b/seam/resources/thermostat_daily_program.py
@@ -34,7 +34,7 @@ class Periods(ResourceMapping):
starts_at_time: str
@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 +42,13 @@ 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
@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..2c022801 100644
--- a/seam/resources/thermostat_schedule.py
+++ b/seam/resources/thermostat_schedule.py
@@ -46,7 +46,7 @@ class Errors(ResourceMapping):
message: str
@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 +58,15 @@ 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
@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..8c1d00ed 100644
--- a/seam/resources/unmanaged_access_code.py
+++ b/seam/resources/unmanaged_access_code.py
@@ -72,17 +72,17 @@ 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]
@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 +132,31 @@ class ModifiedFields(ResourceMapping):
:ivar to: The new value of the field."""
field: str
- from_: str
- to: str
+ from_: Optional[str]
+ to: Optional[str]
@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]
@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 +200,25 @@ class ModifiedFields(ResourceMapping):
:ivar to: The new value of the field."""
field: str
- from_: str
- to: str
+ from_: Optional[str]
+ to: Optional[str]
@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]]
@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 +231,24 @@ 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
@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..71e7a68e 100644
--- a/seam/resources/unmanaged_access_grant.py
+++ b/seam/resources/unmanaged_access_grant.py
@@ -56,10 +56,10 @@ class Errors(ResourceMapping):
created_at: str
error_code: str
message: str
- missing_device_ids: List[str]
+ missing_device_ids: Optional[List[str]]
@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 +93,12 @@ 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]
@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 +117,13 @@ 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]
@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 +132,14 @@ 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]]
@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 +170,15 @@ 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
@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),
@@ -226,7 +226,7 @@ class FailedDevices(ResourceMapping):
message: str
@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 +236,15 @@ 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]
@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 +264,21 @@ 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
@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..c30e9a4e 100644
--- a/seam/resources/unmanaged_access_method.py
+++ b/seam/resources/unmanaged_access_method.py
@@ -54,7 +54,7 @@ class Errors(ResourceMapping):
message: str
@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 +85,12 @@ 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]
@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 +107,12 @@ 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]
@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 +120,13 @@ 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]
@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 +155,10 @@ class Warnings(ResourceMapping):
created_at: str
message: str
warning_code: str
- original_access_method_id: str
+ original_access_method_id: Optional[str]
@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 +167,23 @@ 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
@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..15ba2dcc 100644
--- a/seam/resources/unmanaged_device.py
+++ b/seam/resources/unmanaged_device.py
@@ -92,13 +92,13 @@ 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]
@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),
@@ -114,19 +114,23 @@ 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: str
- time_zone: str
- timezone: str
+ location_name: Optional[str]
+ room_name: Optional[str]
+ time_zone: Optional[str]
+ timezone: Optional[str]
@classmethod
- def from_dict(cls, d: Dict[str, Any]):
+ 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),
)
@@ -176,16 +180,16 @@ class Battery(ResourceMapping):
level: float
@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
@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"))
@@ -208,7 +212,7 @@ class Battery(ResourceMapping):
status: str
@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 +237,16 @@ 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]
@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 +265,20 @@ 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]
@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 +325,11 @@ 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]
@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 +340,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 +368,13 @@ 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
@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..6905f5af 100644
--- a/seam/resources/unmanaged_user_identity.py
+++ b/seam/resources/unmanaged_user_identity.py
@@ -50,7 +50,7 @@ class Errors(ResourceMapping):
message: str
@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),
@@ -75,7 +75,7 @@ class Warnings(ResourceMapping):
warning_code: str
@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 +85,16 @@ 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
@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..141a82a3 100644
--- a/seam/resources/user_identity.py
+++ b/seam/resources/user_identity.py
@@ -52,7 +52,7 @@ class Errors(ResourceMapping):
message: str
@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),
@@ -77,7 +77,7 @@ class Warnings(ResourceMapping):
warning_code: str
@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 +87,17 @@ 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
@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..137e031b 100644
--- a/seam/resources/webhook.py
+++ b/seam/resources/webhook.py
@@ -16,13 +16,13 @@ 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
@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..bbc0e01e 100644
--- a/seam/resources/workspace.py
+++ b/seam/resources/workspace.py
@@ -43,14 +43,14 @@ 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]
@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 +60,18 @@ 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
@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/route.py b/seam/route.py
new file mode 100644
index 00000000..59f71fb8
--- /dev/null
+++ b/seam/route.py
@@ -0,0 +1,17 @@
+from typing import Any, Callable, TypeVar, cast
+
+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:
+ # 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 a194118e..3f5053f5 100644
--- a/seam/routes/access_codes.py
+++ b/seam/routes/access_codes.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
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
@@ -37,7 +39,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.
@@ -79,7 +81,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
@@ -97,7 +101,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.
@@ -141,7 +145,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 +157,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 +167,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
@@ -169,7 +178,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 `_.
@@ -181,7 +190,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
@@ -195,9 +206,9 @@ 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
+ user_identifier_key: Optional[str] = None,
) -> List[AccessCode]:
"""Returns a list of all `access codes `_.
@@ -223,7 +234,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 +253,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
@@ -250,7 +265,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.
@@ -263,7 +278,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
@@ -278,16 +294,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 +318,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,18 +326,11 @@ 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.
- """
+ :raises ValueError: At least one parameter must be provided."""
raise NotImplementedError()
@abc.abstractmethod
@@ -344,7 +340,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.
@@ -365,7 +361,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()
@@ -384,6 +381,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,
*,
@@ -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.
@@ -444,8 +444,10 @@ def create(
:param use_offline_access_code: Deprecated: Use ``is_offline_access_code`` instead.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if device_id is not None:
json_payload["device_id"] = device_id
@@ -482,10 +484,20 @@ 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"])
+ @route_metadata(
+ path="/access_codes/create_multiple",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def create_multiple(
self,
*,
@@ -500,7 +512,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.
@@ -544,8 +556,10 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if device_ids is not None:
json_payload["device_ids"] = device_ids
@@ -576,49 +590,78 @@ 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)
+ 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"]]
+ @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 `_.
: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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError(
+ "At least one parameter is required for /access_codes/delete"
+ )
- self.client.post("/access_codes/delete", json=json_payload)
+ self.client.delete("/access_codes/delete", params=params)
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.
:param device_id: ID of the device for which you want to generate a code.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/access_codes/get", has_required_parameters=True, has_pagination=False
+ )
def get(
self,
*,
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 `_.
@@ -630,20 +673,28 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError("At least one parameter is required for /access_codes/get")
- 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"])
+ @route_metadata(
+ path="/access_codes/list", has_required_parameters=True, has_pagination=True
+ )
def list(
self,
*,
@@ -654,9 +705,9 @@ 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
+ user_identifier_key: Optional[str] = None,
) -> List[AccessCode]:
"""Returns a list of all `access codes `_.
@@ -682,8 +733,10 @@ def list(
:param user_identifier_key: Your user ID for the user by which to filter access codes.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_code_ids is not None:
json_payload["access_code_ids"] = access_code_ids
@@ -706,10 +759,20 @@ 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"]]
+ @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 `_.
@@ -723,25 +786,37 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
)
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,
*,
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.
@@ -754,8 +829,9 @@ 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``.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if device_id is not None:
json_payload["device_id"] = device_id
@@ -766,10 +842,18 @@ 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
+ @route_metadata(
+ path="/access_codes/update", has_required_parameters=True, has_pagination=False
+ )
def update(
self,
*,
@@ -781,16 +865,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 +889,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,19 +897,12 @@ 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 = {}
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_code_id is not None:
json_payload["access_code_id"] = access_code_id
@@ -858,38 +922,34 @@ 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)
+ 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
+ @route_metadata(
+ path="/access_codes/update_multiple",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def update_multiple(
self,
*,
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.
@@ -910,8 +970,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if common_code_key is not None:
json_payload["common_code_key"] = common_code_key
@@ -922,6 +983,11 @@ 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)
+ 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 048c13fb..4c4c756c 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
@@ -18,7 +19,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()
@@ -27,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:
@@ -38,8 +46,10 @@ def create_unmanaged_access_code(
:param name: Name of the simulated unmanaged access code.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if code is not None:
json_payload["code"] = code
@@ -48,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 ac90e4ee..8a6e67d4 100644
--- a/seam/routes/access_codes_unmanaged.py
+++ b/seam/routes/access_codes_unmanaged.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
import abc
from ..client import SeamHttpClient
+from ..route import route_metadata
+from ..null import Null
from ..resources import UnmanagedAccessCode
@@ -13,7 +15,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 `_.
@@ -28,7 +30,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 +39,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
@@ -45,7 +49,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 `_.
@@ -57,7 +61,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
@@ -66,9 +72,9 @@ 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
+ user_identifier_key: Optional[str] = None,
) -> List[UnmanagedAccessCode]:
"""Returns a list of all `unmanaged access codes `_.
@@ -82,7 +88,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
@@ -93,7 +101,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 `_.
@@ -106,7 +114,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()
@@ -115,13 +124,18 @@ 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,
*,
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 `_.
@@ -136,8 +150,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_code_id is not None:
json_payload["access_code_id"] = access_code_id
@@ -150,32 +165,53 @@ def convert_to_managed(
is_external_modification_allowed
)
- self.client.post(
+ 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
)
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 `_.
:param access_code_id: ID of the unmanaged access code that you want to delete.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 not params:
+ raise ValueError(
+ "At least one parameter is required for /access_codes/unmanaged/delete"
+ )
- self.client.post("/access_codes/unmanaged/delete", json=json_payload)
+ self.client.delete("/access_codes/unmanaged/delete", params=params)
return None
+ @route_metadata(
+ path="/access_codes/unmanaged/get",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def get(
self,
*,
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 `_.
@@ -187,28 +223,40 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/access_codes/unmanaged/list",
+ has_required_parameters=True,
+ has_pagination=True,
+ )
def list(
self,
*,
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
+ user_identifier_key: Optional[str] = None,
) -> List[UnmanagedAccessCode]:
"""Returns a list of all `unmanaged access codes `_.
@@ -222,24 +270,36 @@ def list(
:param user_identifier_key: Your user ID for the user by which to filter unmanaged access codes.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"]]
+ @route_metadata(
+ path="/access_codes/unmanaged/update",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def update(
self,
*,
@@ -247,7 +307,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 `_.
@@ -260,8 +320,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_code_id is not None:
json_payload["access_code_id"] = access_code_id
@@ -276,6 +337,11 @@ def update(
is_external_modification_allowed
)
- self.client.post("/access_codes/unmanaged/update", json=json_payload)
+ 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 f6835ebd..fbc7685a 100644
--- a/seam/routes/access_grants.py
+++ b/seam/routes/access_grants.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
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,
@@ -26,14 +28,14 @@ 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,
- 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.
@@ -67,14 +69,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
@@ -82,7 +88,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.
@@ -90,7 +96,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
@@ -100,7 +108,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.
@@ -112,7 +120,9 @@ def get_related(
:param include:
- :returns: OK"""
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
raise NotImplementedError()
@abc.abstractmethod
@@ -121,17 +131,17 @@ 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
+ user_identity_id: Optional[str] = None,
) -> List[AccessGrant]:
"""Gets an Access Grant.
@@ -174,7 +184,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
@@ -183,9 +195,9 @@ def update(
*,
access_grant_id: Optional[str] = None,
access_grant_key: Optional[str] = None,
- ends_at: Optional[str] = None,
- name: Optional[str] = None,
- starts_at: 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.
@@ -198,7 +210,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()
@@ -212,6 +225,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,
*,
@@ -222,14 +238,14 @@ 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,
- 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.
@@ -263,8 +279,10 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if requested_access_methods is not None:
json_payload["requested_access_methods"] = requested_access_methods
@@ -297,28 +315,46 @@ 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"])
+ @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.
- :param access_grant_id: ID of Access Grant to delete."""
- json_payload = {}
+ :param access_grant_id: ID of Access Grant to delete.
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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
+ @route_metadata(
+ path="/access_grants/get", has_required_parameters=True, has_pagination=False
+ )
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.
@@ -326,25 +362,37 @@ def get(
:param access_grant_key: Unique key of Access Grant to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError(
+ "At least one parameter is required for /access_grants/get"
+ )
- 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"])
+ @route_metadata(
+ path="/access_grants/get_related",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def get_related(
self,
*,
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.
@@ -356,8 +404,10 @@ def get_related(
:param include:
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_grant_ids is not None:
json_payload["access_grant_ids"] = access_grant_ids
@@ -368,26 +418,34 @@ 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"])
+ @route_metadata(
+ path="/access_grants/list", has_required_parameters=False, has_pagination=True
+ )
def list(
self,
*,
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
+ user_identity_id: Optional[str] = None,
) -> List[AccessGrant]:
"""Gets an Access Grant.
@@ -418,7 +476,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
@@ -451,6 +509,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:
@@ -460,28 +523,38 @@ def request_access_methods(
:param requested_access_methods: Array of requested access methods to add to the access grant.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_grant_id is not None:
json_payload["access_grant_id"] = access_grant_id
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
)
return AccessGrant.from_dict(res["access_grant"])
+ @route_metadata(
+ path="/access_grants/update", has_required_parameters=True, has_pagination=False
+ )
def update(
self,
*,
access_grant_id: Optional[str] = None,
access_grant_key: Optional[str] = None,
- ends_at: Optional[str] = None,
- name: Optional[str] = None,
- starts_at: 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.
@@ -494,8 +567,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_grant_id is not None:
json_payload["access_grant_id"] = access_grant_id
@@ -508,6 +582,11 @@ def update(
if starts_at is not None:
json_payload["starts_at"] = starts_at
- self.client.post("/access_grants/update", json=json_payload)
+ 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 43786955..843709e8 100644
--- a/seam/routes/access_grants_unmanaged.py
+++ b/seam/routes/access_grants_unmanaged.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
import abc
from ..client import SeamHttpClient
+from ..route import route_metadata
+from ..null import Null
from ..resources import UnmanagedAccessGrant
@@ -12,7 +14,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
@@ -22,9 +26,9 @@ 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
+ user_identity_id: Optional[str] = None,
) -> List[UnmanagedAccessGrant]:
"""Gets unmanaged Access Grants (where is_managed = false).
@@ -49,7 +53,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.
@@ -62,7 +66,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()
@@ -71,30 +76,47 @@ 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).
:param access_grant_id: ID of unmanaged Access Grant to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 not params:
+ raise ValueError(
+ "At least one parameter is required for /access_grants/unmanaged/get"
+ )
- 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"])
+ @route_metadata(
+ path="/access_grants/unmanaged/list",
+ has_required_parameters=False,
+ has_pagination=True,
+ )
def list(
self,
*,
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
+ user_identity_id: Optional[str] = None,
) -> List[UnmanagedAccessGrant]:
"""Gets unmanaged Access Grants (where is_managed = false).
@@ -111,31 +133,36 @@ 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 = {}
+ 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"]]
+ @route_metadata(
+ path="/access_grants/unmanaged/update",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def update(
self,
*,
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.
@@ -148,8 +175,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_grant_id is not None:
json_payload["access_grant_id"] = access_grant_id
@@ -158,6 +186,11 @@ 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)
+ 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 ffafd2d5..915c4773 100644
--- a/seam/routes/access_methods.py
+++ b/seam/routes/access_methods.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
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,
@@ -22,7 +24,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.
@@ -32,7 +34,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
@@ -41,7 +45,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.
@@ -50,7 +54,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
@@ -59,7 +64,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 `_.
@@ -69,7 +74,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 +85,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
@@ -87,7 +96,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.
@@ -97,7 +106,9 @@ def get_related(
:param include:
- :returns: OK"""
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
raise NotImplementedError()
@abc.abstractmethod
@@ -110,8 +121,8 @@ def list(
acs_entrance_id: Optional[str] = None,
device_id: Optional[str] = None,
limit: Optional[int] = None,
- page_cursor: Optional[str] = None,
- space_id: 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.
@@ -131,7 +142,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
@@ -140,7 +153,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.
@@ -150,7 +163,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()
@@ -164,12 +179,17 @@ 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,
*,
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.
@@ -179,14 +199,21 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_method_id is not None:
json_payload["access_method_id"] = access_method_id
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 = (
@@ -201,12 +228,17 @@ 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,
*,
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.
@@ -215,26 +247,37 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError(
+ "At least one parameter is required for /access_methods/delete"
+ )
- self.client.post("/access_methods/delete", json=json_payload)
+ self.client.delete("/access_methods/delete", params=params)
return None
+ @route_metadata(
+ path="/access_methods/encode",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def encode(
self,
*,
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 `_.
@@ -244,14 +287,21 @@ def encode(
:param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_method_id is not None:
json_payload["access_method_id"] = access_method_id
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 = (
@@ -266,27 +316,42 @@ 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.
:param access_method_id: ID of access method to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 not params:
+ raise ValueError(
+ "At least one parameter is required for /access_methods/get"
+ )
- 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"])
+ @route_metadata(
+ path="/access_methods/get_related",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def get_related(
self,
*,
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.
@@ -296,8 +361,10 @@ def get_related(
:param include:
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_method_ids is not None:
json_payload["access_method_ids"] = access_method_ids
@@ -306,10 +373,18 @@ 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"])
+ @route_metadata(
+ path="/access_methods/list", has_required_parameters=True, has_pagination=True
+ )
def list(
self,
*,
@@ -319,8 +394,8 @@ def list(
acs_entrance_id: Optional[str] = None,
device_id: Optional[str] = None,
limit: Optional[int] = None,
- page_cursor: Optional[str] = None,
- space_id: 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.
@@ -340,36 +415,48 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"]]
+ @route_metadata(
+ path="/access_methods/unlock_door",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def unlock_door(
self,
*,
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.
@@ -379,14 +466,21 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_method_id is not None:
json_payload["access_method_id"] = access_method_id
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 44ad9c5f..fd7cd14a 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
@@ -12,7 +13,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
@@ -22,7 +25,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.
@@ -34,7 +37,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()
@@ -43,28 +48,45 @@ 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).
:param access_method_id: ID of unmanaged access method to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/access_methods/unmanaged/list",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def list(
self,
*,
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.
@@ -76,18 +98,25 @@ def list(
:param space_id: ID of the space for which you want to retrieve all unmanaged access methods.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError(
+ "At least one parameter is required for /access_methods/unmanaged/list"
+ )
- 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.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 11ce5959..6b84487e 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
@@ -12,7 +13,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 `_.
@@ -21,14 +22,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 +41,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
@@ -47,7 +53,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 `_.
@@ -70,7 +76,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 +87,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
@@ -88,7 +98,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 `_.
@@ -97,7 +107,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()
@@ -106,12 +117,17 @@ 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,
*,
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 `_.
@@ -120,8 +136,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_access_group_id is not None:
json_payload["acs_access_group_id"] = acs_access_group_id
@@ -130,45 +147,79 @@ 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)
+ 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
+ @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 `_.
- :param acs_access_group_id: ID of the access group that you want to delete."""
- json_payload = {}
+ :param acs_access_group_id: ID of the access group that you want to delete.
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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
+ @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 `_.
:param acs_access_group_id: ID of the access group that you want to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/acs/access_groups/list",
+ has_required_parameters=False,
+ has_pagination=False,
+ )
def list(
self,
*,
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 `_.
@@ -181,21 +232,26 @@ def list(
:param user_identity_id: ID of the user identity for which you want to retrieve all access groups.
:returns: OK"""
- json_payload = {}
+ 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"]]
+ @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]:
@@ -203,39 +259,63 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+ 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
)
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 `_.
:param acs_access_group_id: ID of the access group for which you want to retrieve all access system users.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"]]
+ @route_metadata(
+ path="/acs/access_groups/remove_user",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def remove_user(
self,
*,
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 `_.
@@ -244,16 +324,22 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError(
+ "At least one parameter is required for /acs/access_groups/remove_user"
+ )
- 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 c655973e..ceff9409 100644
--- a/seam/routes/acs_credentials.py
+++ b/seam/routes/acs_credentials.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
import abc
from ..client import SeamHttpClient
+from ..route import route_metadata
+from ..null import Null
from ..resources import AcsCredential, AcsEntrance
@@ -12,7 +14,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 `_.
@@ -21,7 +23,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
@@ -40,7 +43,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.
@@ -70,14 +73,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 +93,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
@@ -99,8 +108,8 @@ def list(
created_before: Optional[str] = None,
is_multi_phone_sync_credential: Optional[bool] = None,
limit: Optional[float] = None,
- page_cursor: Optional[str] = None,
- search: Optional[str] = None
+ page_cursor: Optional[Union[str, Null]] = None,
+ search: Optional[str] = None,
) -> List[AcsCredential]:
"""Returns a list of all `credentials `_.
@@ -129,7 +138,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
@@ -138,7 +149,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 `_.
@@ -147,7 +158,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
@@ -156,7 +168,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 `_.
@@ -165,7 +177,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()
@@ -174,12 +187,17 @@ 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,
*,
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 `_.
@@ -188,8 +206,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_credential_id is not None:
json_payload["acs_credential_id"] = acs_credential_id
@@ -198,10 +217,20 @@ 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)
+ 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
+ @route_metadata(
+ path="/acs/credentials/create",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def create(
self,
*,
@@ -217,7 +246,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.
@@ -247,8 +276,10 @@ def create(
:param visionline_metadata: Visionline-specific metadata for the new credential.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_method is not None:
json_payload["access_method"] = access_method
@@ -281,38 +312,68 @@ 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"])
+ @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 `_.
- :param acs_credential_id: ID of the credential that you want to delete."""
- json_payload = {}
+ :param acs_credential_id: ID of the credential that you want to delete.
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 not params:
+ raise ValueError(
+ "At least one parameter is required for /acs/credentials/delete"
+ )
- self.client.post("/acs/credentials/delete", json=json_payload)
+ self.client.delete("/acs/credentials/delete", params=params)
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 `_.
:param acs_credential_id: ID of the credential that you want to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/acs/credentials/list", has_required_parameters=False, has_pagination=True
+ )
def list(
self,
*,
@@ -322,8 +383,8 @@ def list(
created_before: Optional[str] = None,
is_multi_phone_sync_credential: Optional[bool] = None,
limit: Optional[float] = None,
- page_cursor: Optional[str] = None,
- search: Optional[str] = None
+ page_cursor: Optional[Union[str, Null]] = None,
+ search: Optional[str] = None,
) -> List[AcsCredential]:
"""Returns a list of all `credentials `_.
@@ -344,54 +405,69 @@ 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 = {}
+ 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"]]
+ @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.
: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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 not params:
+ raise ValueError(
+ "At least one parameter is required for /acs/credentials/list_accessible_entrances"
+ )
- 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"]]
+ @route_metadata(
+ path="/acs/credentials/unassign",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def unassign(
self,
*,
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 `_.
@@ -400,8 +476,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_credential_id is not None:
json_payload["acs_credential_id"] = acs_credential_id
@@ -410,16 +487,26 @@ 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)
+ 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
+ @route_metadata(
+ path="/acs/credentials/update",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def update(
self,
*,
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 `_.
@@ -428,8 +515,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_credential_id is not None:
json_payload["acs_credential_id"] = acs_credential_id
@@ -438,6 +526,11 @@ def update(
if ends_at is not None:
json_payload["ends_at"] = ends_at
- self.client.post("/acs/credentials/update", json=json_payload)
+ 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 e537a0f3..5b6d9c8a 100644
--- a/seam/routes/acs_encoders.py
+++ b/seam/routes/acs_encoders.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
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
@@ -20,7 +22,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``
@@ -32,7 +34,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 +45,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
@@ -52,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 `_.
@@ -75,7 +81,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 `_.
@@ -85,7 +91,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
@@ -96,7 +104,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``.
@@ -110,7 +118,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()
@@ -124,13 +134,18 @@ 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,
*,
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``
@@ -142,8 +157,10 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_encoder_id is not None:
json_payload["acs_encoder_id"] = acs_encoder_id
@@ -152,6 +169,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 = (
@@ -166,21 +188,32 @@ 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 `_.
:param acs_encoder_id: ID of the encoder that you want to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/acs/encoders/list", has_required_parameters=False, has_pagination=True
+ )
def list(
self,
*,
@@ -188,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 `_.
@@ -203,7 +236,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
@@ -220,12 +253,17 @@ 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,
*,
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 `_.
@@ -235,14 +273,21 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_encoder_id is not None:
json_payload["acs_encoder_id"] = acs_encoder_id
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 = (
@@ -257,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,
*,
@@ -264,7 +314,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``.
@@ -278,8 +328,10 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_encoder_id is not None:
json_payload["acs_encoder_id"] = acs_encoder_id
@@ -290,6 +342,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 e08aaf10..ac0d1793 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):
@@ -11,7 +12,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 `_.
@@ -20,7 +21,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 +33,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
@@ -40,7 +44,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 `_.
@@ -48,7 +52,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
@@ -57,7 +63,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 `_.
@@ -65,7 +71,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()
@@ -74,12 +82,17 @@ 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,
*,
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 `_.
@@ -88,8 +101,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_encoder_id is not None:
json_payload["acs_encoder_id"] = acs_encoder_id
@@ -98,12 +112,22 @@ 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
)
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:
@@ -111,14 +135,21 @@ 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 = {}
+ :param scenario: Scenario to simulate.
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_encoder_id is not None:
json_payload["acs_encoder_id"] = acs_encoder_id
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,
@@ -126,12 +157,17 @@ 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,
*,
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 `_.
@@ -139,8 +175,10 @@ def next_credential_scan_will_fail(
:param error_code:
- :param acs_credential_id_on_seam:"""
- json_payload = {}
+ :param acs_credential_id_on_seam:
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_encoder_id is not None:
json_payload["acs_encoder_id"] = acs_encoder_id
@@ -149,18 +187,28 @@ 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
)
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,
*,
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 `_.
@@ -168,8 +216,10 @@ 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 = {}
+ :param scenario: Scenario to simulate.
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_encoder_id is not None:
json_payload["acs_encoder_id"] = acs_encoder_id
@@ -178,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 104779c0..ddb863d4 100644
--- a/seam/routes/acs_entrances.py
+++ b/seam/routes/acs_entrances.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
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
@@ -13,7 +15,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
@@ -22,7 +26,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 `_.
@@ -31,7 +35,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
@@ -45,10 +50,10 @@ 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
+ space_id: Optional[str] = None,
) -> List[AcsEntrance]:
"""Returns a list of all `access system entrances `_.
@@ -87,7 +92,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
@@ -96,7 +103,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.
@@ -106,7 +113,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()
@@ -115,27 +124,42 @@ 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 `_.
:param acs_entrance_id: ID of the entrance that you want to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/acs/entrances/grant_access",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def grant_access(
self,
*,
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 `_.
@@ -144,8 +168,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_entrance_id is not None:
json_payload["acs_entrance_id"] = acs_entrance_id
@@ -154,10 +179,18 @@ 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
+ @route_metadata(
+ path="/acs/entrances/list", has_required_parameters=False, has_pagination=True
+ )
def list(
self,
*,
@@ -168,10 +201,10 @@ 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
+ space_id: Optional[str] = None,
) -> List[AcsEntrance]:
"""Returns a list of all `access system entrances `_.
@@ -198,7 +231,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
@@ -227,6 +260,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]:
@@ -236,26 +274,36 @@ def list_credentials_with_access(
:param include_if: Conditions that credentials must meet to be included in the returned list.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_entrance_id is not None:
json_payload["acs_entrance_id"] = acs_entrance_id
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
)
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,
*,
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.
@@ -265,14 +313,21 @@ def unlock(
:param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_credential_id is not None:
json_payload["acs_credential_id"] = acs_credential_id
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 b4ca7d3d..7eb41612 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
@@ -12,7 +13,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
@@ -21,7 +24,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 `_.
@@ -46,7 +49,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
@@ -55,7 +60,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.
@@ -63,7 +68,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()
@@ -72,27 +79,38 @@ 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 `_.
:param acs_system_id: ID of the access system that you want to get.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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"])
+ @route_metadata(
+ path="/acs/systems/list", has_required_parameters=False, has_pagination=False
+ )
def list(
self,
*,
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 `_.
@@ -105,19 +123,24 @@ 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 = {}
+ 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"]]
+ @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]:
@@ -127,25 +150,36 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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,
+ 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
)
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,
*,
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.
@@ -153,8 +187,10 @@ def report_devices(
:param acs_encoders: Array of ACS encoders to report
- :param acs_entrances: Array of ACS entrances to report"""
- json_payload = {}
+ :param acs_entrances: Array of ACS entrances to report
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_system_id is not None:
json_payload["acs_system_id"] = acs_system_id
@@ -163,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 7f59c38e..1da621eb 100644
--- a/seam/routes/acs_users.py
+++ b/seam/routes/acs_users.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
import abc
from ..client import SeamHttpClient
+from ..route import route_metadata
+from ..null import Null
from ..resources import AcsUser, AcsEntrance
@@ -15,7 +17,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
@@ -29,7 +32,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 `_.
@@ -49,7 +52,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
@@ -58,7 +63,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 `_.
@@ -67,7 +72,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
@@ -76,7 +82,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 `_.
@@ -86,7 +92,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
@@ -96,11 +104,11 @@ 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,
- 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 +137,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.
@@ -139,7 +147,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
@@ -148,7 +158,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 `_.
@@ -157,7 +167,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
@@ -166,7 +177,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 `_.
@@ -175,7 +186,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
@@ -184,7 +196,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.
@@ -193,7 +205,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
@@ -202,7 +215,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.
@@ -211,14 +224,15 @@ 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
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,
@@ -226,7 +240,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 `_.
@@ -247,7 +261,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()
@@ -256,6 +271,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:
@@ -264,18 +284,27 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_access_group_id is not None:
json_payload["acs_access_group_id"] = acs_access_group_id
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)
+ 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
+ @route_metadata(
+ path="/acs/users/create", has_required_parameters=True, has_pagination=False
+ )
def create(
self,
*,
@@ -286,7 +315,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 `_.
@@ -306,8 +335,10 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_system_id is not None:
json_payload["acs_system_id"] = acs_system_id
@@ -326,16 +357,22 @@ 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"])
+ @route_metadata(
+ path="/acs/users/delete", has_required_parameters=True, has_pagination=False
+ )
def delete(
self,
*,
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 `_.
@@ -344,26 +381,33 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError("At least one parameter is required for /acs/users/delete")
- self.client.post("/acs/users/delete", json=json_payload)
+ self.client.delete("/acs/users/delete", params=params)
return None
+ @route_metadata(
+ path="/acs/users/get", has_required_parameters=True, has_pagination=False
+ )
def get(
self,
*,
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 `_.
@@ -373,31 +417,39 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 not params:
+ raise ValueError("At least one parameter is required for /acs/users/get")
- 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"])
+ @route_metadata(
+ path="/acs/users/list", has_required_parameters=False, has_pagination=True
+ )
def list(
self,
*,
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,
- user_identity_phone_number: Optional[str] = None
+ user_identity_phone_number: Optional[str] = None,
) -> List[AcsUser]:
"""Returns a list of all `access system users `_.
@@ -418,35 +470,40 @@ 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 = {}
+ 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"]]
+ @route_metadata(
+ path="/acs/users/list_accessible_entrances",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def list_accessible_entrances(
self,
*,
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.
@@ -456,28 +513,38 @@ 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 = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError(
+ "At least one parameter is required for /acs/users/list_accessible_entrances"
+ )
- 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"]]
+ @route_metadata(
+ path="/acs/users/remove_from_access_group",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def remove_from_access_group(
self,
*,
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 `_.
@@ -486,26 +553,37 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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)
+ 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
+ @route_metadata(
+ path="/acs/users/revoke_access_to_all_entrances",
+ has_required_parameters=True,
+ has_pagination=False,
+ )
def revoke_access_to_all_entrances(
self,
*,
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 `_.
@@ -514,8 +592,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_system_id is not None:
json_payload["acs_system_id"] = acs_system_id
@@ -524,16 +603,24 @@ 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
+ @route_metadata(
+ path="/acs/users/suspend", has_required_parameters=True, has_pagination=False
+ )
def suspend(
self,
*,
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.
@@ -542,8 +629,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_system_id is not None:
json_payload["acs_system_id"] = acs_system_id
@@ -552,16 +640,24 @@ 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
+ @route_metadata(
+ path="/acs/users/unsuspend", has_required_parameters=True, has_pagination=False
+ )
def unsuspend(
self,
*,
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.
@@ -570,8 +666,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if acs_system_id is not None:
json_payload["acs_system_id"] = acs_system_id
@@ -580,14 +677,22 @@ 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
+ @route_metadata(
+ path="/acs/users/update", has_required_parameters=True, has_pagination=False
+ )
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,
@@ -595,7 +700,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 `_.
@@ -616,8 +721,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if access_schedule is not None:
json_payload["access_schedule"] = access_schedule
@@ -638,6 +744,9 @@ 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)
+ 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 e14dfe8e..8f0c221c 100644
--- a/seam/routes/action_attempts.py
+++ b/seam/routes/action_attempts.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
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
@@ -12,7 +14,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 `_.
@@ -20,7 +22,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
@@ -30,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.
@@ -51,11 +55,14 @@ 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,
*,
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 `_.
@@ -63,13 +70,20 @@ def get(
:param wait_for_action_attempt: Whether, and for how long, to wait for the action attempt to finish.
- :returns: OK"""
- json_payload = {}
+ :returns: OK
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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
+
+ if not params:
+ raise ValueError(
+ "At least one parameter is required for /action_attempts/get"
+ )
- 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")
@@ -83,13 +97,16 @@ 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,
*,
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.
@@ -102,7 +119,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 29f996af..62da3156 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
@@ -17,7 +18,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 `_.
@@ -44,7 +45,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
@@ -52,7 +55,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 +75,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 +103,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.
@@ -115,7 +118,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
@@ -126,7 +130,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 `_.
@@ -149,7 +153,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()
@@ -158,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,
*,
@@ -168,7 +179,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 `_.
@@ -189,7 +200,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
@@ -208,28 +219,43 @@ 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"])
+ @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 `_.
- :param client_session_id: ID of the client session that you want to delete."""
- json_payload = {}
+ :param client_session_id: ID of the client session that you want to delete.
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 not params:
+ raise ValueError(
+ "At least one parameter is required for /client_sessions/delete"
+ )
- self.client.post("/client_sessions/delete", json=json_payload)
+ self.client.delete("/client_sessions/delete", params=params)
return None
+ @route_metadata(
+ path="/client_sessions/get", has_required_parameters=False, has_pagination=False
+ )
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 `_.
@@ -238,17 +264,22 @@ def get(
:param user_identifier_key: User identifier key associated with the client session that you want to get.
:returns: OK"""
- json_payload = {}
+ 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"])
+ @route_metadata(
+ path="/client_sessions/get_or_create",
+ has_required_parameters=False,
+ has_pagination=False,
+ )
def get_or_create(
self,
*,
@@ -257,7 +288,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.
@@ -274,7 +305,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
@@ -293,6 +324,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,
*,
@@ -301,7 +337,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.
@@ -316,8 +352,9 @@ 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.
- """
- json_payload = {}
+
+ :raises ValueError: At least one parameter must be provided."""
+ json_payload: Dict[str, Any] = {}
if client_session_id is not None:
json_payload["client_session_id"] = client_session_id
@@ -332,10 +369,20 @@ 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)
+ 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
+ @route_metadata(
+ path="/client_sessions/list",
+ has_required_parameters=False,
+ has_pagination=False,
+ )
def list(
self,
*,
@@ -343,7 +390,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 `_.
@@ -358,34 +405,46 @@ def list(
:param without_user_identifier_key: Indicates whether to retrieve only client sessions without associated user identifier keys.
:returns: OK"""
- json_payload = {}
+ 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"]]
+ @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 `_.
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 = {}
+ :param client_session_id: ID of the client session that you want to revoke.
+
+ :raises ValueError: At least one parameter must be provided."""
+ 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 d6b32d6e..053ed685 100644
--- a/seam/routes/connect_webviews.py
+++ b/seam/routes/connect_webviews.py
@@ -1,6 +1,8 @@
from typing import Optional, Any, List, Dict, Union
import abc
from ..client import SeamHttpClient
+from ..route import route_metadata
+from ..null import Null
from ..resources import ConnectWebview
@@ -19,7 +21,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