diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6acae23e1..593be15ca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,7 @@ jobs: publish: name: Publish to PyPI runs-on: ubuntu-latest + needs: [build] environment: name: publishing url: https://pypi.org/p/singer-sdk diff --git a/.gitignore b/.gitignore index 18f01b438..d6a92f65e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Local Poetry configuration file + +poetry.toml + # CI _changelog_fragment.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9707001c..cf0c9db99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,7 +45,7 @@ repos: - id: check-readthedocs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.11 + rev: v0.1.13 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/cookiecutter/mapper-template/cookiecutter.json b/cookiecutter/mapper-template/cookiecutter.json index c42b1cf06..9d7b6385f 100644 --- a/cookiecutter/mapper-template/cookiecutter.json +++ b/cookiecutter/mapper-template/cookiecutter.json @@ -7,6 +7,7 @@ "variant": "None (Skip)", "include_ci_files": ["GitHub", "None (Skip)"], "license": ["Apache-2.0"], + "ide": ["VSCode", "None"], "__prompts__": { "name": "The name of the mapper, in CamelCase", "admin_name": "Provide your [bold yellow]full name[/]", @@ -14,6 +15,7 @@ "mapper_id": "The ID of the tap, in kebab-case", "library_name": "The name of the library, in snake_case. This is how the library will be imported in Python.", "include_ci_files": "Whether to include CI files for a common CI services", - "license": "The license for the project" + "license": "The license for the project", + "ide": "Add configuration files for your preferred IDE" } } diff --git a/cookiecutter/mapper-template/hooks/post_gen_project.py b/cookiecutter/mapper-template/hooks/post_gen_project.py new file mode 100644 index 000000000..a700beb50 --- /dev/null +++ b/cookiecutter/mapper-template/hooks/post_gen_project.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +from pathlib import Path +import shutil + + +BASE_PATH = Path("{{cookiecutter.library_name}}") + + +if __name__ == "__main__": + if "{{ cookiecutter.license }}" != "Apache-2.0": + Path("LICENSE").unlink() + + if "{{ cookiecutter.include_ci_files }}" != "GitHub": + shutil.rmtree(Path(".github")) + + if "{{ cookiecutter.ide }}" != "VSCode": + shutil.rmtree(".vscode") diff --git a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/{% if cookiecutter.include_ci_files == 'GitHub' %}dependabot.yml{%endif%} b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/dependabot.yml similarity index 100% rename from cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/{% if cookiecutter.include_ci_files == 'GitHub' %}dependabot.yml{%endif%} rename to cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/dependabot.yml diff --git a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/workflows/{% if cookiecutter.include_ci_files == 'GitHub' %}test.yml{%endif%} b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/workflows/test.yml similarity index 100% rename from cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/workflows/{% if cookiecutter.include_ci_files == 'GitHub' %}test.yml{%endif%} rename to cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.github/workflows/test.yml diff --git a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/{%if 'Apache-2.0' == cookiecutter.license %}LICENSE{%endif%} b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/LICENSE similarity index 100% rename from cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/{%if 'Apache-2.0' == cookiecutter.license %}LICENSE{%endif%} rename to cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/LICENSE diff --git a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/{{cookiecutter.library_name}}/__main__.py b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/{{cookiecutter.library_name}}/__main__.py new file mode 100644 index 000000000..763efef91 --- /dev/null +++ b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/{{cookiecutter.library_name}}/__main__.py @@ -0,0 +1,7 @@ +"""{{ cookiecutter.name }} entry point.""" + +from __future__ import annotations + +from {{ cookiecutter.library_name }}.mapper import {{ cookiecutter.name }}Mapper + +{{ cookiecutter.name }}Mapper.cli() diff --git a/cookiecutter/tap-template/cookiecutter.json b/cookiecutter/tap-template/cookiecutter.json index 017b31109..4eae4d8ad 100644 --- a/cookiecutter/tap-template/cookiecutter.json +++ b/cookiecutter/tap-template/cookiecutter.json @@ -16,6 +16,7 @@ ], "include_ci_files": ["GitHub", "None"], "license": ["Apache-2.0", "None"], + "ide": ["VSCode", "None"], "__prompts__": { "source_name": "The name of the source, in CamelCase", "admin_name": "Provide your [bold yellow]full name[/]", @@ -25,6 +26,7 @@ "stream_type": "The type of stream the source provides", "auth_method": "The [bold red]authentication[/] method used by the source, for REST and GraphQL sources", "include_ci_files": "Whether to include CI files for a common CI services", - "license": "The license for the project" + "license": "The license for the project", + "ide": "Add configuration files for your preferred IDE" } } diff --git a/cookiecutter/tap-template/hooks/post_gen_project.py b/cookiecutter/tap-template/hooks/post_gen_project.py index 775a3e1ed..ca51924f2 100644 --- a/cookiecutter/tap-template/hooks/post_gen_project.py +++ b/cookiecutter/tap-template/hooks/post_gen_project.py @@ -26,3 +26,6 @@ if "{{ cookiecutter.include_ci_files }}" != "GitHub": shutil.rmtree(".github") + + if "{{ cookiecutter.ide }}" != "VSCode": + shutil.rmtree(".vscode") diff --git a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/{{cookiecutter.library_name}}/__main__.py b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/{{cookiecutter.library_name}}/__main__.py new file mode 100644 index 000000000..a302e899c --- /dev/null +++ b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/{{cookiecutter.library_name}}/__main__.py @@ -0,0 +1,7 @@ +"""{{ cookiecutter.source_name }} entry point.""" + +from __future__ import annotations + +from {{ cookiecutter.library_name }}.tap import Tap{{ cookiecutter.source_name }} + +Tap{{ cookiecutter.source_name }}.cli() diff --git a/cookiecutter/target-template/cookiecutter.json b/cookiecutter/target-template/cookiecutter.json index c7c31835a..2490a31db 100644 --- a/cookiecutter/target-template/cookiecutter.json +++ b/cookiecutter/target-template/cookiecutter.json @@ -8,6 +8,7 @@ "serialization_method": ["Per record", "Per batch", "SQL"], "include_ci_files": ["GitHub", "None (Skip)"], "license": ["Apache-2.0"], + "ide": ["VSCode", "None"], "__prompts__": { "name": "The name of the mapper, in CamelCase", "admin_name": "Provide your [bold yellow]full name[/]", @@ -16,6 +17,7 @@ "library_name": "The name of the library, in snake_case. This is how the library will be imported in Python.", "serialization_method": "The serialization method to use for loading data", "include_ci_files": "Whether to include CI files for a common CI services", - "license": "The license for the project" + "license": "The license for the project", + "ide": "Add configuration files for your preferred IDE" } } diff --git a/cookiecutter/target-template/hooks/post_gen_project.py b/cookiecutter/target-template/hooks/post_gen_project.py index 44edd337b..a700beb50 100644 --- a/cookiecutter/target-template/hooks/post_gen_project.py +++ b/cookiecutter/target-template/hooks/post_gen_project.py @@ -12,3 +12,6 @@ if "{{ cookiecutter.include_ci_files }}" != "GitHub": shutil.rmtree(Path(".github")) + + if "{{ cookiecutter.ide }}" != "VSCode": + shutil.rmtree(".vscode") diff --git a/cookiecutter/target-template/{{cookiecutter.target_id}}/{{cookiecutter.library_name}}/__main__.py b/cookiecutter/target-template/{{cookiecutter.target_id}}/{{cookiecutter.library_name}}/__main__.py new file mode 100644 index 000000000..77b325ec2 --- /dev/null +++ b/cookiecutter/target-template/{{cookiecutter.target_id}}/{{cookiecutter.library_name}}/__main__.py @@ -0,0 +1,7 @@ +"""{{ cookiecutter.destination_name }} entry point.""" + +from __future__ import annotations + +from {{ cookiecutter.library_name }}.target import Target{{ cookiecutter.destination_name }} + +Target{{ cookiecutter.destination_name }}.cli() diff --git a/docs/parent_streams.md b/docs/parent_streams.md index 235dbfac8..fec6d8fb9 100644 --- a/docs/parent_streams.md +++ b/docs/parent_streams.md @@ -8,18 +8,21 @@ from a parent record each time the child stream is invoked. 1. Set `parent_stream_type` in the child-stream's class to the class of the parent. 2. Implement one of the below methods to pass context from the parent to the child: - 1. If using `get_records(context)` you can simply return a tuple instead of a `record` + 1. If using [`get_records`](singer_sdk.Stream.get_child_context) you can simply return a tuple instead of a `record` dictionary. A tuple return value will be interpreted by the SDK as `(record: dict, child_context: dict)`. - 1. Override `get_child_context(record, context: Dict) -> dict` to return a new + 2. Override [`get_child_context`](singer_sdk.Stream.get_child_context) to return a new child context object based on records and any existing context from the parent stream. + 3. If you need to sync more than one child stream per parent record, you can override + [`generate_child_contexts`](singer_sdk.Stream.generate_child_contexts) to yield as many + contexts as you need. 3. If the parent stream's replication key won't get updated when child items are changed, indicate this by adding `ignore_parent_replication_key = True` in the child stream class declaration. 4. If the number of _parent_ items is very large (thousands or tens of thousands), you can - optionally set `state_partitioning_keys` on the child stream to specify a subset of context keys to use + optionally set [`state_partitioning_keys`](singer_sdk.Stream.state_partitioning_keys) on the child stream to specify a subset of context keys to use in state bookmarks. (When not set, the number of bookmarks will be equal to the number - of parent items.) If you do not wish to store any state bookmarks for the child stream, set `state_partitioning_keys` to `[]`. + of parent items.) If you do not wish to store any state bookmarks for the child stream, set[`state_partitioning_keys`](singer_sdk.Stream.state_partitioning_keys) to `[]`. ## Example parent-child implementation diff --git a/e2e-tests/cookiecutters/mapper-base.json b/e2e-tests/cookiecutters/mapper-base.json index 390e8a7ba..01834631d 100644 --- a/e2e-tests/cookiecutters/mapper-base.json +++ b/e2e-tests/cookiecutters/mapper-base.json @@ -8,6 +8,7 @@ "variant": "None (Skip)", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../mapper-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-graphql-jwt.json b/e2e-tests/cookiecutters/tap-graphql-jwt.json index 5daf4ab8f..28b3dfee4 100644 --- a/e2e-tests/cookiecutters/tap-graphql-jwt.json +++ b/e2e-tests/cookiecutters/tap-graphql-jwt.json @@ -10,6 +10,7 @@ "auth_method": "JWT", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-other-custom.json b/e2e-tests/cookiecutters/tap-other-custom.json index 3ea01eaf4..d0aabab09 100644 --- a/e2e-tests/cookiecutters/tap-other-custom.json +++ b/e2e-tests/cookiecutters/tap-other-custom.json @@ -10,6 +10,7 @@ "auth_method": "Custom or N/A", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-rest-api_key-github.json b/e2e-tests/cookiecutters/tap-rest-api_key-github.json index 01570aba8..cb162402a 100644 --- a/e2e-tests/cookiecutters/tap-rest-api_key-github.json +++ b/e2e-tests/cookiecutters/tap-rest-api_key-github.json @@ -10,6 +10,7 @@ "auth_method": "API Key", "include_ci_files": "GitHub", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-rest-basic_auth.json b/e2e-tests/cookiecutters/tap-rest-basic_auth.json index 6c7d7fa19..aa91e3c0d 100644 --- a/e2e-tests/cookiecutters/tap-rest-basic_auth.json +++ b/e2e-tests/cookiecutters/tap-rest-basic_auth.json @@ -10,6 +10,7 @@ "auth_method": "Basic Auth", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-rest-bearer_token.json b/e2e-tests/cookiecutters/tap-rest-bearer_token.json index 157457462..274039845 100644 --- a/e2e-tests/cookiecutters/tap-rest-bearer_token.json +++ b/e2e-tests/cookiecutters/tap-rest-bearer_token.json @@ -10,6 +10,7 @@ "auth_method": "Bearer Token", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-rest-custom.json b/e2e-tests/cookiecutters/tap-rest-custom.json index 831135b7a..67d72dea9 100644 --- a/e2e-tests/cookiecutters/tap-rest-custom.json +++ b/e2e-tests/cookiecutters/tap-rest-custom.json @@ -10,6 +10,7 @@ "auth_method": "Custom or N/A", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-rest-jwt.json b/e2e-tests/cookiecutters/tap-rest-jwt.json index b46807d49..aa3729388 100644 --- a/e2e-tests/cookiecutters/tap-rest-jwt.json +++ b/e2e-tests/cookiecutters/tap-rest-jwt.json @@ -10,6 +10,7 @@ "auth_method": "JWT", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-rest-oauth2.json b/e2e-tests/cookiecutters/tap-rest-oauth2.json index 4a41b80e3..905349aac 100644 --- a/e2e-tests/cookiecutters/tap-rest-oauth2.json +++ b/e2e-tests/cookiecutters/tap-rest-oauth2.json @@ -10,6 +10,7 @@ "auth_method": "OAuth2", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/tap-sql-custom.json b/e2e-tests/cookiecutters/tap-sql-custom.json index 3c5996860..81ac625d0 100644 --- a/e2e-tests/cookiecutters/tap-sql-custom.json +++ b/e2e-tests/cookiecutters/tap-sql-custom.json @@ -10,6 +10,7 @@ "auth_method": "Custom or N/A", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "../tap-template/", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/target-per_record.json b/e2e-tests/cookiecutters/target-per_record.json index f5dde1cef..89f923911 100644 --- a/e2e-tests/cookiecutters/target-per_record.json +++ b/e2e-tests/cookiecutters/target-per_record.json @@ -9,6 +9,7 @@ "serialization_method": "Per record", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "./sdk/cookiecutter/target-template", "_output_dir": "." } diff --git a/e2e-tests/cookiecutters/target-sql.json b/e2e-tests/cookiecutters/target-sql.json index 63691d718..881b3ebe4 100644 --- a/e2e-tests/cookiecutters/target-sql.json +++ b/e2e-tests/cookiecutters/target-sql.json @@ -9,6 +9,7 @@ "serialization_method": "SQL", "include_ci_files": "None (Skip)", "license": "Apache-2.0", + "ide": "VSCode", "_template": "./sdk/cookiecutter/target-template", "_output_dir": "." } diff --git a/noxfile.py b/noxfile.py index 15a00a708..5ebfd0163 100644 --- a/noxfile.py +++ b/noxfile.py @@ -42,24 +42,19 @@ "coverage[toml]", "duckdb", "duckdb-engine", + "pyarrow", "pytest", "pytest-benchmark", "pytest-durations", "pytest-httpserver", "pytest-snapshot", - "pyarrow", + "pytz", "requests-mock", + "rfc3339-validator", "time-machine", ] -def _clean_py312_deps(session: Session, dependencies: list[str]) -> None: - """Clean dependencies for Python 3.12.""" - if session.python == "3.12": - dependencies.remove("duckdb") - dependencies.remove("duckdb-engine") - - @session(python=main_python_version) def mypy(session: Session) -> None: """Check types with mypy.""" @@ -85,7 +80,6 @@ def mypy(session: Session) -> None: @session(python=python_versions) def tests(session: Session) -> None: """Execute pytest tests and compute coverage.""" - _clean_py312_deps(session, test_dependencies) session.install(".[s3,parquet]") session.install(*test_dependencies) @@ -119,7 +113,6 @@ def tests(session: Session) -> None: @session(python=main_python_version) def benches(session: Session) -> None: """Run benchmarks.""" - _clean_py312_deps(session, test_dependencies) session.install(".[s3]") session.install(*test_dependencies) sqlalchemy_version = os.environ.get("SQLALCHEMY_VERSION") @@ -142,7 +135,6 @@ def update_snapshots(session: Session) -> None: """Update pytest snapshots.""" args = session.posargs or ["-m", "snapshot"] - _clean_py312_deps(session, test_dependencies) session.install(".") session.install(*test_dependencies) session.run("pytest", "--snapshot-update", *args) diff --git a/poetry.lock b/poetry.lock index e8a11cda3..09975f5d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -142,19 +142,22 @@ tzdata = ["tzdata"] [[package]] name = "beautifulsoup4" -version = "4.12.2" +version = "4.12.3" description = "Screen-scraping library" optional = true python-versions = ">=3.6.0" files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] @@ -611,50 +614,57 @@ files = [ [[package]] name = "duckdb" -version = "0.9.2" -description = "DuckDB embedded database" +version = "0.9.3.dev2705" +description = "DuckDB in-process database" optional = false python-versions = ">=3.7.0" files = [ - {file = "duckdb-0.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aadcea5160c586704c03a8a796c06a8afffbefefb1986601104a60cb0bfdb5ab"}, - {file = "duckdb-0.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:08215f17147ed83cbec972175d9882387366de2ed36c21cbe4add04b39a5bcb4"}, - {file = "duckdb-0.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee6c2a8aba6850abef5e1be9dbc04b8e72a5b2c2b67f77892317a21fae868fe7"}, - {file = "duckdb-0.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ff49f3da9399900fd58b5acd0bb8bfad22c5147584ad2427a78d937e11ec9d0"}, - {file = "duckdb-0.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5ac5baf8597efd2bfa75f984654afcabcd698342d59b0e265a0bc6f267b3f0"}, - {file = "duckdb-0.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:81c6df905589a1023a27e9712edb5b724566587ef280a0c66a7ec07c8083623b"}, - {file = "duckdb-0.9.2-cp310-cp310-win32.whl", hash = "sha256:a298cd1d821c81d0dec8a60878c4b38c1adea04a9675fb6306c8f9083bbf314d"}, - {file = "duckdb-0.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:492a69cd60b6cb4f671b51893884cdc5efc4c3b2eb76057a007d2a2295427173"}, - {file = "duckdb-0.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:061a9ea809811d6e3025c5de31bc40e0302cfb08c08feefa574a6491e882e7e8"}, - {file = "duckdb-0.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a43f93be768af39f604b7b9b48891f9177c9282a408051209101ff80f7450d8f"}, - {file = "duckdb-0.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac29c8c8f56fff5a681f7bf61711ccb9325c5329e64f23cb7ff31781d7b50773"}, - {file = "duckdb-0.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b14d98d26bab139114f62ade81350a5342f60a168d94b27ed2c706838f949eda"}, - {file = "duckdb-0.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:796a995299878913e765b28cc2b14c8e44fae2f54ab41a9ee668c18449f5f833"}, - {file = "duckdb-0.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6cb64ccfb72c11ec9c41b3cb6181b6fd33deccceda530e94e1c362af5f810ba1"}, - {file = "duckdb-0.9.2-cp311-cp311-win32.whl", hash = "sha256:930740cb7b2cd9e79946e1d3a8f66e15dc5849d4eaeff75c8788d0983b9256a5"}, - {file = "duckdb-0.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:c28f13c45006fd525001b2011cdf91fa216530e9751779651e66edc0e446be50"}, - {file = "duckdb-0.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fbce7bbcb4ba7d99fcec84cec08db40bc0dd9342c6c11930ce708817741faeeb"}, - {file = "duckdb-0.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15a82109a9e69b1891f0999749f9e3265f550032470f51432f944a37cfdc908b"}, - {file = "duckdb-0.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9490fb9a35eb74af40db5569d90df8a04a6f09ed9a8c9caa024998c40e2506aa"}, - {file = "duckdb-0.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:696d5c6dee86c1a491ea15b74aafe34ad2b62dcd46ad7e03b1d00111ca1a8c68"}, - {file = "duckdb-0.9.2-cp37-cp37m-win32.whl", hash = "sha256:4f0935300bdf8b7631ddfc838f36a858c1323696d8c8a2cecbd416bddf6b0631"}, - {file = "duckdb-0.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:0aab900f7510e4d2613263865570203ddfa2631858c7eb8cbed091af6ceb597f"}, - {file = "duckdb-0.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7d8130ed6a0c9421b135d0743705ea95b9a745852977717504e45722c112bf7a"}, - {file = "duckdb-0.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:974e5de0294f88a1a837378f1f83330395801e9246f4e88ed3bfc8ada65dcbee"}, - {file = "duckdb-0.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4fbc297b602ef17e579bb3190c94d19c5002422b55814421a0fc11299c0c1100"}, - {file = "duckdb-0.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dd58a0d84a424924a35b3772419f8cd78a01c626be3147e4934d7a035a8ad68"}, - {file = "duckdb-0.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11a1194a582c80dfb57565daa06141727e415ff5d17e022dc5f31888a5423d33"}, - {file = "duckdb-0.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:be45d08541002a9338e568dca67ab4f20c0277f8f58a73dfc1435c5b4297c996"}, - {file = "duckdb-0.9.2-cp38-cp38-win32.whl", hash = "sha256:dd6f88aeb7fc0bfecaca633629ff5c986ac966fe3b7dcec0b2c48632fd550ba2"}, - {file = "duckdb-0.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:28100c4a6a04e69aa0f4a6670a6d3d67a65f0337246a0c1a429f3f28f3c40b9a"}, - {file = "duckdb-0.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ae5bf0b6ad4278e46e933e51473b86b4b932dbc54ff097610e5b482dd125552"}, - {file = "duckdb-0.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e5d0bb845a80aa48ed1fd1d2d285dd352e96dc97f8efced2a7429437ccd1fe1f"}, - {file = "duckdb-0.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ce262d74a52500d10888110dfd6715989926ec936918c232dcbaddb78fc55b4"}, - {file = "duckdb-0.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6935240da090a7f7d2666f6d0a5e45ff85715244171ca4e6576060a7f4a1200e"}, - {file = "duckdb-0.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5cfb93e73911696a98b9479299d19cfbc21dd05bb7ab11a923a903f86b4d06e"}, - {file = "duckdb-0.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:64e3bc01751f31e7572d2716c3e8da8fe785f1cdc5be329100818d223002213f"}, - {file = "duckdb-0.9.2-cp39-cp39-win32.whl", hash = "sha256:6e5b80f46487636368e31b61461940e3999986359a78660a50dfdd17dd72017c"}, - {file = "duckdb-0.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:e6142a220180dbeea4f341708bd5f9501c5c962ce7ef47c1cadf5e8810b4cb13"}, - {file = "duckdb-0.9.2.tar.gz", hash = "sha256:3843afeab7c3fc4a4c0b53686a4cc1d9cdbdadcbb468d60fef910355ecafd447"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:08a1eda7ef70113435ac0e15b1d93115e95dad32ff4173228bfa593d41b968ed"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:459552f83fc919f683b1f824ac4ce70bed2e8a11c72986fd6623caf9b349f80d"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1a8c3fd64086053d5dbd43e72a041d721bf44cb4e570c94c8ffc65058409af2"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29697d564b403ec679bd5e81a74c3c621292ffb12144e3215d17fc0cb043b163"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c75478952f9b969ddee4a9550449a70bab32a2ab7e0f50a261c8d918eea15171"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6bd75d0a6171353e8b425df19f868a5d83f1bea77e25b970680963439073a1e8"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d83af0116de0ce7ebbaa21e1b2ecf5e89980a7092ad5390a02ca4c570b79fd4b"}, + {file = "duckdb-0.9.3.dev2705-cp310-cp310-win_amd64.whl", hash = "sha256:fd603bdea86644819ab892a6b03c6eec07e0b2cea9f5b06f5b3b7c5cfcdf1e7b"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9315a5945aae127f1931170954c0a05da9444dbfb5b48deb9ce3d0b1d5102beb"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9c73db7bc4226320cb4669f2538aa3299c4bb300b7126d0c933b10cfc8fa3e4b"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:12022719c506db8be5f31a1292a23ac38069e1a7b88fc9634ba4f8cc303bb2bb"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a738849a6cbbf32afa0d0f4b2a5bc3bdcdc644b0ac847f87d940c7a4691773"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c57280104d0e57615942de97e279708a377565ef218f76ee789b5a0c1848d799"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:774b92033fabf7acc9158b3bef1fd60c3760f1a7e1cd96d2da824621031f42f7"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c83f885a81e3fe46da3bc5fd60558e2ed3a24437fc2e5f2dc819e5f1b2a24ece"}, + {file = "duckdb-0.9.3.dev2705-cp311-cp311-win_amd64.whl", hash = "sha256:daa327ac390af8a0a289c77fb5bb86a397cdb736dc9e49c27798c710b4274680"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f5c032b530474ee85f2e391ead57518018903817537656b0595e897b47dd3b64"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bb91a83481ed053ca8cc0d42e9233efac46692b14507b8c6084b9a91a73ff17c"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:79a3b78b5aea31ec7e8d8a1ae738a2185dc80d9bca1b4450377ce5b48b149178"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5a83ef94f831976e39b5620088d4b0cc2ac5e6dfe664775fdc78457753eb6d2"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e73edb3f36e30c04bc8b2bbf50054125f379ed5e947dbee5128de103a4ace50"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5516c8d9532d1982851bbc59ac38bf3dc1ec9113567476fbd8eb48bf603da45c"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1c58fbff5cd88e5e074ad1d2437c3a821c346ee61d01028c5aa4eec9f8c60b40"}, + {file = "duckdb-0.9.3.dev2705-cp312-cp312-win_amd64.whl", hash = "sha256:432ac8d8a1bfbe56967052c0a92ae375a1ce6a3e165ca85704479f2b86525834"}, + {file = "duckdb-0.9.3.dev2705-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:854a4566b5fe427f295e05551d3849ab6deaa1bad07f7383d24f4a31a78bd459"}, + {file = "duckdb-0.9.3.dev2705-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4065ac8eda6c1b8c3d7de666b133364f4af0db18c0fdde0122ef7986bc97cd63"}, + {file = "duckdb-0.9.3.dev2705-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0978fe3a1cad5b911885a1b91b613615fd9f649496f1a153000afb56621c01b2"}, + {file = "duckdb-0.9.3.dev2705-cp37-cp37m-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b6034c407d5f1268d91a4ced153a06ef0480b51c0cd632b233714e1026222e8"}, + {file = "duckdb-0.9.3.dev2705-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:149166680c390731e64d4cb8924bfa033ec0b503004726715da9cfaedbbc503e"}, + {file = "duckdb-0.9.3.dev2705-cp37-cp37m-win_amd64.whl", hash = "sha256:a58b061d552e54c2a1cc1475ca2b0cd8d31c8e9edc56406e83ddbccb27bded86"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ec4b7bbc064f2f2bb0e55a3db48a6d103cfad60dc611efb5cd5a98c0e0a35cd"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d74f218ee85792292b9873b8040f878dc2b3465d185e21c38165be136a1f2cda"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:41ae12720c915ecee590991ceede8eaead5cb87b1302c1f685ab9a644bea1c0e"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f3c01418ec03b457bec43c7b228280eaf5d33a27883458ecddc485f0846bad"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cfb43a04514d17a4b48b80b2abbe59b9ca1ab1e595235411d5155f0f8ee3976"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac8c503051e019d16a26dc82b3dd1fe2107a9b624d752864414d762f997e1af8"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:952ae36eb7a7e480baf695ece6098b3580cb029ee5f0071bb2b116faecb27cf4"}, + {file = "duckdb-0.9.3.dev2705-cp38-cp38-win_amd64.whl", hash = "sha256:594f4b1262cf36a8c137ac7f20bac6a89b8fcd9292e761b3c7dbbe7b4763329b"}, + {file = "duckdb-0.9.3.dev2705-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:305b3a3c28dad642b09926bd07910e73c1ffa2d3c5573c0fd7c7b8f82a38bdb2"}, + {file = "duckdb-0.9.3.dev2705-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f745759001a9fdb862ddb87cfd8a2b09fb5d3c65d1c9c659eb59aa9f4ba3d461"}, + {file = "duckdb-0.9.3.dev2705-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c92c4e3416a28be2bb7b7b9e6b8cc383c739f54d27c548baef151f54444c6587"}, + {file = "duckdb-0.9.3.dev2705-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68ba13064272afe2e74daf1e6999f695a9f2298a4eb53615bcf8d679a6b4a50d"}, + {file = "duckdb-0.9.3.dev2705-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43b74df9a84927f85a843ea65952bef4e35175dfc8285804350fc374210060f3"}, + {file = "duckdb-0.9.3.dev2705-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2445b5e0eec59fc0e3c0bb734a67c4a0367cb6f40444c1e2dd773e95d9f625d5"}, + {file = "duckdb-0.9.3.dev2705-cp39-cp39-win_amd64.whl", hash = "sha256:a176e0290ff151a8ca4757bb0e977380b12ff6ae76dfd4dea13963421bc6759e"}, + {file = "duckdb-0.9.3.dev2705.tar.gz", hash = "sha256:9d2c502007f69dd12e9cce82b37f7612e7d03e9ab9a8b6a1baf4247f2bd8c7bd"}, ] [[package]] @@ -908,13 +918,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." optional = true python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -947,13 +957,13 @@ files = [ [[package]] name = "jsonpath-ng" -version = "1.6.0" +version = "1.6.1" description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." optional = false python-versions = "*" files = [ - {file = "jsonpath-ng-1.6.0.tar.gz", hash = "sha256:5483f8e9d74c39c9abfab554c070ae783c1c8cbadf5df60d561bc705ac68a07e"}, - {file = "jsonpath_ng-1.6.0-py3-none-any.whl", hash = "sha256:6fd04833412c4b3d9299edf369542f5e67095ca84efa17cbb7f06a34958adc9f"}, + {file = "jsonpath-ng-1.6.1.tar.gz", hash = "sha256:086c37ba4917304850bd837aeab806670224d3f038fe2833ff593a672ef0a5fa"}, + {file = "jsonpath_ng-1.6.1-py3-none-any.whl", hash = "sha256:8f22cd8273d7772eea9aaa84d922e0841aa36fdb8a2c6b7f6c3791a16a9bc0be"}, ] [package.dependencies] @@ -984,13 +994,13 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema" -version = "4.20.0" +version = "4.21.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.20.0-py3-none-any.whl", hash = "sha256:ed6231f0429ecf966f5bc8dfef245998220549cbbcf140f913b7464c52c3b6b3"}, - {file = "jsonschema-4.20.0.tar.gz", hash = "sha256:4f614fd46d8d61258610998997743ec5492a648b33cf478c1ddc23ed4598a5fa"}, + {file = "jsonschema-4.21.0-py3-none-any.whl", hash = "sha256:70a09719d375c0a2874571b363c8a24be7df8071b80c9aa76bc4551e7297c63c"}, + {file = "jsonschema-4.21.0.tar.gz", hash = "sha256:3ba18e27f7491ea4a1b22edce00fb820eec968d397feb3f9cb61d5894bb38167"}, ] [package.dependencies] @@ -1380,47 +1390,47 @@ files = [ [[package]] name = "numpy" -version = "1.26.2" +version = "1.26.3" description = "Fundamental package for array computing in Python" optional = true python-versions = ">=3.9" files = [ - {file = "numpy-1.26.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3703fc9258a4a122d17043e57b35e5ef1c5a5837c3db8be396c82e04c1cf9b0f"}, - {file = "numpy-1.26.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc392fdcbd21d4be6ae1bb4475a03ce3b025cd49a9be5345d76d7585aea69440"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36340109af8da8805d8851ef1d74761b3b88e81a9bd80b290bbfed61bd2b4f75"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc008217145b3d77abd3e4d5ef586e3bdfba8fe17940769f8aa09b99e856c00"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ced40d4e9e18242f70dd02d739e44698df3dcb010d31f495ff00a31ef6014fe"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b272d4cecc32c9e19911891446b72e986157e6a1809b7b56518b4f3755267523"}, - {file = "numpy-1.26.2-cp310-cp310-win32.whl", hash = "sha256:22f8fc02fdbc829e7a8c578dd8d2e15a9074b630d4da29cda483337e300e3ee9"}, - {file = "numpy-1.26.2-cp310-cp310-win_amd64.whl", hash = "sha256:26c9d33f8e8b846d5a65dd068c14e04018d05533b348d9eaeef6c1bd787f9919"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b96e7b9c624ef3ae2ae0e04fa9b460f6b9f17ad8b4bec6d7756510f1f6c0c841"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa18428111fb9a591d7a9cc1b48150097ba6a7e8299fb56bdf574df650e7d1f1"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06fa1ed84aa60ea6ef9f91ba57b5ed963c3729534e6e54055fc151fad0423f0a"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca5482c3dbdd051bcd1fce8034603d6ebfc125a7bd59f55b40d8f5d246832b"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:854ab91a2906ef29dc3925a064fcd365c7b4da743f84b123002f6139bcb3f8a7"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f43740ab089277d403aa07567be138fc2a89d4d9892d113b76153e0e412409f8"}, - {file = "numpy-1.26.2-cp311-cp311-win32.whl", hash = "sha256:a2bbc29fcb1771cd7b7425f98b05307776a6baf43035d3b80c4b0f29e9545186"}, - {file = "numpy-1.26.2-cp311-cp311-win_amd64.whl", hash = "sha256:2b3fca8a5b00184828d12b073af4d0fc5fdd94b1632c2477526f6bd7842d700d"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a4cd6ed4a339c21f1d1b0fdf13426cb3b284555c27ac2f156dfdaaa7e16bfab0"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d5244aabd6ed7f312268b9247be47343a654ebea52a60f002dc70c769048e75"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3cdb4d9c70e6b8c0814239ead47da00934666f668426fc6e94cce869e13fd7"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa317b2325f7aa0a9471663e6093c210cb2ae9c0ad824732b307d2c51983d5b6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:174a8880739c16c925799c018f3f55b8130c1f7c8e75ab0a6fa9d41cab092fd6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f79b231bf5c16b1f39c7f4875e1ded36abee1591e98742b05d8a0fb55d8a3eec"}, - {file = "numpy-1.26.2-cp312-cp312-win32.whl", hash = "sha256:4a06263321dfd3598cacb252f51e521a8cb4b6df471bb12a7ee5cbab20ea9167"}, - {file = "numpy-1.26.2-cp312-cp312-win_amd64.whl", hash = "sha256:b04f5dc6b3efdaab541f7857351aac359e6ae3c126e2edb376929bd3b7f92d7e"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4eb8df4bf8d3d90d091e0146f6c28492b0be84da3e409ebef54349f71ed271ef"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a13860fdcd95de7cf58bd6f8bc5a5ef81c0b0625eb2c9a783948847abbef2c2"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64308ebc366a8ed63fd0bf426b6a9468060962f1a4339ab1074c228fa6ade8e3"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf8aab04a2c0e859da118f0b38617e5ee65d75b83795055fb66c0d5e9e9b818"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d73a3abcac238250091b11caef9ad12413dab01669511779bc9b29261dd50210"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b361d369fc7e5e1714cf827b731ca32bff8d411212fccd29ad98ad622449cc36"}, - {file = "numpy-1.26.2-cp39-cp39-win32.whl", hash = "sha256:bd3f0091e845164a20bd5a326860c840fe2af79fa12e0469a12768a3ec578d80"}, - {file = "numpy-1.26.2-cp39-cp39-win_amd64.whl", hash = "sha256:2beef57fb031dcc0dc8fa4fe297a742027b954949cabb52a2a376c144e5e6060"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1cc3d5029a30fb5f06704ad6b23b35e11309491c999838c31f124fee32107c79"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc3c222bb9fb5a12e334d0479b97bb2df446fbe622b470928f5284ffca3f8d"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe6b44fb8fcdf7eda4ef4461b97b3f63c466b27ab151bec2366db8b197387841"}, - {file = "numpy-1.26.2.tar.gz", hash = "sha256:f65738447676ab5777f11e6bbbdb8ce11b785e105f690bc45966574816b6d3ea"}, + {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, + {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"}, + {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"}, + {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"}, + {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"}, + {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"}, + {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"}, + {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"}, + {file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"}, + {file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"}, + {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"}, ] [[package]] @@ -2005,13 +2015,13 @@ files = [ [[package]] name = "referencing" -version = "0.32.0" +version = "0.32.1" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.32.0-py3-none-any.whl", hash = "sha256:bdcd3efb936f82ff86f993093f6da7435c7de69a3b3a5a06678a6050184bee99"}, - {file = "referencing-0.32.0.tar.gz", hash = "sha256:689e64fe121843dcfd57b71933318ef1f91188ffb45367332700a86ac8fd6161"}, + {file = "referencing-0.32.1-py3-none-any.whl", hash = "sha256:7e4dc12271d8e15612bfe35792f5ea1c40970dadf8624602e33db2758f7ee554"}, + {file = "referencing-0.32.1.tar.gz", hash = "sha256:3c57da0513e9563eb7e203ebe9bb3a1b509b042016433bd1e45a2853466c3dd3"}, ] [package.dependencies] @@ -2058,112 +2068,126 @@ six = "*" fixture = ["fixtures"] test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"] +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] + +[package.dependencies] +six = "*" + [[package]] name = "rpds-py" -version = "0.16.2" +version = "0.17.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:509b617ac787cd1149600e731db9274ebbef094503ca25158e6f23edaba1ca8f"}, - {file = "rpds_py-0.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:413b9c17388bbd0d87a329d8e30c1a4c6e44e2bb25457f43725a8e6fe4161e9e"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2946b120718eba9af2b4dd103affc1164a87b9e9ebff8c3e4c05d7b7a7e274e2"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35ae5ece284cf36464eb160880018cf6088a9ac5ddc72292a6092b6ef3f4da53"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc6a7620ba7639a3db6213da61312cb4aa9ac0ca6e00dc1cbbdc21c2aa6eb57"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cb6fe8ecdfffa0e711a75c931fb39f4ba382b4b3ccedeca43f18693864fe850"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dace7b26a13353e24613417ce2239491b40a6ad44e5776a18eaff7733488b44"}, - {file = "rpds_py-0.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bdbc5fcb04a7309074de6b67fa9bc4b418ab3fc435fec1f2779a0eced688d04"}, - {file = "rpds_py-0.16.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f42e25c016927e2a6b1ce748112c3ab134261fc2ddc867e92d02006103e1b1b7"}, - {file = "rpds_py-0.16.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eab36eae3f3e8e24b05748ec9acc66286662f5d25c52ad70cadab544e034536b"}, - {file = "rpds_py-0.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0474df4ade9a3b4af96c3d36eb81856cb9462e4c6657d4caecfd840d2a13f3c9"}, - {file = "rpds_py-0.16.2-cp310-none-win32.whl", hash = "sha256:84c5a4d1f9dd7e2d2c44097fb09fffe728629bad31eb56caf97719e55575aa82"}, - {file = "rpds_py-0.16.2-cp310-none-win_amd64.whl", hash = "sha256:2bd82db36cd70b3628c0c57d81d2438e8dd4b7b32a6a9f25f24ab0e657cb6c4e"}, - {file = "rpds_py-0.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:adc0c3d6fc6ae35fee3e4917628983f6ce630d513cbaad575b4517d47e81b4bb"}, - {file = "rpds_py-0.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ec23fcad480e77ede06cf4127a25fc440f7489922e17fc058f426b5256ee0edb"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07aab64e2808c3ebac2a44f67e9dc0543812b715126dfd6fe4264df527556cb6"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4ebb8b20bd09c5ce7884c8f0388801100f5e75e7f733b1b6613c713371feefc"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3d7e2ea25d3517c6d7e5a1cc3702cffa6bd18d9ef8d08d9af6717fc1c700eed"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f28ac0e8e7242d140f99402a903a2c596ab71550272ae9247ad78f9a932b5698"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19f00f57fdd38db4bb5ad09f9ead1b535332dbf624200e9029a45f1f35527ebb"}, - {file = "rpds_py-0.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3da5a4c56953bdbf6d04447c3410309616c54433146ccdb4a277b9cb499bc10e"}, - {file = "rpds_py-0.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec2e1cf025b2c0f48ec17ff3e642661da7ee332d326f2e6619366ce8e221f018"}, - {file = "rpds_py-0.16.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e0441fb4fdd39a230477b2ca9be90868af64425bfe7b122b57e61e45737a653b"}, - {file = "rpds_py-0.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9f0350ef2fba5f34eb0c9000ea328e51b9572b403d2f7f3b19f24085f6f598e8"}, - {file = "rpds_py-0.16.2-cp311-none-win32.whl", hash = "sha256:5a80e2f83391ad0808b4646732af2a7b67550b98f0cae056cb3b40622a83dbb3"}, - {file = "rpds_py-0.16.2-cp311-none-win_amd64.whl", hash = "sha256:e04e56b4ca7a770593633556e8e9e46579d66ec2ada846b401252a2bdcf70a6d"}, - {file = "rpds_py-0.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:5e6caa3809e50690bd92fa490f5c38caa86082c8c3315aa438bce43786d5e90d"}, - {file = "rpds_py-0.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e53b9b25cac9065328901713a7e9e3b12e4f57ef4280b370fbbf6fef2052eef"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af27423662f32d7501a00c5e7342f7dbd1e4a718aea7a239781357d15d437133"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d4dd5fb16eb3825742bad8339d454054261ab59fed2fbac84e1d84d5aae7ba"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e061de3b745fe611e23cd7318aec2c8b0e4153939c25c9202a5811ca911fd733"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b811d182ad17ea294f2ec63c0621e7be92a1141e1012383461872cead87468f"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5552f328eaef1a75ff129d4d0c437bf44e43f9436d3996e8eab623ea0f5fcf73"}, - {file = "rpds_py-0.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dcbe1f8dd179e4d69b70b1f1d9bb6fd1e7e1bdc9c9aad345cdeb332e29d40748"}, - {file = "rpds_py-0.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8aad80645a011abae487d356e0ceb359f4938dfb6f7bcc410027ed7ae4f7bb8b"}, - {file = "rpds_py-0.16.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6f5549d6ed1da9bfe3631ca9483ae906f21410be2445b73443fa9f017601c6f"}, - {file = "rpds_py-0.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d452817e0d9c749c431a1121d56a777bd7099b720b3d1c820f1725cb40928f58"}, - {file = "rpds_py-0.16.2-cp312-none-win32.whl", hash = "sha256:888a97002e986eca10d8546e3c8b97da1d47ad8b69726dcfeb3e56348ebb28a3"}, - {file = "rpds_py-0.16.2-cp312-none-win_amd64.whl", hash = "sha256:d8dda2a806dfa4a9b795950c4f5cc56d6d6159f7d68080aedaff3bdc9b5032f5"}, - {file = "rpds_py-0.16.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:071980663c273bf3d388fe5c794c547e6f35ba3335477072c713a3176bf14a60"}, - {file = "rpds_py-0.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:726ac36e8a3bb8daef2fd482534cabc5e17334052447008405daca7ca04a3108"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9e557db6a177470316c82f023e5d571811c9a4422b5ea084c85da9aa3c035fc"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90123853fc8b1747f80b0d354be3d122b4365a93e50fc3aacc9fb4c2488845d6"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a61f659665a39a4d17d699ab3593d7116d66e1e2e3f03ef3fb8f484e91908808"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc97f0640e91d7776530f06e6836c546c1c752a52de158720c4224c9e8053cad"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a54e99a2b9693a37ebf245937fd6e9228b4cbd64b9cc961e1f3391ec6c7391"}, - {file = "rpds_py-0.16.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4b677d929cf1f6bac07ad76e0f2d5de367e6373351c01a9c0a39f6b21b4a8b"}, - {file = "rpds_py-0.16.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:5ef00873303d678aaf8b0627e111fd434925ca01c657dbb2641410f1cdaef261"}, - {file = "rpds_py-0.16.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:349cb40897fd529ca15317c22c0eab67f5ac5178b5bd2c6adc86172045210acc"}, - {file = "rpds_py-0.16.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2ddef620e70eaffebed5932ce754d539c0930f676aae6212f8e16cd9743dd365"}, - {file = "rpds_py-0.16.2-cp38-none-win32.whl", hash = "sha256:882ce6e25e585949c3d9f9abd29202367175e0aab3aba0c58c9abbb37d4982ff"}, - {file = "rpds_py-0.16.2-cp38-none-win_amd64.whl", hash = "sha256:f4bd4578e44f26997e9e56c96dedc5f1af43cc9d16c4daa29c771a00b2a26851"}, - {file = "rpds_py-0.16.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:69ac7ea9897ec201ce68b48582f3eb34a3f9924488a5432a93f177bf76a82a7e"}, - {file = "rpds_py-0.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a9880b4656efe36ccad41edc66789e191e5ee19a1ea8811e0aed6f69851a82f4"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee94cb58c0ba2c62ee108c2b7c9131b2c66a29e82746e8fa3aa1a1effbd3dcf1"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24f7a2eb3866a9e91f4599851e0c8d39878a470044875c49bd528d2b9b88361c"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca57468da2d9a660bcf8961637c85f2fbb2aa64d9bc3f9484e30c3f9f67b1dd7"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccd4e400309e1f34a5095bf9249d371f0fd60f8a3a5c4a791cad7b99ce1fd38d"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80443fe2f7b3ea3934c5d75fb0e04a5dbb4a8e943e5ff2de0dec059202b70a8b"}, - {file = "rpds_py-0.16.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d6a9f052e72d493efd92a77f861e45bab2f6be63e37fa8ecf0c6fd1a58fedb0"}, - {file = "rpds_py-0.16.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:35953f4f2b3216421af86fd236b7c0c65935936a94ea83ddbd4904ba60757773"}, - {file = "rpds_py-0.16.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:981d135c7cdaf6cd8eadae1c950de43b976de8f09d8e800feed307140d3d6d00"}, - {file = "rpds_py-0.16.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d0dd7ed2f16df2e129496e7fbe59a34bc2d7fc8db443a606644d069eb69cbd45"}, - {file = "rpds_py-0.16.2-cp39-none-win32.whl", hash = "sha256:703d95c75a72e902544fda08e965885525e297578317989fd15a6ce58414b41d"}, - {file = "rpds_py-0.16.2-cp39-none-win_amd64.whl", hash = "sha256:e93ec1b300acf89730cf27975ef574396bc04edecc358e9bd116fb387a123239"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:44627b6ca7308680a70766454db5249105fa6344853af6762eaad4158a2feebe"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3f91df8e6dbb7360e176d1affd5fb0246d2b88d16aa5ebc7db94fd66b68b61da"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d904c5693e08bad240f16d79305edba78276be87061c872a4a15e2c301fa2c0"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:290a81cfbe4673285cdf140ec5cd1658ffbf63ab359f2b352ebe172e7cfa5bf0"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b634c5ec0103c5cbebc24ebac4872b045cccb9456fc59efdcf6fe39775365bd2"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a297a4d08cc67c7466c873c78039d87840fb50d05473db0ec1b7b03d179bf322"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2e75e17bd0bb66ee34a707da677e47c14ee51ccef78ed6a263a4cc965a072a1"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f1b9d9260e06ea017feb7172976ab261e011c1dc2f8883c7c274f6b2aabfe01a"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:162d7cd9cd311c1b0ff1c55a024b8f38bd8aad1876b648821da08adc40e95734"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:9b32f742ce5b57201305f19c2ef7a184b52f6f9ba6871cc042c2a61f0d6b49b8"}, - {file = "rpds_py-0.16.2-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac08472f41ea77cd6a5dae36ae7d4ed3951d6602833af87532b556c1b4601d63"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:495a14b72bbe217f2695dcd9b5ab14d4f8066a00f5d209ed94f0aca307f85f6e"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:8d6b6937ae9eac6d6c0ca3c42774d89fa311f55adff3970fb364b34abde6ed3d"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a61226465bda9283686db8f17d02569a98e4b13c637be5a26d44aa1f1e361c2"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5cf6af100ffb5c195beec11ffaa8cf8523057f123afa2944e6571d54da84cdc9"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6df15846ee3fb2e6397fe25d7ca6624af9f89587f3f259d177b556fed6bebe2c"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1be2f033df1b8be8c3167ba3c29d5dca425592ee31e35eac52050623afba5772"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96f957d6ab25a78b9e7fc9749d754b98eac825a112b4e666525ce89afcbd9ed5"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:088396c7c70e59872f67462fcac3ecbded5233385797021976a09ebd55961dfe"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4c46ad6356e1561f2a54f08367d1d2e70a0a1bb2db2282d2c1972c1d38eafc3b"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:47713dc4fce213f5c74ca8a1f6a59b622fc1b90868deb8e8e4d993e421b4b39d"}, - {file = "rpds_py-0.16.2-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f811771019f063bbd0aa7bb72c8a934bc13ebacb4672d712fc1639cfd314cccc"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f19afcfc0dd0dca35694df441e9b0f95bc231b512f51bded3c3d8ca32153ec19"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4b682c5775d6a3d21e314c10124599976809455ee67020e8e72df1769b87bc3"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c647ca87fc0ebe808a41de912e9a1bfef9acb85257e5d63691364ac16b81c1f0"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:302bd4983bbd47063e452c38be66153760112f6d3635c7eeefc094299fa400a9"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf721ede3eb7b829e4a9b8142bd55db0bdc82902720548a703f7e601ee13bdc3"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:358dafc89ce3894c7f486c615ba914609f38277ef67f566abc4c854d23b997fa"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cad0f59ee3dc35526039f4bc23642d52d5f6616b5f687d846bfc6d0d6d486db0"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cffa76b385dfe1e38527662a302b19ffb0e7f5cf7dd5e89186d2c94a22dd9d0c"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:83640a5d7cd3bff694747d50436b8b541b5b9b9782b0c8c1688931d6ee1a1f2d"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:ed99b4f7179d2111702020fd7d156e88acd533f5a7d3971353e568b6051d5c97"}, - {file = "rpds_py-0.16.2-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4022b9dc620e14f30201a8a73898a873c8e910cb642bcd2f3411123bc527f6ac"}, - {file = "rpds_py-0.16.2.tar.gz", hash = "sha256:781ef8bfc091b19960fc0142a23aedadafa826bc32b433fdfe6fd7f964d7ef44"}, + {file = "rpds_py-0.17.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4128980a14ed805e1b91a7ed551250282a8ddf8201a4e9f8f5b7e6225f54170d"}, + {file = "rpds_py-0.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ff1dcb8e8bc2261a088821b2595ef031c91d499a0c1b031c152d43fe0a6ecec8"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d65e6b4f1443048eb7e833c2accb4fa7ee67cc7d54f31b4f0555b474758bee55"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a71169d505af63bb4d20d23a8fbd4c6ce272e7bce6cc31f617152aa784436f29"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:436474f17733c7dca0fbf096d36ae65277e8645039df12a0fa52445ca494729d"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10162fe3f5f47c37ebf6d8ff5a2368508fe22007e3077bf25b9c7d803454d921"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:720215373a280f78a1814becb1312d4e4d1077b1202a56d2b0815e95ccb99ce9"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70fcc6c2906cfa5c6a552ba7ae2ce64b6c32f437d8f3f8eea49925b278a61453"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:91e5a8200e65aaac342a791272c564dffcf1281abd635d304d6c4e6b495f29dc"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:99f567dae93e10be2daaa896e07513dd4bf9c2ecf0576e0533ac36ba3b1d5394"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24e4900a6643f87058a27320f81336d527ccfe503984528edde4bb660c8c8d59"}, + {file = "rpds_py-0.17.1-cp310-none-win32.whl", hash = "sha256:0bfb09bf41fe7c51413f563373e5f537eaa653d7adc4830399d4e9bdc199959d"}, + {file = "rpds_py-0.17.1-cp310-none-win_amd64.whl", hash = "sha256:20de7b7179e2031a04042e85dc463a93a82bc177eeba5ddd13ff746325558aa6"}, + {file = "rpds_py-0.17.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:65dcf105c1943cba45d19207ef51b8bc46d232a381e94dd38719d52d3980015b"}, + {file = "rpds_py-0.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:01f58a7306b64e0a4fe042047dd2b7d411ee82e54240284bab63e325762c1147"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:071bc28c589b86bc6351a339114fb7a029f5cddbaca34103aa573eba7b482382"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae35e8e6801c5ab071b992cb2da958eee76340e6926ec693b5ff7d6381441745"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149c5cd24f729e3567b56e1795f74577aa3126c14c11e457bec1b1c90d212e38"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e796051f2070f47230c745d0a77a91088fbee2cc0502e9b796b9c6471983718c"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e820ee1004327609b28db8307acc27f5f2e9a0b185b2064c5f23e815f248f8"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1957a2ab607f9added64478a6982742eb29f109d89d065fa44e01691a20fc20a"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8587fd64c2a91c33cdc39d0cebdaf30e79491cc029a37fcd458ba863f8815383"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4dc889a9d8a34758d0fcc9ac86adb97bab3fb7f0c4d29794357eb147536483fd"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2953937f83820376b5979318840f3ee47477d94c17b940fe31d9458d79ae7eea"}, + {file = "rpds_py-0.17.1-cp311-none-win32.whl", hash = "sha256:1bfcad3109c1e5ba3cbe2f421614e70439f72897515a96c462ea657261b96518"}, + {file = "rpds_py-0.17.1-cp311-none-win_amd64.whl", hash = "sha256:99da0a4686ada4ed0f778120a0ea8d066de1a0a92ab0d13ae68492a437db78bf"}, + {file = "rpds_py-0.17.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1dc29db3900cb1bb40353772417800f29c3d078dbc8024fd64655a04ee3c4bdf"}, + {file = "rpds_py-0.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82ada4a8ed9e82e443fcef87e22a3eed3654dd3adf6e3b3a0deb70f03e86142a"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d36b2b59e8cc6e576f8f7b671e32f2ff43153f0ad6d0201250a7c07f25d570e"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3677fcca7fb728c86a78660c7fb1b07b69b281964673f486ae72860e13f512ad"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:516fb8c77805159e97a689e2f1c80655c7658f5af601c34ffdb916605598cda2"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df3b6f45ba4515632c5064e35ca7f31d51d13d1479673185ba8f9fefbbed58b9"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a967dd6afda7715d911c25a6ba1517975acd8d1092b2f326718725461a3d33f9"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dbbb95e6fc91ea3102505d111b327004d1c4ce98d56a4a02e82cd451f9f57140"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02866e060219514940342a1f84303a1ef7a1dad0ac311792fbbe19b521b489d2"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2528ff96d09f12e638695f3a2e0c609c7b84c6df7c5ae9bfeb9252b6fa686253"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd345a13ce06e94c753dab52f8e71e5252aec1e4f8022d24d56decd31e1b9b23"}, + {file = "rpds_py-0.17.1-cp312-none-win32.whl", hash = "sha256:2a792b2e1d3038daa83fa474d559acfd6dc1e3650ee93b2662ddc17dbff20ad1"}, + {file = "rpds_py-0.17.1-cp312-none-win_amd64.whl", hash = "sha256:292f7344a3301802e7c25c53792fae7d1593cb0e50964e7bcdcc5cf533d634e3"}, + {file = "rpds_py-0.17.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:8ffe53e1d8ef2520ebcf0c9fec15bb721da59e8ef283b6ff3079613b1e30513d"}, + {file = "rpds_py-0.17.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4341bd7579611cf50e7b20bb8c2e23512a3dc79de987a1f411cb458ab670eb90"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4eb548daf4836e3b2c662033bfbfc551db58d30fd8fe660314f86bf8510b93"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b686f25377f9c006acbac63f61614416a6317133ab7fafe5de5f7dc8a06d42eb"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e21b76075c01d65d0f0f34302b5a7457d95721d5e0667aea65e5bb3ab415c25"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b86b21b348f7e5485fae740d845c65a880f5d1eda1e063bc59bef92d1f7d0c55"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f175e95a197f6a4059b50757a3dca33b32b61691bdbd22c29e8a8d21d3914cae"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1701fc54460ae2e5efc1dd6350eafd7a760f516df8dbe51d4a1c79d69472fbd4"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9051e3d2af8f55b42061603e29e744724cb5f65b128a491446cc029b3e2ea896"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7450dbd659fed6dd41d1a7d47ed767e893ba402af8ae664c157c255ec6067fde"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5a024fa96d541fd7edaa0e9d904601c6445e95a729a2900c5aec6555fe921ed6"}, + {file = "rpds_py-0.17.1-cp38-none-win32.whl", hash = "sha256:da1ead63368c04a9bded7904757dfcae01eba0e0f9bc41d3d7f57ebf1c04015a"}, + {file = "rpds_py-0.17.1-cp38-none-win_amd64.whl", hash = "sha256:841320e1841bb53fada91c9725e766bb25009cfd4144e92298db296fb6c894fb"}, + {file = "rpds_py-0.17.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f6c43b6f97209e370124baf2bf40bb1e8edc25311a158867eb1c3a5d449ebc7a"}, + {file = "rpds_py-0.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7d63ec01fe7c76c2dbb7e972fece45acbb8836e72682bde138e7e039906e2c"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81038ff87a4e04c22e1d81f947c6ac46f122e0c80460b9006e6517c4d842a6ec"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:810685321f4a304b2b55577c915bece4c4a06dfe38f6e62d9cc1d6ca8ee86b99"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25f071737dae674ca8937a73d0f43f5a52e92c2d178330b4c0bb6ab05586ffa6"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa5bfb13f1e89151ade0eb812f7b0d7a4d643406caaad65ce1cbabe0a66d695f"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfe07308b311a8293a0d5ef4e61411c5c20f682db6b5e73de6c7c8824272c256"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a000133a90eea274a6f28adc3084643263b1e7c1a5a66eb0a0a7a36aa757ed74"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d0e8a6434a3fbf77d11448c9c25b2f25244226cfbec1a5159947cac5b8c5fa4"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efa767c220d94aa4ac3a6dd3aeb986e9f229eaf5bce92d8b1b3018d06bed3772"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:dbc56680ecf585a384fbd93cd42bc82668b77cb525343170a2d86dafaed2a84b"}, + {file = "rpds_py-0.17.1-cp39-none-win32.whl", hash = "sha256:270987bc22e7e5a962b1094953ae901395e8c1e1e83ad016c5cfcfff75a15a3f"}, + {file = "rpds_py-0.17.1-cp39-none-win_amd64.whl", hash = "sha256:2a7b2f2f56a16a6d62e55354dd329d929560442bd92e87397b7a9586a32e3e76"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a3264e3e858de4fc601741498215835ff324ff2482fd4e4af61b46512dd7fc83"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f2f3b28b40fddcb6c1f1f6c88c6f3769cd933fa493ceb79da45968a21dccc920"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9584f8f52010295a4a417221861df9bea4c72d9632562b6e59b3c7b87a1522b7"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c64602e8be701c6cfe42064b71c84ce62ce66ddc6422c15463fd8127db3d8066"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060f412230d5f19fc8c8b75f315931b408d8ebf56aec33ef4168d1b9e54200b1"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9412abdf0ba70faa6e2ee6c0cc62a8defb772e78860cef419865917d86c7342"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9737bdaa0ad33d34c0efc718741abaafce62fadae72c8b251df9b0c823c63b22"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9f0e4dc0f17dcea4ab9d13ac5c666b6b5337042b4d8f27e01b70fae41dd65c57"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1db228102ab9d1ff4c64148c96320d0be7044fa28bd865a9ce628ce98da5973d"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d8bbd8e56f3ba25a7d0cf980fc42b34028848a53a0e36c9918550e0280b9d0b6"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:be22ae34d68544df293152b7e50895ba70d2a833ad9566932d750d3625918b82"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bf046179d011e6114daf12a534d874958b039342b347348a78b7cdf0dd9d6041"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a746a6d49665058a5896000e8d9d2f1a6acba8a03b389c1e4c06e11e0b7f40d"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0b8bf5b8db49d8fd40f54772a1dcf262e8be0ad2ab0206b5a2ec109c176c0a4"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7f4cb1f173385e8a39c29510dd11a78bf44e360fb75610594973f5ea141028b"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fbd70cb8b54fe745301921b0816c08b6d917593429dfc437fd024b5ba713c58"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bdf1303df671179eaf2cb41e8515a07fc78d9d00f111eadbe3e14262f59c3d0"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad059a4bd14c45776600d223ec194e77db6c20255578bb5bcdd7c18fd169361"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3664d126d3388a887db44c2e293f87d500c4184ec43d5d14d2d2babdb4c64cad"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:698ea95a60c8b16b58be9d854c9f993c639f5c214cf9ba782eca53a8789d6b19"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:c3d2010656999b63e628a3c694f23020322b4178c450dc478558a2b6ef3cb9bb"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:938eab7323a736533f015e6069a7d53ef2dcc841e4e533b782c2bfb9fb12d84b"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e626b365293a2142a62b9a614e1f8e331b28f3ca57b9f05ebbf4cf2a0f0bdc5"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:380e0df2e9d5d5d339803cfc6d183a5442ad7ab3c63c2a0982e8c824566c5ccc"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b760a56e080a826c2e5af09002c1a037382ed21d03134eb6294812dda268c811"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5576ee2f3a309d2bb403ec292d5958ce03953b0e57a11d224c1f134feaf8c40f"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3c3461ebb4c4f1bbc70b15d20b565759f97a5aaf13af811fcefc892e9197ba"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:637b802f3f069a64436d432117a7e58fab414b4e27a7e81049817ae94de45d8d"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffee088ea9b593cc6160518ba9bd319b5475e5f3e578e4552d63818773c6f56a"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ac732390d529d8469b831949c78085b034bff67f584559340008d0f6041a049"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:93432e747fb07fa567ad9cc7aaadd6e29710e515aabf939dfbed8046041346c6"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b7d9ca34542099b4e185b3c2a2b2eda2e318a7dbde0b0d83357a6d4421b5296"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:0387ce69ba06e43df54e43968090f3626e231e4bc9150e4c3246947567695f68"}, + {file = "rpds_py-0.17.1.tar.gz", hash = "sha256:0210b2668f24c078307260bf88bdac9d6f1093635df5123789bfee4d8d7fc8e7"}, ] [[package]] @@ -2894,13 +2918,13 @@ files = [ [[package]] name = "types-jsonschema" -version = "4.20.0.0" +version = "4.21.0.20240118" description = "Typing stubs for jsonschema" optional = false python-versions = ">=3.8" files = [ - {file = "types-jsonschema-4.20.0.0.tar.gz", hash = "sha256:0de1032d243f1d3dba8b745ad84efe8c1af71665a9deb1827636ac535dcb79c1"}, - {file = "types_jsonschema-4.20.0.0-py3-none-any.whl", hash = "sha256:e6d5df18aaca4412f0aae246a294761a92040e93d7bc840f002b7329a8b72d26"}, + {file = "types-jsonschema-4.21.0.20240118.tar.gz", hash = "sha256:31aae1b5adc0176c1155c2d4f58348b22d92ae64315e9cc83bd6902168839232"}, + {file = "types_jsonschema-4.21.0.20240118-py3-none-any.whl", hash = "sha256:77a4ac36b0be4f24274d5b9bf0b66208ee771c05f80e34c4641de7d63e8a872d"}, ] [package.dependencies] @@ -3077,4 +3101,4 @@ testing = ["pytest", "pytest-durations"] [metadata] lock-version = "2.0" python-versions = ">=3.7.1" -content-hash = "d3572f44e47bda6b6b5175f3ab3dea2a8cd21964adfe3640cdb81c2089c48e5b" +content-hash = "1946758d59e09b15f9a72956ca9621426ba92f1637abfc44a08b83b83857c37a" diff --git a/pyproject.toml b/pyproject.toml index 8c98c173e..c9ce1138e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,6 @@ pendulum = [ PyJWT = "~=2.4" python-dateutil = ">=2.8.2" python-dotenv = ">=0.20" -pytz = ">=2022.2.1" PyYAML = ">=6.0" requests = ">=2.25.1" simpleeval = ">=0.9.13" @@ -127,9 +126,8 @@ coverage = [ {extras = ["toml"], version = ">=7.4", python = ">=3.12"}, ] -# TODO: Remove the Python 3.12 marker when DuckDB supports it -duckdb = { version = ">=0.8.0", python = "<3.12" } -duckdb-engine = { version = ">=0.9.4", python = "<3.12" } +duckdb = { version = ">=0.9.3.dev2490", allow-prereleases = true } +duckdb-engine = { version = ">=0.9.4" } mypy = [ { version = ">=1.0,<1.5", python = "<3.8" }, @@ -138,7 +136,9 @@ mypy = [ pytest-benchmark = ">=4.0.0" pytest-httpserver = { version = ">=1.0.6", python = "<4" } pytest-snapshot = ">=0.9.0" +pytz = ">=2022.2.1" requests-mock = ">=1.10.0" +rfc3339-validator = ">=0.1.4" time-machine = [ { version = ">=2.10.0,<2.11", python = "<3.8" }, { version = ">=2.10.0", python = ">=3.8" }, @@ -215,7 +215,6 @@ fail_under = 82 [tool.mypy] exclude = "tests" files = "singer_sdk" -python_version = "3.8" warn_redundant_casts = true warn_return_any = true warn_unused_configs = true diff --git a/samples/sample_mapper/__main__.py b/samples/sample_mapper/__main__.py new file mode 100644 index 000000000..594c58871 --- /dev/null +++ b/samples/sample_mapper/__main__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from samples.sample_mapper.mapper import StreamTransform + +StreamTransform.cli() diff --git a/samples/sample_tap_bigquery/__main__.py b/samples/sample_tap_bigquery/__main__.py new file mode 100644 index 000000000..04945c5db --- /dev/null +++ b/samples/sample_tap_bigquery/__main__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from samples.sample_tap_bigquery import TapBigQuery + +TapBigQuery.cli() diff --git a/samples/sample_tap_gitlab/__main__.py b/samples/sample_tap_gitlab/__main__.py new file mode 100644 index 000000000..236b22a6a --- /dev/null +++ b/samples/sample_tap_gitlab/__main__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from sample_tap_gitlab.gitlab_tap import SampleTapGitlab + +SampleTapGitlab.cli() diff --git a/samples/sample_tap_gitlab/gitlab_rest_streams.py b/samples/sample_tap_gitlab/gitlab_rest_streams.py index 1db629099..0d5968132 100644 --- a/samples/sample_tap_gitlab/gitlab_rest_streams.py +++ b/samples/sample_tap_gitlab/gitlab_rest_streams.py @@ -11,6 +11,7 @@ from singer_sdk.typing import ( ArrayType, DateTimeType, + DateType, IntegerType, PropertiesList, Property, @@ -158,9 +159,9 @@ class EpicsStream(ProjectBasedStream): Property("description", StringType), Property("state", StringType), Property("author_id", IntegerType), - Property("start_date", DateTimeType), - Property("end_date", DateTimeType), - Property("due_date", DateTimeType), + Property("start_date", DateType), + Property("end_date", DateType), + Property("due_date", DateType), Property("created_at", DateTimeType), Property("updated_at", DateTimeType), Property("labels", ArrayType(StringType)), diff --git a/samples/sample_tap_gitlab/schemas/epic_issues.json b/samples/sample_tap_gitlab/schemas/epic_issues.json index 2330bb427..36b4e14a5 100644 --- a/samples/sample_tap_gitlab/schemas/epic_issues.json +++ b/samples/sample_tap_gitlab/schemas/epic_issues.json @@ -122,7 +122,7 @@ }, "due_date": { "type": ["string", "null"], - "format": "date-time" + "format": "date" }, "epic_issue_id": { "type": "integer" @@ -150,7 +150,7 @@ }, "due_date": { "type": ["string", "null"], - "format": "date-time" + "format": "date" }, "id": { "type": "integer" @@ -163,7 +163,7 @@ }, "start_date": { "type": ["string", "null"], - "format": "date-time" + "format": "date" }, "state": { "type": "string" diff --git a/samples/sample_tap_google_analytics/__main__.py b/samples/sample_tap_google_analytics/__main__.py new file mode 100644 index 000000000..e274833e6 --- /dev/null +++ b/samples/sample_tap_google_analytics/__main__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from samples.sample_tap_google_analytics.ga_tap import SampleTapGoogleAnalytics + +SampleTapGoogleAnalytics.cli() diff --git a/samples/sample_tap_hostile/__main__.py b/samples/sample_tap_hostile/__main__.py new file mode 100644 index 000000000..83b94e0e1 --- /dev/null +++ b/samples/sample_tap_hostile/__main__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from samples.sample_tap_hostile import SampleTapHostile + +SampleTapHostile.cli() diff --git a/samples/sample_tap_sqlite/__main__.py b/samples/sample_tap_sqlite/__main__.py new file mode 100644 index 000000000..e758e688d --- /dev/null +++ b/samples/sample_tap_sqlite/__main__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from samples.sample_tap_sqlite import SQLiteTap + +SQLiteTap.cli() diff --git a/samples/sample_target_parquet/__main__.py b/samples/sample_target_parquet/__main__.py new file mode 100644 index 000000000..ee89a2954 --- /dev/null +++ b/samples/sample_target_parquet/__main__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from sample_target_parquet.parquet_target import SampleTargetParquet + +SampleTargetParquet.cli() diff --git a/singer_sdk/_singerlib/messages.py b/singer_sdk/_singerlib/messages.py index cbea58527..6a9fc24ef 100644 --- a/singer_sdk/_singerlib/messages.py +++ b/singer_sdk/_singerlib/messages.py @@ -54,7 +54,7 @@ def exclude_null_dict(pairs: list[tuple[str, t.Any]]) -> dict[str, t.Any]: class Message: """Singer base message.""" - type: SingerMessageType = field(init=False) # noqa: A003 + type: SingerMessageType = field(init=False) """The message type.""" def to_dict(self) -> dict[str, t.Any]: diff --git a/singer_sdk/_singerlib/schema.py b/singer_sdk/_singerlib/schema.py index 41dd8104b..1be527a97 100644 --- a/singer_sdk/_singerlib/schema.py +++ b/singer_sdk/_singerlib/schema.py @@ -47,7 +47,7 @@ class Schema: This is because we wanted to expand it with extra STANDARD_KEYS. """ - type: str | list[str] | None = None # noqa: A003 + type: str | list[str] | None = None default: t.Any | None = None properties: dict | None = None items: t.Any | None = None @@ -60,7 +60,7 @@ class Schema: maxLength: int | None = None # noqa: N815 minLength: int | None = None # noqa: N815 anyOf: t.Any | None = None # noqa: N815 - format: str | None = None # noqa: A003 + format: str | None = None additionalProperties: t.Any | None = None # noqa: N815 patternProperties: t.Any | None = None # noqa: N815 required: list[str] | None = None diff --git a/singer_sdk/_singerlib/utils.py b/singer_sdk/_singerlib/utils.py index c86b0f37f..7022ee86f 100644 --- a/singer_sdk/_singerlib/utils.py +++ b/singer_sdk/_singerlib/utils.py @@ -1,9 +1,7 @@ from __future__ import annotations import sys -from datetime import datetime, timedelta - -import pytz +from datetime import datetime, timedelta, timezone if sys.version_info < (3, 11): from backports.datetime_fromisoformat import MonkeyPatch @@ -33,9 +31,9 @@ def strptime_to_utc(dtimestr: str) -> datetime: """ d_object: datetime = datetime.fromisoformat(dtimestr) if d_object.tzinfo is None: - return d_object.replace(tzinfo=pytz.UTC) + return d_object.replace(tzinfo=timezone.utc) - return d_object.astimezone(tz=pytz.UTC) + return d_object.astimezone(tz=timezone.utc) def strftime(dtime: datetime, format_str: str = DATETIME_FMT) -> str: diff --git a/singer_sdk/exceptions.py b/singer_sdk/exceptions.py index 75135e800..03b06a7c0 100644 --- a/singer_sdk/exceptions.py +++ b/singer_sdk/exceptions.py @@ -138,3 +138,22 @@ class ConformedNameClashException(Exception): class MissingKeyPropertiesError(Exception): """Raised when a recieved (and/or transformed) record is missing key properties.""" + + +class InvalidJSONSchema(Exception): + """Raised when a JSON schema is invalid.""" + + +class InvalidRecord(Exception): + """Raised when a stream record is invalid according to its declared schema.""" + + def __init__(self, error_message: str, record: dict) -> None: + """Initialize an InvalidRecord exception. + + Args: + error_message: A message describing the error. + record: The invalid record. + """ + super().__init__(f"Record Message Validation Error: {error_message}") + self.error_message = error_message + self.record = record diff --git a/singer_sdk/helpers/_batch.py b/singer_sdk/helpers/_batch.py index 490e2ef8b..b57002e1d 100644 --- a/singer_sdk/helpers/_batch.py +++ b/singer_sdk/helpers/_batch.py @@ -37,7 +37,7 @@ class BaseBatchFileEncoding: __encoding_format__: t.ClassVar[str] = "OVERRIDE_ME" # Base encoding fields - format: str = field(init=False) # noqa: A003 + format: str = field(init=False) """The format of the batch file.""" compression: str | None = None @@ -188,7 +188,7 @@ def fs(self, **kwargs: t.Any) -> t.Generator[FS, None, None]: filesystem.close() @contextmanager - def open( # noqa: A003 + def open( self, filename: str, mode: str = "rb", diff --git a/singer_sdk/helpers/_resources.py b/singer_sdk/helpers/_resources.py index 39fe9c592..89b3ed269 100644 --- a/singer_sdk/helpers/_resources.py +++ b/singer_sdk/helpers/_resources.py @@ -6,12 +6,12 @@ if t.TYPE_CHECKING: from types import ModuleType + from singer_sdk.helpers._compat import Traversable + if sys.version_info < (3, 9): import importlib_resources - from importlib_resources.abc import Traversable else: import importlib.resources as importlib_resources - from importlib.abc import Traversable def get_package_files(package: str | ModuleType) -> Traversable: @@ -24,4 +24,4 @@ def get_package_files(package: str | ModuleType) -> Traversable: Returns: The file as a Traversable object. """ - return t.cast(Traversable, importlib_resources.files(package)) + return importlib_resources.files(package) diff --git a/singer_sdk/metrics.py b/singer_sdk/metrics.py index e4191eadf..990285ae0 100644 --- a/singer_sdk/metrics.py +++ b/singer_sdk/metrics.py @@ -15,11 +15,13 @@ import yaml -from singer_sdk.helpers._resources import Traversable, get_package_files +from singer_sdk.helpers._resources import get_package_files if t.TYPE_CHECKING: from types import TracebackType + from singer_sdk.helpers._compat import Traversable + DEFAULT_LOG_INTERVAL = 60.0 METRICS_LOGGER_NAME = __name__ METRICS_LOG_LEVEL_SETTING = "metrics_log_level" diff --git a/singer_sdk/sinks/core.py b/singer_sdk/sinks/core.py index f1a7c0f92..a85a1651c 100644 --- a/singer_sdk/sinks/core.py +++ b/singer_sdk/sinks/core.py @@ -13,9 +13,14 @@ from gzip import open as gzip_open from types import MappingProxyType -from jsonschema import Draft7Validator +import jsonschema +from typing_extensions import override -from singer_sdk.exceptions import MissingKeyPropertiesError +from singer_sdk.exceptions import ( + InvalidJSONSchema, + InvalidRecord, + MissingKeyPropertiesError, +) from singer_sdk.helpers._batch import ( BaseBatchFileEncoding, BatchConfig, @@ -39,7 +44,83 @@ from singer_sdk.target_base import Target -JSONSchemaValidator = Draft7Validator + +class BaseJSONSchemaValidator(abc.ABC): + """Abstract base class for JSONSchema validator.""" + + def __init__(self, schema: dict[str, t.Any]) -> None: + """Initialize the record validator. + + Args: + schema: Schema of the stream to sink. + """ + self.schema = schema + + @abc.abstractmethod + def validate(self, record: dict[str, t.Any]) -> None: + """Validate a record message. + + This method MUST raise an ``InvalidRecord`` exception if the record is invalid. + + Args: + record: Record message to validate. + """ + + +class JSONSchemaValidator(BaseJSONSchemaValidator): + """Validate records using the ``fastjsonschema`` library.""" + + def __init__( + self, + schema: dict, + *, + validate_formats: bool = False, + format_checker: jsonschema.FormatChecker | None = None, + ): + """Initialize the validator. + + Args: + schema: Schema of the stream to sink. + validate_formats: Whether JSON string formats (e.g. ``date-time``) should + be validated. + format_checker: User-defined format checker. + + Raises: + InvalidJSONSchema: If the schema provided from tap or mapper is invalid. + """ + jsonschema_validator = jsonschema.Draft7Validator + + super().__init__(schema) + if validate_formats: + format_checker = format_checker or jsonschema_validator.FORMAT_CHECKER + else: + format_checker = jsonschema.FormatChecker(formats=()) + + try: + jsonschema_validator.check_schema(schema) + except jsonschema.SchemaError as e: + error_message = f"Schema Validation Error: {e}" + raise InvalidJSONSchema(error_message) from e + + self.validator = jsonschema_validator( + schema=schema, + format_checker=format_checker, + ) + + @override + def validate(self, record: dict): # noqa: ANN201 + """Validate a record message. + + Args: + record: Record message to validate. + + Raises: + InvalidRecord: If the record is invalid. + """ + try: + self.validator.validate(record) + except jsonschema.ValidationError as e: + raise InvalidRecord(e.message, record) from e class Sink(metaclass=abc.ABCMeta): @@ -51,6 +132,15 @@ class Sink(metaclass=abc.ABCMeta): MAX_SIZE_DEFAULT = 10000 + validate_schema = True + """Enable JSON schema record validation.""" + + validate_field_string_format = False + """Enable JSON schema format validation, for example `date-time` string fields.""" + + fail_on_record_validation_exception: bool = True + """Interrupt the target execution when a record fails schema validation.""" + def __init__( self, target: Target, @@ -95,10 +185,23 @@ def __init__( self._batch_records_read: int = 0 self._batch_dupe_records_merged: int = 0 - self._validator = Draft7Validator( - schema, - format_checker=Draft7Validator.FORMAT_CHECKER, - ) + self._validator: BaseJSONSchemaValidator | None = self.get_validator() + + def get_validator(self) -> BaseJSONSchemaValidator | None: + """Get a record validator for this sink. + + Override this method to use a custom format validator, or disable record + validation by returning `None`. + + Returns: + An instance of a subclass of ``BaseJSONSchemaValidator``. + """ + if self.validate_schema: + return JSONSchemaValidator( + self.schema, + validate_formats=self.validate_field_string_format, + ) + return None def _get_context(self, record: dict) -> dict: # noqa: ARG002 """Return an empty dictionary by default. @@ -328,8 +431,20 @@ def _validate_and_parse(self, record: dict) -> dict: Returns: TODO + + Raises: + InvalidRecord: If the record is invalid. """ - self._validator.validate(record) + if self._validator is not None: + # TODO: Check the performance impact of this try/except block. It runs + # on every record, so it's probably bad and should be moved up the stack. + try: + self._validator.validate(record) + except InvalidRecord as e: + if self.fail_on_record_validation_exception: + raise + self.logger.exception("Record validation failed %s", e) + self._parse_timestamps_in_record( record=record, schema=self.schema, diff --git a/singer_sdk/streams/core.py b/singer_sdk/streams/core.py index 459b9e761..567d7284d 100644 --- a/singer_sdk/streams/core.py +++ b/singer_sdk/streams/core.py @@ -1026,17 +1026,18 @@ def _process_record( partition_context: The partition context. """ partition_context = partition_context or {} - child_context = copy.copy( - self.get_child_context(record=record, context=child_context), - ) for key, val in partition_context.items(): # Add state context to records if not already present if key not in record: record[key] = val - # Sync children, except when primary mapper filters out the record - if self.stream_maps[0].get_filter_result(record): - self._sync_children(child_context) + for context in self.generate_child_contexts( + record=record, + context=child_context, + ): + # Sync children, except when primary mapper filters out the record + if self.stream_maps[0].get_filter_result(record): + self._sync_children(copy.copy(context)) def _sync_records( # noqa: C901 self, @@ -1289,6 +1290,22 @@ def get_child_context(self, record: dict, context: dict | None) -> dict | None: return context or record + def generate_child_contexts( + self, + record: dict, + context: dict | None, + ) -> t.Iterable[dict | None]: + """Generate child contexts. + + Args: + record: Individual record in the stream. + context: Stream partition or context dictionary. + + Yields: + A child context for each child stream. + """ + yield self.get_child_context(record=record, context=context) + # Abstract Methods @abc.abstractmethod diff --git a/singer_sdk/testing/templates.py b/singer_sdk/testing/templates.py index 4a16feb05..5395e5520 100644 --- a/singer_sdk/testing/templates.py +++ b/singer_sdk/testing/templates.py @@ -38,7 +38,7 @@ class TestTemplate: plugin_type: str | None = None @property - def id(self) -> str: # noqa: A003 + def id(self) -> str: """Test ID. Raises: @@ -130,7 +130,7 @@ class TapTestTemplate(TestTemplate): plugin_type = "tap" @property - def id(self) -> str: # noqa: A003 + def id(self) -> str: """Test ID. Returns: @@ -162,7 +162,7 @@ class StreamTestTemplate(TestTemplate): required_kwargs: t.ClassVar[list[str]] = ["stream"] @property - def id(self) -> str: # noqa: A003 + def id(self) -> str: """Test ID. Returns: @@ -196,7 +196,7 @@ class AttributeTestTemplate(TestTemplate): plugin_type = "attribute" @property - def id(self) -> str: # noqa: A003 + def id(self) -> str: """Test ID. Returns: @@ -292,7 +292,7 @@ def run( # type: ignore[override] super().run(config, resource, runner) @property - def id(self) -> str: # noqa: A003 + def id(self) -> str: """Test ID. Returns: @@ -337,4 +337,4 @@ def singer_filepath(self) -> Traversable: Returns: The expected Path to this tests singer file. """ - return importlib_resources.files(target_test_streams) / f"{self.name}.singer" # type: ignore[no-any-return] + return importlib_resources.files(target_test_streams) / f"{self.name}.singer" diff --git a/tests/_singerlib/test_utils.py b/tests/_singerlib/test_utils.py index f48b9684b..c0c040280 100644 --- a/tests/_singerlib/test_utils.py +++ b/tests/_singerlib/test_utils.py @@ -1,6 +1,6 @@ from __future__ import annotations -from datetime import datetime +from datetime import datetime, timezone import pytest import pytz @@ -30,11 +30,12 @@ def test_round_trip(): "2021-01-01T00:00:00.000000+00:00", "2021-01-01T00:00:00.000000+06:00", "2021-01-01T00:00:00.000000-04:00", + "2021-01-01T00:00:00.000000", ], - ids=["Z", "offset+0", "offset+6", "offset-4"], + ids=["Z", "offset+0", "offset+6", "offset-4", "no offset"], ) def test_strptime_to_utc(dtimestr): - assert strptime_to_utc(dtimestr).tzinfo == pytz.UTC + assert strptime_to_utc(dtimestr).tzinfo == timezone.utc def test_stftime_non_utc(): diff --git a/tests/core/connectors/test_sql_connector.py b/tests/core/connectors/test_sql_connector.py index 49fa2072f..f9c6557dd 100644 --- a/tests/core/connectors/test_sql_connector.py +++ b/tests/core/connectors/test_sql_connector.py @@ -1,6 +1,5 @@ from __future__ import annotations -import sys import typing as t from decimal import Decimal from unittest import mock @@ -8,7 +7,6 @@ import pytest import sqlalchemy as sa from sqlalchemy.dialects import registry, sqlite -from sqlalchemy.exc import NoSuchModuleError from singer_sdk.connectors import SQLConnector from singer_sdk.exceptions import ConfigValidationError @@ -312,11 +310,6 @@ def get_column_alter_ddl( ) -@pytest.mark.xfail( - reason="DuckDB does not build on Python 3.12 yet", - condition=sys.version_info >= (3, 12), - raises=NoSuchModuleError, -) class TestDuckDBConnector: @pytest.fixture def connector(self): diff --git a/tests/core/sinks/test_type_checker.py b/tests/core/sinks/test_type_checker.py new file mode 100644 index 000000000..28376d5d9 --- /dev/null +++ b/tests/core/sinks/test_type_checker.py @@ -0,0 +1,100 @@ +"""Test the custom type validator.""" + +from __future__ import annotations + +import pytest +from typing_extensions import override + +from singer_sdk.sinks.core import BaseJSONSchemaValidator, InvalidJSONSchema, Sink +from singer_sdk.target_base import Target + + +@pytest.fixture +def test_schema_invalid(): + """Return a test schema with an invalid type.""" + + return { + "type": "object", + "properties": { + "datetime_col": {"type": "ssttrriinngg", "format": "date-time"}, + }, + } + + +@pytest.fixture +def target(): + """Return a target object.""" + + class CustomTarget(Target): + name = "test_target" + + return CustomTarget() + + +def test_default_schema_type_checks(target, test_schema_invalid): + """Test type checks on _validator initialization.""" + + class CustomSink(Sink): + """Custom sink class.""" + + @override + def process_batch(self, context: dict) -> None: + pass + + @override + def process_record(self, record: dict, context: dict) -> None: + pass + + with pytest.raises( + InvalidJSONSchema, + match=r"Schema Validation Error: 'ssttrriinngg' is not valid under any", + ): + CustomSink(target, "test_stream", test_schema_invalid, None) + + +def test_disable_schema_type_checks_returning_none(target, test_schema_invalid): + """Test type checks on _validator initialization.""" + + class CustomSink(Sink): + """Custom sink class.""" + + @override + def get_validator(self) -> BaseJSONSchemaValidator | None: + """Get a record validator for this sink. + + Override this method to use a custom format validator + or disable jsonschema validator, by returning `None`. + + Returns: + An instance of a subclass of ``BaseJSONSchemaValidator``. + """ + return None + + @override + def process_batch(self, context: dict) -> None: + pass + + @override + def process_record(self, record: dict, context: dict) -> None: + pass + + CustomSink(target, "test_stream", test_schema_invalid, None) + + +def test_disable_schema_type_checks_setting_false(target, test_schema_invalid): + """Test type checks on _validator initialization.""" + + class CustomSink(Sink): + """Custom sink class.""" + + validate_schema = False + + @override + def process_batch(self, context: dict) -> None: + pass + + @override + def process_record(self, record: dict, context: dict) -> None: + pass + + CustomSink(target, "test_stream", test_schema_invalid, None) diff --git a/tests/core/sinks/test_validation.py b/tests/core/sinks/test_validation.py index 0672c9f49..e583fddad 100644 --- a/tests/core/sinks/test_validation.py +++ b/tests/core/sinks/test_validation.py @@ -5,6 +5,7 @@ import pytest +from singer_sdk.exceptions import InvalidRecord from tests.conftest import BatchSinkMock, TargetMock @@ -58,6 +59,120 @@ def test_validate_record(): assert updated_record["invalid_datetime"] == "9999-12-31 23:59:59.999999" +@pytest.fixture +def draft7_sink_stop(): + """Return a sink object with Draft7 checks enabled.""" + + class CustomSink(BatchSinkMock): + """Custom sink class.""" + + validate_field_string_format = True + + return CustomSink( + TargetMock(), + "users", + { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "created_at": {"type": "string", "format": "date-time"}, + "created_at_date": {"type": "string", "format": "date"}, + "created_at_time": {"type": "string", "format": "time"}, + "invalid_datetime": {"type": "string", "format": "date-time"}, + }, + }, + ["id"], + ) + + +@pytest.fixture +def draft7_sink_continue(): + """Return a sink object with Draft7 checks enabled.""" + + class CustomSink(BatchSinkMock): + """Custom sink class.""" + + validate_field_string_format = True + fail_on_record_validation_exception = False + + return CustomSink( + TargetMock(), + "users", + { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "created_at": {"type": "string", "format": "date-time"}, + "created_at_date": {"type": "string", "format": "date"}, + "created_at_time": {"type": "string", "format": "time"}, + "invalid_datetime": {"type": "string", "format": "date-time"}, + }, + }, + ["id"], + ) + + +def test_validate_record_jsonschema_format_checking_enabled_stop_on_error( + draft7_sink_stop, +): + sink: BatchSinkMock = draft7_sink_stop + + record = { + "id": 1, + "created_at": "2021-01-01T00:00:00+00:00", + "created_at_date": "2021-01-01", + "created_at_time": "00:01:00+00:00", + "missing_datetime": "2021-01-01T00:00:00+00:00", + "invalid_datetime": "not a datetime", + } + with pytest.raises( + InvalidRecord, + match=r"Record Message Validation Error", + ): + sink._validate_and_parse(record) + + +def test_validate_record_jsonschema_format_checking_enabled_continue_on_error( + capsys: pytest.CaptureFixture, + draft7_sink_continue, +): + sink: BatchSinkMock = draft7_sink_continue + + record = { + "id": 1, + "created_at": "2021-01-01T00:00:00+00:00", + "created_at_date": "2021-01-01", + "created_at_time": "00:01:00+00:00", + "missing_datetime": "2021-01-01T00:00:00+00:00", + "invalid_datetime": "not a datetime", + } + + updated_record = sink._validate_and_parse(record) + captured = capsys.readouterr() + + assert updated_record["created_at"] == datetime.datetime( + 2021, + 1, + 1, + 0, + 0, + tzinfo=datetime.timezone.utc, + ) + assert updated_record["created_at_date"] == datetime.date( + 2021, + 1, + 1, + ) + assert updated_record["created_at_time"] == datetime.time( + 0, + 1, + tzinfo=datetime.timezone.utc, + ) + assert updated_record["missing_datetime"] == "2021-01-01T00:00:00+00:00" + assert updated_record["invalid_datetime"] == "9999-12-31 23:59:59.999999" + assert "Record Message Validation Error" in captured.err + + @pytest.fixture def bench_sink() -> BatchSinkMock: target = TargetMock() diff --git a/tests/core/test_parent_child.py b/tests/core/test_parent_child.py index 7fd01a153..8b9ad2a16 100644 --- a/tests/core/test_parent_child.py +++ b/tests/core/test_parent_child.py @@ -167,3 +167,100 @@ def test_child_deselected_parent(tap_with_deselected_parent: MyTap): assert all(msg["type"] == SingerMessageType.RECORD for msg in child_record_messages) assert all(msg["stream"] == child_stream.name for msg in child_record_messages) assert all("pid" in msg["record"] for msg in child_record_messages) + + +def test_one_parent_many_children(tap: MyTap): + """Test tap output with parent stream deselected.""" + + class ParentMany(Stream): + """A parent stream.""" + + name = "parent_many" + schema: t.ClassVar[dict] = { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "children": {"type": "array", "items": {"type": "integer"}}, + }, + } + + def get_records( + self, + context: dict | None, # noqa: ARG002 + ) -> t.Iterable[dict | tuple[dict, dict | None]]: + yield {"id": "1", "children": [1, 2, 3]} + + def generate_child_contexts( + self, + record: dict, + context: dict | None, # noqa: ARG002 + ) -> t.Iterable[dict | None]: + for child_id in record["children"]: + yield {"child_id": child_id, "pid": record["id"]} + + class ChildMany(Stream): + """A child stream.""" + + name = "child_many" + schema: t.ClassVar[dict] = { + "type": "object", + "properties": { + "id": {"type": "integer"}, + "pid": {"type": "integer"}, + }, + } + parent_stream_type = ParentMany + + def get_records(self, context: dict | None): + """Get dummy records.""" + yield { + "id": context["child_id"], + "composite_id": f"{context['pid']}-{context['child_id']}", + } + + class MyTapMany(Tap): + """A tap with streams having a parent-child relationship.""" + + name = "my-tap-many" + + def discover_streams(self): + """Discover streams.""" + return [ + ParentMany(self), + ChildMany(self), + ] + + tap = MyTapMany() + parent_stream = tap.streams["parent_many"] + child_stream = tap.streams["child_many"] + + messages = _get_messages(tap) + + # Parent schema is emitted + assert messages[1] + assert messages[1]["type"] == SingerMessageType.SCHEMA + assert messages[1]["stream"] == parent_stream.name + assert messages[1]["schema"] == parent_stream.schema + + # Child schemas are emitted + schema_messages = messages[2:9:3] + assert schema_messages + assert all(msg["type"] == SingerMessageType.SCHEMA for msg in schema_messages) + assert all(msg["stream"] == child_stream.name for msg in schema_messages) + assert all(msg["schema"] == child_stream.schema for msg in schema_messages) + + # Child records are emitted + child_record_messages = messages[3:10:3] + assert child_record_messages + assert all(msg["type"] == SingerMessageType.RECORD for msg in child_record_messages) + assert all(msg["stream"] == child_stream.name for msg in child_record_messages) + assert all("pid" in msg["record"] for msg in child_record_messages) + + # State messages are emitted + state_messages = messages[4:11:3] + assert state_messages + assert all(msg["type"] == SingerMessageType.STATE for msg in state_messages) + + # Parent record is emitted + assert messages[11] + assert messages[11]["type"] == SingerMessageType.RECORD