From b566a5b11392550753d39e475eaaf9cbdcf4d89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:02:45 -0600 Subject: [PATCH 01/20] chore: Bump docs dependencies (#2544) --- .readthedocs.yml | 2 +- noxfile.py | 2 +- poetry.lock | 116 +++++++++++++---------------------------------- pyproject.toml | 17 +++---- 4 files changed, 40 insertions(+), 97 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index decc69828..3b35af1b7 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -3,7 +3,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.12" sphinx: builder: html diff --git a/noxfile.py b/noxfile.py index 0b47fa639..d0d5fbd89 100644 --- a/noxfile.py +++ b/noxfile.py @@ -186,7 +186,7 @@ def docs_serve(session: Session) -> None: "build", "-W", ] - session.install(".[docs]") + session.install(".[docs]", "sphinx-autobuild") build_dir = Path("build") if build_dir.exists(): diff --git a/poetry.lock b/poetry.lock index 4cea6f749..92ea330cd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" +version = "0.7.16" +description = "A light, configurable Sphinx theme" optional = true -python-versions = ">=3.6" +python-versions = ">=3.9" files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] @@ -52,9 +52,6 @@ files = [ {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] -[package.dependencies] -pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} - [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] @@ -979,20 +976,6 @@ files = [ importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} referencing = ">=0.31.0" -[[package]] -name = "livereload" -version = "2.7.0" -description = "Python LiveReload is an awesome tool for web developers" -optional = true -python-versions = ">=3.7" -files = [ - {file = "livereload-2.7.0-py3-none-any.whl", hash = "sha256:19bee55aff51d5ade6ede0dc709189a0f904d3b906d3ea71641ed548acff3246"}, - {file = "livereload-2.7.0.tar.gz", hash = "sha256:f4ba199ef93248902841e298670eebfe1aa9e148e19b343bc57dbf1b74de0513"}, -] - -[package.dependencies] -tornado = "*" - [[package]] name = "markdown-it-py" version = "3.0.0" @@ -2092,57 +2075,39 @@ files = [ [[package]] name = "sphinx" -version = "7.1.2" +version = "7.4.5" description = "Python documentation generator" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, - {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, + {file = "sphinx-7.4.5-py3-none-any.whl", hash = "sha256:9f135d8c1d277db67be514be579c4c4a26c8c0e962219aaca5a721b04bd6d0d8"}, + {file = "sphinx-7.4.5.tar.gz", hash = "sha256:a4abe5385bf856df094c1e6cadf24a2351b12057be3670b99a12c05a01d209f5"}, ] [package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.18.1,<0.21" +alabaster = ">=0.7.14,<0.8.0" +babel = ">=2.13" +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} +docutils = ">=0.20,<0.22" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.13" -requests = ">=2.25.0" -snowballstemmer = ">=2.0" +importlib-metadata = {version = ">=6.0", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.1" +packaging = ">=23.0" +Pygments = ">=2.17" +requests = ">=2.30.0" +snowballstemmer = ">=2.2" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" +sphinxcontrib-serializinghtml = ">=1.1.9" +tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] -test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] - -[[package]] -name = "sphinx-autobuild" -version = "2021.3.14" -description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." -optional = true -python-versions = ">=3.6" -files = [ - {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, - {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, -] - -[package.dependencies] -colorama = "*" -livereload = "*" -sphinx = "*" - -[package.extras] -test = ["pytest", "pytest-cov"] +lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] [[package]] name = "sphinx-basic-ng" @@ -2305,17 +2270,18 @@ test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +version = "1.1.10" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = true -python-versions = ">=3.5" +python-versions = ">=3.9" files = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] @@ -2484,26 +2450,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "tornado" -version = "6.4.1" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -optional = true -python-versions = ">=3.8" -files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, -] - [[package]] name = "types-jsonschema" version = "4.23.0.20240712" @@ -2664,7 +2610,7 @@ doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linke test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [extras] -docs = ["furo", "myst-parser", "pytest", "sphinx", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinx-reredirects"] +docs = ["furo", "myst-parser", "pytest", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinx-reredirects"] faker = ["faker"] jwt = ["PyJWT", "cryptography"] parquet = ["numpy", "numpy", "pyarrow"] @@ -2674,4 +2620,4 @@ testing = ["pytest", "pytest-durations"] [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "82c4b9443a3fed513d597831da8a953b3b2989e4859895939a31db30959a19d9" +content-hash = "91c9f418561bf500a204fa69295a5bb8b66da5b01ea1b77c397d38cb3b749ac4" diff --git a/pyproject.toml b/pyproject.toml index 990fb97f0..dcf67b2e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,14 +65,13 @@ urllib3 = ">=1.26,<2" # Sphinx dependencies installed as optional 'docs' extras # https://github.com/readthedocs/readthedocs.org/issues/4912#issuecomment-664002569 -sphinx = {version = ">=4.5", optional = true} -furo = {version = ">=2022.12.7", optional = true} -sphinx-copybutton = {version = ">=0.3.1", optional = true} -myst-parser = {version = ">=1", optional = true} -sphinx-autobuild = {version = ">=2021.3.14", optional = true} -sphinx-inline-tabs = {version = ">=2023.4.21", optional = true} -sphinx-notfound-page = {version = ">=1.0.0", optional = true} -sphinx-reredirects = {version = ">=0.1.1", optional = true} +sphinx = {version = ">=7", python = ">=3.9", optional = true} +furo = {version = ">=2024.5.6", python = ">=3.9", optional = true} +sphinx-copybutton = {version = ">=0.5.2", python = ">=3.9", optional = true} +myst-parser = {version = ">=3", python = ">=3.9", optional = true} +sphinx-inline-tabs = {version = ">=2023.4.21", python = ">=3.9", optional = true} +sphinx-notfound-page = {version = ">=1.0.0", python = ">=3.9", optional = true} +sphinx-reredirects = {version = ">=0.1.5", python = ">=3.9", optional = true} # File storage dependencies installed as optional 'filesystem' extras fs-s3fs = {version = ">=1.1.1", optional = true} @@ -106,7 +105,6 @@ docs = [ "pytest", "sphinx", "sphinx-copybutton", - "sphinx-autobuild", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinx-reredirects", @@ -253,7 +251,6 @@ DEP002 = [ "furo", "myst-parser", "sphinx", - "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", From fe5a01b681b1460d9d7f19917947cad0ce26703a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Wed, 17 Jul 2024 20:15:26 -0600 Subject: [PATCH 02/20] feat: Developers can now customize the default logging configuration for their taps/targets by adding `default_logging.yml` to their package (#2432) * feat: Developers can now customize the default logging configuration for their taps/targets by overriding the `get_default_logging_config` method Related: * Closes https://github.com/meltano/sdk/issues/1373 * Use a standard location for the default logging config * Small refactor * Tweak docs language * Small refactor * Pass package name to get the logging config * Get module name programmatically * Address broken `importlib.resources` in Python 3.9 --- docs/implementation/logging.md | 18 ++++++++++++++++++ poetry.lock | 2 +- pyproject.toml | 2 +- singer_sdk/helpers/_resources.py | 2 +- singer_sdk/metrics.py | 16 +++++++++++----- singer_sdk/plugin_base.py | 5 ++++- 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/docs/implementation/logging.md b/docs/implementation/logging.md index da20a5780..adf6f2182 100644 --- a/docs/implementation/logging.md +++ b/docs/implementation/logging.md @@ -77,3 +77,21 @@ This will send metrics to a `metrics.log`: 2022-09-29 00:48:53,302 INFO METRIC: {"metric_type": "timer", "metric": "sync_duration", "value": 0.5258760452270508, "tags": {"stream": "countries", "context": {}, "status": "succeeded"}} 2022-09-29 00:48:53,303 INFO METRIC: {"metric_type": "counter", "metric": "record_count", "value": 250, "tags": {"stream": "countries", "context": {}}} ``` + +## For package developers + +If you're developing a tap or target package and would like to customize its logging configuration, you can put a `default_loggging.yml` file in the package root to set the default logging configuration for your package. This file will be used if the `SINGER_SDK_LOG_CONFIG` environment variable is not set: + +``` +. +├── README.md +├── poetry.lock +├── pyproject.toml +└── tap_example +    ├── __init__.py +    ├── __main__.py +    ├── default_logging.yml # <-- This file will be used if SINGER_SDK_LOG_CONFIG is not set +    ├── client.py +    ├── streams.py +    └── tap.py +``` diff --git a/poetry.lock b/poetry.lock index 92ea330cd..73a4db60d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2620,4 +2620,4 @@ testing = ["pytest", "pytest-durations"] [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "91c9f418561bf500a204fa69295a5bb8b66da5b01ea1b77c397d38cb3b749ac4" +content-hash = "6a6acd1298eca878a1a7e0dc47decdb156fdc8eb6622dbba1e6b5422e190cecd" diff --git a/pyproject.toml b/pyproject.toml index dcf67b2e8..eae7ecd64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ backports-datetime-fromisoformat = { version = ">=2.0.1", python = "<3.11" } click = "~=8.0" fs = ">=2.4.16" importlib-metadata = {version = "<9.0.0", python = "<3.12"} -importlib-resources = {version = ">=5.12.0,!=6.2.0,!=6.3.0,!=6.3.1", python = "<3.9"} +importlib-resources = {version = ">=5.12.0,!=6.2.0,!=6.3.0,!=6.3.1", python = "<3.10"} inflection = ">=0.5.1" joblib = ">=1.3.0" jsonpath-ng = ">=1.5.3" diff --git a/singer_sdk/helpers/_resources.py b/singer_sdk/helpers/_resources.py index 89b3ed269..02f7b30ff 100644 --- a/singer_sdk/helpers/_resources.py +++ b/singer_sdk/helpers/_resources.py @@ -8,7 +8,7 @@ from singer_sdk.helpers._compat import Traversable -if sys.version_info < (3, 9): +if sys.version_info < (3, 10): import importlib_resources else: import importlib.resources as importlib_resources diff --git a/singer_sdk/metrics.py b/singer_sdk/metrics.py index 8a7efe51e..50d7d3926 100644 --- a/singer_sdk/metrics.py +++ b/singer_sdk/metrics.py @@ -394,23 +394,29 @@ def _load_yaml_logging_config(path: Traversable | Path) -> t.Any: # noqa: ANN40 return yaml.safe_load(f) -def _get_default_config() -> t.Any: # noqa: ANN401 +def _get_default_config_path(package: str) -> Traversable: """Get a logging configuration. + Args: + package: The package name to get the logging configuration for. + Returns: A logging configuration. """ - log_config_path = get_package_files("singer_sdk").joinpath("default_logging.yml") - return _load_yaml_logging_config(log_config_path) + filename = "default_logging.yml" + path = get_package_files(package) / filename + return path if path.is_file() else get_package_files("singer_sdk") / filename -def _setup_logging(config: t.Mapping[str, t.Any]) -> None: +def _setup_logging(config: t.Mapping[str, t.Any], *, package: str) -> None: """Setup logging. Args: + package: The package name to get the logging configuration for. config: A plugin configuration dictionary. """ - logging.config.dictConfig(_get_default_config()) + path = _get_default_config_path(package) + logging.config.dictConfig(_load_yaml_logging_config(path)) config = config or {} metrics_log_level = config.get(METRICS_LOG_LEVEL_SETTING, "INFO").upper() diff --git a/singer_sdk/plugin_base.py b/singer_sdk/plugin_base.py index 1564558cf..b88559088 100644 --- a/singer_sdk/plugin_base.py +++ b/singer_sdk/plugin_base.py @@ -162,7 +162,10 @@ def __init__( if self._is_secret_config(k): config_dict[k] = SecretString(v) self._config = config_dict - metrics._setup_logging(self.config) # noqa: SLF001 + metrics._setup_logging( # noqa: SLF001 + self.config, + package=self.__module__.split(".", maxsplit=1)[0], + ) self.metrics_logger = metrics.get_metrics_logger() self._validate_config(raise_errors=validate_config) From c8f38fcd173bf7e61706603059ef9b233e0d3174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Thu, 18 Jul 2024 17:56:12 -0600 Subject: [PATCH 03/20] refactor: Deprecate `row` parameter of `Stream.post_process` in favor or `record` Take 2 for https://github.com/meltano/sdk/pull/966 --- .pre-commit-config.yaml | 2 +- pyproject.toml | 1 + singer_sdk/helpers/_deprecation.py | 21 ++++++++ singer_sdk/streams/core.py | 21 ++++++-- singer_sdk/typing.py | 2 +- tests/core/helpers/test_deprecation.py | 70 ++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 singer_sdk/helpers/_deprecation.py create mode 100644 tests/core/helpers/test_deprecation.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ebf5e849c..5edfaf497 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: - id: check-readthedocs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.2 + rev: v0.5.3 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/pyproject.toml b/pyproject.toml index eae7ecd64..6ab3d7813 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -386,6 +386,7 @@ unfixable = [ "SLF001", "PLC2701", # Allow usage of private members in tests "PLR6301", # Don't suggest making test methods static, etc. + "INP001", # flake8-no-pep420: implicit-namespace-package ] # Disabled some checks in samples code "samples/*" = ["ANN", "D"] diff --git a/singer_sdk/helpers/_deprecation.py b/singer_sdk/helpers/_deprecation.py new file mode 100644 index 000000000..2ecf9fdf3 --- /dev/null +++ b/singer_sdk/helpers/_deprecation.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import functools +import typing as t +import warnings + + +def deprecate_row_param(func: t.Callable) -> t.Callable: + @functools.wraps(func) + def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any: # noqa: ANN401 + if "row" in kwargs: + warnings.warn( + f"The 'row' parameter for '{func.__qualname__}' is deprecated. " + "Use 'record' instead.", + category=DeprecationWarning, + stacklevel=2, + ) + kwargs["record"] = kwargs.pop("row") + return func(*args, **kwargs) + + return wrapper diff --git a/singer_sdk/streams/core.py b/singer_sdk/streams/core.py index c588a9729..5200cf53d 100644 --- a/singer_sdk/streams/core.py +++ b/singer_sdk/streams/core.py @@ -7,6 +7,7 @@ import datetime import json import typing as t +from contextlib import contextmanager from os import PathLike from pathlib import Path from types import MappingProxyType @@ -28,6 +29,7 @@ ) from singer_sdk.helpers._catalog import pop_deselected_record_properties from singer_sdk.helpers._compat import datetime_fromisoformat +from singer_sdk.helpers._deprecation import deprecate_row_param from singer_sdk.helpers._flattening import get_flattening_options from singer_sdk.helpers._state import ( finalize_state_progress_markers, @@ -1155,6 +1157,15 @@ def _sync_batches( self._write_batch_message(encoding=encoding, manifest=manifest) self._write_state_message() + @contextmanager + def _with_context( + self, + context: types.Context | None, + ) -> t.Generator[None, None, None]: + self.context = MappingProxyType(context) if context else None + yield + self.context = None + # Public methods ("final", not recommended to be overridden) @t.final @@ -1387,9 +1398,10 @@ def get_batches( for manifest in batcher.get_batches(records=records): yield batch_config.encoding, manifest + @deprecate_row_param def post_process( # noqa: PLR6301 self, - row: types.Record, + record: types.Record, context: types.Context | None = None, # noqa: ARG002 ) -> dict | None: """As needed, append or transform raw data to match expected structure. @@ -1403,10 +1415,13 @@ def post_process( # noqa: PLR6301 invalid or not-applicable records from the stream. Args: - row: Individual record in the stream. + record: Individual record in the stream. context: Stream partition or context dictionary. Returns: The resulting record dict, or `None` if the record should be excluded. + + .. versionchanged:: 0.39.0 + The ``row`` parameter was renamed to ``record`` with a deprecation warning. """ - return row + return record diff --git a/singer_sdk/typing.py b/singer_sdk/typing.py index ca4d917df..908850686 100644 --- a/singer_sdk/typing.py +++ b/singer_sdk/typing.py @@ -612,7 +612,7 @@ class Property(JSONTypeHelper[T], t.Generic[T]): """Generic Property. Should be nested within a `PropertiesList`.""" # TODO: Make some of these arguments keyword-only. This is a breaking change. - def __init__( # noqa: PLR0913 + def __init__( self, name: str, wrapped: JSONTypeHelper[T] | type[JSONTypeHelper[T]], diff --git a/tests/core/helpers/test_deprecation.py b/tests/core/helpers/test_deprecation.py new file mode 100644 index 000000000..5c25feb17 --- /dev/null +++ b/tests/core/helpers/test_deprecation.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import warnings + +from singer_sdk.helpers._deprecation import deprecate_row_param + + +class Deprecated: + @deprecate_row_param + def check_row(self, record): + pass + + +class StaleSubclass(Deprecated): + def check_row(self, row): + return super().check_row(row=row) + + +class OkSubclass(Deprecated): + def check_row(self, row): + return super().check_row(row) + + +class NewSubclass(Deprecated): + def check_row(self, record): + return super().check_row(record) + + +def test_deprecated_row_parameter(): + d = Deprecated() + + # No warning should be raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + d.check_row("test") + + # No warning should be raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + d.check_row(record="test") + + # Warning should be raised + _assert_deprecation_warning(d) + + s = StaleSubclass() + _assert_deprecation_warning(s) + + o = OkSubclass() + # No warning should be raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + o.check_row(row="test") + + n = NewSubclass() + # No warning should be raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + n.check_row(record="test") + + +def _assert_deprecation_warning(instance: Deprecated): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + instance.check_row(row="test") + assert len(w) == 1 + assert issubclass(w[-1].category, DeprecationWarning) + assert str(w[-1].message) == ( + "The 'row' parameter for 'Deprecated.check_row' is deprecated. " + "Use 'record' instead." + ) From c3bad131afb5e89fd4c860f46f6dedc7e8edbaf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Thu, 18 Jul 2024 17:57:22 -0600 Subject: [PATCH 04/20] Revert "refactor: Deprecate `row` parameter of `Stream.post_process` in favor or `record`" This reverts commit c8f38fcd173bf7e61706603059ef9b233e0d3174. --- .pre-commit-config.yaml | 2 +- pyproject.toml | 1 - singer_sdk/helpers/_deprecation.py | 21 -------- singer_sdk/streams/core.py | 21 ++------ singer_sdk/typing.py | 2 +- tests/core/helpers/test_deprecation.py | 70 -------------------------- 6 files changed, 5 insertions(+), 112 deletions(-) delete mode 100644 singer_sdk/helpers/_deprecation.py delete mode 100644 tests/core/helpers/test_deprecation.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5edfaf497..ebf5e849c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: - id: check-readthedocs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.3 + rev: v0.5.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/pyproject.toml b/pyproject.toml index 6ab3d7813..eae7ecd64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -386,7 +386,6 @@ unfixable = [ "SLF001", "PLC2701", # Allow usage of private members in tests "PLR6301", # Don't suggest making test methods static, etc. - "INP001", # flake8-no-pep420: implicit-namespace-package ] # Disabled some checks in samples code "samples/*" = ["ANN", "D"] diff --git a/singer_sdk/helpers/_deprecation.py b/singer_sdk/helpers/_deprecation.py deleted file mode 100644 index 2ecf9fdf3..000000000 --- a/singer_sdk/helpers/_deprecation.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -import functools -import typing as t -import warnings - - -def deprecate_row_param(func: t.Callable) -> t.Callable: - @functools.wraps(func) - def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any: # noqa: ANN401 - if "row" in kwargs: - warnings.warn( - f"The 'row' parameter for '{func.__qualname__}' is deprecated. " - "Use 'record' instead.", - category=DeprecationWarning, - stacklevel=2, - ) - kwargs["record"] = kwargs.pop("row") - return func(*args, **kwargs) - - return wrapper diff --git a/singer_sdk/streams/core.py b/singer_sdk/streams/core.py index 5200cf53d..c588a9729 100644 --- a/singer_sdk/streams/core.py +++ b/singer_sdk/streams/core.py @@ -7,7 +7,6 @@ import datetime import json import typing as t -from contextlib import contextmanager from os import PathLike from pathlib import Path from types import MappingProxyType @@ -29,7 +28,6 @@ ) from singer_sdk.helpers._catalog import pop_deselected_record_properties from singer_sdk.helpers._compat import datetime_fromisoformat -from singer_sdk.helpers._deprecation import deprecate_row_param from singer_sdk.helpers._flattening import get_flattening_options from singer_sdk.helpers._state import ( finalize_state_progress_markers, @@ -1157,15 +1155,6 @@ def _sync_batches( self._write_batch_message(encoding=encoding, manifest=manifest) self._write_state_message() - @contextmanager - def _with_context( - self, - context: types.Context | None, - ) -> t.Generator[None, None, None]: - self.context = MappingProxyType(context) if context else None - yield - self.context = None - # Public methods ("final", not recommended to be overridden) @t.final @@ -1398,10 +1387,9 @@ def get_batches( for manifest in batcher.get_batches(records=records): yield batch_config.encoding, manifest - @deprecate_row_param def post_process( # noqa: PLR6301 self, - record: types.Record, + row: types.Record, context: types.Context | None = None, # noqa: ARG002 ) -> dict | None: """As needed, append or transform raw data to match expected structure. @@ -1415,13 +1403,10 @@ def post_process( # noqa: PLR6301 invalid or not-applicable records from the stream. Args: - record: Individual record in the stream. + row: Individual record in the stream. context: Stream partition or context dictionary. Returns: The resulting record dict, or `None` if the record should be excluded. - - .. versionchanged:: 0.39.0 - The ``row`` parameter was renamed to ``record`` with a deprecation warning. """ - return record + return row diff --git a/singer_sdk/typing.py b/singer_sdk/typing.py index 908850686..ca4d917df 100644 --- a/singer_sdk/typing.py +++ b/singer_sdk/typing.py @@ -612,7 +612,7 @@ class Property(JSONTypeHelper[T], t.Generic[T]): """Generic Property. Should be nested within a `PropertiesList`.""" # TODO: Make some of these arguments keyword-only. This is a breaking change. - def __init__( + def __init__( # noqa: PLR0913 self, name: str, wrapped: JSONTypeHelper[T] | type[JSONTypeHelper[T]], diff --git a/tests/core/helpers/test_deprecation.py b/tests/core/helpers/test_deprecation.py deleted file mode 100644 index 5c25feb17..000000000 --- a/tests/core/helpers/test_deprecation.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -import warnings - -from singer_sdk.helpers._deprecation import deprecate_row_param - - -class Deprecated: - @deprecate_row_param - def check_row(self, record): - pass - - -class StaleSubclass(Deprecated): - def check_row(self, row): - return super().check_row(row=row) - - -class OkSubclass(Deprecated): - def check_row(self, row): - return super().check_row(row) - - -class NewSubclass(Deprecated): - def check_row(self, record): - return super().check_row(record) - - -def test_deprecated_row_parameter(): - d = Deprecated() - - # No warning should be raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - d.check_row("test") - - # No warning should be raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - d.check_row(record="test") - - # Warning should be raised - _assert_deprecation_warning(d) - - s = StaleSubclass() - _assert_deprecation_warning(s) - - o = OkSubclass() - # No warning should be raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - o.check_row(row="test") - - n = NewSubclass() - # No warning should be raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - n.check_row(record="test") - - -def _assert_deprecation_warning(instance: Deprecated): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - instance.check_row(row="test") - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert str(w[-1].message) == ( - "The 'row' parameter for 'Deprecated.check_row' is deprecated. " - "Use 'record' instead." - ) From 6811b319d9d189fb528883d0f8643585fd58fdc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 19:05:26 -0600 Subject: [PATCH 05/20] chore(deps): bump the runtime-dependencies group with 2 updates (#2546) Bumps the runtime-dependencies group with 2 updates: [sphinx](https://github.com/sphinx-doc/sphinx) and [furo](https://github.com/pradyunsg/furo). Updates `sphinx` from 7.4.5 to 7.4.6 - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.4.5...v7.4.6) Updates `furo` from 2024.5.6 to 2024.7.18 - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2024.05.06...2024.07.18) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: runtime-dependencies - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor dependency-group: runtime-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 355 ++++++++++++++++++++++++++-------------------------- 1 file changed, 175 insertions(+), 180 deletions(-) diff --git a/poetry.lock b/poetry.lock index 73a4db60d..df017be89 100644 --- a/poetry.lock +++ b/poetry.lock @@ -157,17 +157,17 @@ lxml = ["lxml"] [[package]] name = "boto3" -version = "1.34.138" +version = "1.34.145" description = "The AWS SDK for Python" optional = true python-versions = ">=3.8" files = [ - {file = "boto3-1.34.138-py3-none-any.whl", hash = "sha256:81518aa95fad71279411fb5c94da4b4a554a5d53fc876faca62b7b5c8737f1cb"}, - {file = "boto3-1.34.138.tar.gz", hash = "sha256:f79c15e33eb7706f197d98d828b193cf0891966682ad3ec5e900f6f9e7362e35"}, + {file = "boto3-1.34.145-py3-none-any.whl", hash = "sha256:69d5afb7a017d07dd6bdfb680d2912d5d369b3fafa0a45161207d9f393b14d7e"}, + {file = "boto3-1.34.145.tar.gz", hash = "sha256:ac770fb53dde1743aec56bd8e56b7ee2e2f5ad42a37825968ec4ff8428822640"}, ] [package.dependencies] -botocore = ">=1.34.138,<1.35.0" +botocore = ">=1.34.145,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -176,13 +176,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.138" +version = "1.34.145" description = "Low-level, data-driven core of boto 3." optional = true python-versions = ">=3.8" files = [ - {file = "botocore-1.34.138-py3-none-any.whl", hash = "sha256:84e96a954c39a6f09cae4ea95b2ae582b5ae01b5040c92507b60509c9be5377a"}, - {file = "botocore-1.34.138.tar.gz", hash = "sha256:f558bbea96c4a4abbaeeedc477dabb00902311ba1ca6327974a6819b9f384920"}, + {file = "botocore-1.34.145-py3-none-any.whl", hash = "sha256:2e72e262de02adcb0264ac2bac159a28f55dbba8d9e52aa0308773a42950dff5"}, + {file = "botocore-1.34.145.tar.gz", hash = "sha256:edf0fb4c02186ae29b76263ac5fda18b0a085d334a310551c9984407cf1079e6"}, ] [package.dependencies] @@ -543,13 +543,13 @@ tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [[package]] name = "docutils" -version = "0.20.1" +version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = true -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, - {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] [[package]] @@ -625,13 +625,13 @@ sqlalchemy = ">=1.3.22" [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -718,13 +718,13 @@ six = ">=1.10,<2.0" [[package]] name = "furo" -version = "2024.5.6" +version = "2024.7.18" description = "A clean customisable Sphinx documentation theme." optional = true python-versions = ">=3.8" files = [ - {file = "furo-2024.5.6-py3-none-any.whl", hash = "sha256:490a00d08c0a37ecc90de03ae9227e8eb5d6f7f750edf9807f398a2bdf2358de"}, - {file = "furo-2024.5.6.tar.gz", hash = "sha256:81f205a6605ebccbb883350432b4831c0196dd3d1bc92f61e1f459045b3d2b0b"}, + {file = "furo-2024.7.18-py3-none-any.whl", hash = "sha256:b192c7c1f59805494c8ed606d9375fdac6e6ba8178e747e72bc116745fb7e13f"}, + {file = "furo-2024.7.18.tar.gz", hash = "sha256:37b08c5fccc95d46d8712c8be97acd46043963895edde05b0f4f135d58325c83"}, ] [package.dependencies] @@ -1436,52 +1436,42 @@ files = [ [[package]] name = "pyarrow" -version = "16.1.0" +version = "17.0.0" description = "Python library for Apache Arrow" optional = true python-versions = ">=3.8" files = [ - {file = "pyarrow-16.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:17e23b9a65a70cc733d8b738baa6ad3722298fa0c81d88f63ff94bf25eaa77b9"}, - {file = "pyarrow-16.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4740cc41e2ba5d641071d0ab5e9ef9b5e6e8c7611351a5cb7c1d175eaf43674a"}, - {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98100e0268d04e0eec47b73f20b39c45b4006f3c4233719c3848aa27a03c1aef"}, - {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68f409e7b283c085f2da014f9ef81e885d90dcd733bd648cfba3ef265961848"}, - {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a8914cd176f448e09746037b0c6b3a9d7688cef451ec5735094055116857580c"}, - {file = "pyarrow-16.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:48be160782c0556156d91adbdd5a4a7e719f8d407cb46ae3bb4eaee09b3111bd"}, - {file = "pyarrow-16.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cf389d444b0f41d9fe1444b70650fea31e9d52cfcb5f818b7888b91b586efff"}, - {file = "pyarrow-16.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d0ebea336b535b37eee9eee31761813086d33ed06de9ab6fc6aaa0bace7b250c"}, - {file = "pyarrow-16.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e73cfc4a99e796727919c5541c65bb88b973377501e39b9842ea71401ca6c1c"}, - {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9251264247ecfe93e5f5a0cd43b8ae834f1e61d1abca22da55b20c788417f6"}, - {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddf5aace92d520d3d2a20031d8b0ec27b4395cab9f74e07cc95edf42a5cc0147"}, - {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:25233642583bf658f629eb230b9bb79d9af4d9f9229890b3c878699c82f7d11e"}, - {file = "pyarrow-16.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a33a64576fddfbec0a44112eaf844c20853647ca833e9a647bfae0582b2ff94b"}, - {file = "pyarrow-16.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:185d121b50836379fe012753cf15c4ba9638bda9645183ab36246923875f8d1b"}, - {file = "pyarrow-16.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2e51ca1d6ed7f2e9d5c3c83decf27b0d17bb207a7dea986e8dc3e24f80ff7d6f"}, - {file = "pyarrow-16.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06ebccb6f8cb7357de85f60d5da50e83507954af617d7b05f48af1621d331c9a"}, - {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04707f1979815f5e49824ce52d1dceb46e2f12909a48a6a753fe7cafbc44a0c"}, - {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d32000693deff8dc5df444b032b5985a48592c0697cb6e3071a5d59888714e2"}, - {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8785bb10d5d6fd5e15d718ee1d1f914fe768bf8b4d1e5e9bf253de8a26cb1628"}, - {file = "pyarrow-16.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e1369af39587b794873b8a307cc6623a3b1194e69399af0efd05bb202195a5a7"}, - {file = "pyarrow-16.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:febde33305f1498f6df85e8020bca496d0e9ebf2093bab9e0f65e2b4ae2b3444"}, - {file = "pyarrow-16.1.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b5f5705ab977947a43ac83b52ade3b881eb6e95fcc02d76f501d549a210ba77f"}, - {file = "pyarrow-16.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0d27bf89dfc2576f6206e9cd6cf7a107c9c06dc13d53bbc25b0bd4556f19cf5f"}, - {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d07de3ee730647a600037bc1d7b7994067ed64d0eba797ac74b2bc77384f4c2"}, - {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbef391b63f708e103df99fbaa3acf9f671d77a183a07546ba2f2c297b361e83"}, - {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:19741c4dbbbc986d38856ee7ddfdd6a00fc3b0fc2d928795b95410d38bb97d15"}, - {file = "pyarrow-16.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f2c5fb249caa17b94e2b9278b36a05ce03d3180e6da0c4c3b3ce5b2788f30eed"}, - {file = "pyarrow-16.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:e6b6d3cd35fbb93b70ade1336022cc1147b95ec6af7d36906ca7fe432eb09710"}, - {file = "pyarrow-16.1.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:18da9b76a36a954665ccca8aa6bd9f46c1145f79c0bb8f4f244f5f8e799bca55"}, - {file = "pyarrow-16.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:99f7549779b6e434467d2aa43ab2b7224dd9e41bdde486020bae198978c9e05e"}, - {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f07fdffe4fd5b15f5ec15c8b64584868d063bc22b86b46c9695624ca3505b7b4"}, - {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddfe389a08ea374972bd4065d5f25d14e36b43ebc22fc75f7b951f24378bf0b5"}, - {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3b20bd67c94b3a2ea0a749d2a5712fc845a69cb5d52e78e6449bbd295611f3aa"}, - {file = "pyarrow-16.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ba8ac20693c0bb0bf4b238751d4409e62852004a8cf031c73b0e0962b03e45e3"}, - {file = "pyarrow-16.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:31a1851751433d89a986616015841977e0a188662fcffd1a5677453f1df2de0a"}, - {file = "pyarrow-16.1.0.tar.gz", hash = "sha256:15fbb22ea96d11f0b5768504a3f961edab25eaf4197c341720c4a387f6c60315"}, + {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, + {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, + {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, + {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, + {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"}, ] [package.dependencies] numpy = ">=1.16.6" +[package.extras] +test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] + [[package]] name = "pycparser" version = "2.22" @@ -1784,110 +1774,110 @@ six = "*" [[package]] name = "rpds-py" -version = "0.18.1" +version = "0.19.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, - {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, - {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, - {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, - {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, - {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, - {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, - {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, - {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, - {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, - {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, - {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, - {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, - {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, - {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, - {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, - {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, - {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, - {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, - {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, - {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, + {file = "rpds_py-0.19.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:fb37bd599f031f1a6fb9e58ec62864ccf3ad549cf14bac527dbfa97123edcca4"}, + {file = "rpds_py-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3384d278df99ec2c6acf701d067147320b864ef6727405d6470838476e44d9e8"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54548e0be3ac117595408fd4ca0ac9278fde89829b0b518be92863b17ff67a2"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8eb488ef928cdbc05a27245e52de73c0d7c72a34240ef4d9893fdf65a8c1a955"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5da93debdfe27b2bfc69eefb592e1831d957b9535e0943a0ee8b97996de21b5"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79e205c70afddd41f6ee79a8656aec738492a550247a7af697d5bd1aee14f766"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:959179efb3e4a27610e8d54d667c02a9feaa86bbabaf63efa7faa4dfa780d4f1"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a6e605bb9edcf010f54f8b6a590dd23a4b40a8cb141255eec2a03db249bc915b"}, + {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9133d75dc119a61d1a0ded38fb9ba40a00ef41697cc07adb6ae098c875195a3f"}, + {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd36b712d35e757e28bf2f40a71e8f8a2d43c8b026d881aa0c617b450d6865c9"}, + {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354f3a91718489912f2e0fc331c24eaaf6a4565c080e00fbedb6015857c00582"}, + {file = "rpds_py-0.19.0-cp310-none-win32.whl", hash = "sha256:ebcbf356bf5c51afc3290e491d3722b26aaf5b6af3c1c7f6a1b757828a46e336"}, + {file = "rpds_py-0.19.0-cp310-none-win_amd64.whl", hash = "sha256:75a6076289b2df6c8ecb9d13ff79ae0cad1d5fb40af377a5021016d58cd691ec"}, + {file = "rpds_py-0.19.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6d45080095e585f8c5097897313def60caa2046da202cdb17a01f147fb263b81"}, + {file = "rpds_py-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5c9581019c96f865483d031691a5ff1cc455feb4d84fc6920a5ffc48a794d8a"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1540d807364c84516417115c38f0119dfec5ea5c0dd9a25332dea60b1d26fc4d"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e65489222b410f79711dc3d2d5003d2757e30874096b2008d50329ea4d0f88c"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9da6f400eeb8c36f72ef6646ea530d6d175a4f77ff2ed8dfd6352842274c1d8b"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37f46bb11858717e0efa7893c0f7055c43b44c103e40e69442db5061cb26ed34"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:071d4adc734de562bd11d43bd134330fb6249769b2f66b9310dab7460f4bf714"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9625367c8955e4319049113ea4f8fee0c6c1145192d57946c6ffcd8fe8bf48dd"}, + {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e19509145275d46bc4d1e16af0b57a12d227c8253655a46bbd5ec317e941279d"}, + {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d438e4c020d8c39961deaf58f6913b1bf8832d9b6f62ec35bd93e97807e9cbc"}, + {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90bf55d9d139e5d127193170f38c584ed3c79e16638890d2e36f23aa1630b952"}, + {file = "rpds_py-0.19.0-cp311-none-win32.whl", hash = "sha256:8d6ad132b1bc13d05ffe5b85e7a01a3998bf3a6302ba594b28d61b8c2cf13aaf"}, + {file = "rpds_py-0.19.0-cp311-none-win_amd64.whl", hash = "sha256:7ec72df7354e6b7f6eb2a17fa6901350018c3a9ad78e48d7b2b54d0412539a67"}, + {file = "rpds_py-0.19.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:5095a7c838a8647c32aa37c3a460d2c48debff7fc26e1136aee60100a8cd8f68"}, + {file = "rpds_py-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f2f78ef14077e08856e788fa482107aa602636c16c25bdf59c22ea525a785e9"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7cc6cb44f8636fbf4a934ca72f3e786ba3c9f9ba4f4d74611e7da80684e48d2"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf902878b4af334a09de7a45badbff0389e7cf8dc2e4dcf5f07125d0b7c2656d"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:688aa6b8aa724db1596514751ffb767766e02e5c4a87486ab36b8e1ebc1aedac"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57dbc9167d48e355e2569346b5aa4077f29bf86389c924df25c0a8b9124461fb"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4cf5a9497874822341c2ebe0d5850fed392034caadc0bad134ab6822c0925b"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a790d235b9d39c70a466200d506bb33a98e2ee374a9b4eec7a8ac64c2c261fa"}, + {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d16089dfa58719c98a1c06f2daceba6d8e3fb9b5d7931af4a990a3c486241cb"}, + {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bc9128e74fe94650367fe23f37074f121b9f796cabbd2f928f13e9661837296d"}, + {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c8f77e661ffd96ff104bebf7d0f3255b02aa5d5b28326f5408d6284c4a8b3248"}, + {file = "rpds_py-0.19.0-cp312-none-win32.whl", hash = "sha256:5f83689a38e76969327e9b682be5521d87a0c9e5a2e187d2bc6be4765f0d4600"}, + {file = "rpds_py-0.19.0-cp312-none-win_amd64.whl", hash = "sha256:06925c50f86da0596b9c3c64c3837b2481337b83ef3519e5db2701df695453a4"}, + {file = "rpds_py-0.19.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:52e466bea6f8f3a44b1234570244b1cff45150f59a4acae3fcc5fd700c2993ca"}, + {file = "rpds_py-0.19.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e21cc693045fda7f745c790cb687958161ce172ffe3c5719ca1764e752237d16"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b31f059878eb1f5da8b2fd82480cc18bed8dcd7fb8fe68370e2e6285fa86da6"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dd46f309e953927dd018567d6a9e2fb84783963650171f6c5fe7e5c41fd5666"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a01a4490e170376cd79258b7f755fa13b1a6c3667e872c8e35051ae857a92b"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcf426a8c38eb57f7bf28932e68425ba86def6e756a5b8cb4731d8e62e4e0223"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68eea5df6347d3f1378ce992d86b2af16ad7ff4dcb4a19ccdc23dea901b87fb"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dab8d921b55a28287733263c0e4c7db11b3ee22aee158a4de09f13c93283c62d"}, + {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6fe87efd7f47266dfc42fe76dae89060038f1d9cb911f89ae7e5084148d1cc08"}, + {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:535d4b52524a961d220875688159277f0e9eeeda0ac45e766092bfb54437543f"}, + {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8b1a94b8afc154fbe36978a511a1f155f9bd97664e4f1f7a374d72e180ceb0ae"}, + {file = "rpds_py-0.19.0-cp38-none-win32.whl", hash = "sha256:7c98298a15d6b90c8f6e3caa6457f4f022423caa5fa1a1ca7a5e9e512bdb77a4"}, + {file = "rpds_py-0.19.0-cp38-none-win_amd64.whl", hash = "sha256:b0da31853ab6e58a11db3205729133ce0df26e6804e93079dee095be3d681dc1"}, + {file = "rpds_py-0.19.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5039e3cef7b3e7a060de468a4a60a60a1f31786da94c6cb054e7a3c75906111c"}, + {file = "rpds_py-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab1932ca6cb8c7499a4d87cb21ccc0d3326f172cfb6a64021a889b591bb3045c"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2afd2164a1e85226fcb6a1da77a5c8896c18bfe08e82e8ceced5181c42d2179"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1c30841f5040de47a0046c243fc1b44ddc87d1b12435a43b8edff7e7cb1e0d0"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f757f359f30ec7dcebca662a6bd46d1098f8b9fb1fcd661a9e13f2e8ce343ba1"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15e65395a59d2e0e96caf8ee5389ffb4604e980479c32742936ddd7ade914b22"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb0f6eb3a320f24b94d177e62f4074ff438f2ad9d27e75a46221904ef21a7b05"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b228e693a2559888790936e20f5f88b6e9f8162c681830eda303bad7517b4d5a"}, + {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2575efaa5d949c9f4e2cdbe7d805d02122c16065bfb8d95c129372d65a291a0b"}, + {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5c872814b77a4e84afa293a1bee08c14daed1068b2bb1cc312edbf020bbbca2b"}, + {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:850720e1b383df199b8433a20e02b25b72f0fded28bc03c5bd79e2ce7ef050be"}, + {file = "rpds_py-0.19.0-cp39-none-win32.whl", hash = "sha256:ce84a7efa5af9f54c0aa7692c45861c1667080814286cacb9958c07fc50294fb"}, + {file = "rpds_py-0.19.0-cp39-none-win_amd64.whl", hash = "sha256:1c26da90b8d06227d7769f34915913911222d24ce08c0ab2d60b354e2d9c7aff"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:75969cf900d7be665ccb1622a9aba225cf386bbc9c3bcfeeab9f62b5048f4a07"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8445f23f13339da640d1be8e44e5baf4af97e396882ebbf1692aecd67f67c479"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5a7c1062ef8aea3eda149f08120f10795835fc1c8bc6ad948fb9652a113ca55"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:462b0c18fbb48fdbf980914a02ee38c423a25fcc4cf40f66bacc95a2d2d73bc8"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3208f9aea18991ac7f2b39721e947bbd752a1abbe79ad90d9b6a84a74d44409b"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3444fe52b82f122d8a99bf66777aed6b858d392b12f4c317da19f8234db4533"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb4bac7185a9f0168d38c01d7a00addece9822a52870eee26b8d5b61409213"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6b130bd4163c93798a6b9bb96be64a7c43e1cec81126ffa7ffaa106e1fc5cef5"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a707b158b4410aefb6b054715545bbb21aaa5d5d0080217290131c49c2124a6e"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dc9ac4659456bde7c567107556ab065801622396b435a3ff213daef27b495388"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:81ea573aa46d3b6b3d890cd3c0ad82105985e6058a4baed03cf92518081eec8c"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f148c3f47f7f29a79c38cc5d020edcb5ca780020fab94dbc21f9af95c463581"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0906357f90784a66e89ae3eadc2654f36c580a7d65cf63e6a616e4aec3a81be"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f629ecc2db6a4736b5ba95a8347b0089240d69ad14ac364f557d52ad68cf94b0"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6feacd1d178c30e5bc37184526e56740342fd2aa6371a28367bad7908d454fc"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b6068ee374fdfab63689be0963333aa83b0815ead5d8648389a8ded593378"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78d57546bad81e0da13263e4c9ce30e96dcbe720dbff5ada08d2600a3502e526"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b6683a37338818646af718c9ca2a07f89787551057fae57c4ec0446dc6224b"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e8481b946792415adc07410420d6fc65a352b45d347b78fec45d8f8f0d7496f0"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bec35eb20792ea64c3c57891bc3ca0bedb2884fbac2c8249d9b731447ecde4fa"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:aa5476c3e3a402c37779e95f7b4048db2cb5b0ed0b9d006983965e93f40fe05a"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:19d02c45f2507b489fd4df7b827940f1420480b3e2e471e952af4d44a1ea8e34"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a3e2fd14c5d49ee1da322672375963f19f32b3d5953f0615b175ff7b9d38daed"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:93a91c2640645303e874eada51f4f33351b84b351a689d470f8108d0e0694210"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5b9fc03bf76a94065299d4a2ecd8dfbae4ae8e2e8098bbfa6ab6413ca267709"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a4b07cdf3f84310c08c1de2c12ddadbb7a77568bcb16e95489f9c81074322ed"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba0ed0dc6763d8bd6e5de5cf0d746d28e706a10b615ea382ac0ab17bb7388633"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:474bc83233abdcf2124ed3f66230a1c8435896046caa4b0b5ab6013c640803cc"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329c719d31362355a96b435f4653e3b4b061fcc9eba9f91dd40804ca637d914e"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef9101f3f7b59043a34f1dccbb385ca760467590951952d6701df0da9893ca0c"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:0121803b0f424ee2109d6e1f27db45b166ebaa4b32ff47d6aa225642636cd834"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8344127403dea42f5970adccf6c5957a71a47f522171fafaf4c6ddb41b61703a"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:443cec402ddd650bb2b885113e1dcedb22b1175c6be223b14246a714b61cd521"}, + {file = "rpds_py-0.19.0.tar.gz", hash = "sha256:4fdc9afadbeb393b4bbbad75481e0ea78e4469f2e1d713a90811700830b553a9"}, ] [[package]] @@ -1909,18 +1899,19 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] [[package]] name = "setuptools" -version = "70.2.0" +version = "71.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"}, - {file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"}, + {file = "setuptools-71.0.3-py3-none-any.whl", hash = "sha256:f501b6e6db709818dc76882582d9c516bf3b67b948864c5fa1d1624c09a49207"}, + {file = "setuptools-71.0.3.tar.gz", hash = "sha256:3d8531791a27056f4a38cd3e54084d8b1c4228ff9cf3f2d7dd075ec99f9fd70d"}, ] [package.extras] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (<7.4)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simpleeval" @@ -2075,13 +2066,13 @@ files = [ [[package]] name = "sphinx" -version = "7.4.5" +version = "7.4.6" description = "Python documentation generator" optional = true python-versions = ">=3.9" files = [ - {file = "sphinx-7.4.5-py3-none-any.whl", hash = "sha256:9f135d8c1d277db67be514be579c4c4a26c8c0e962219aaca5a721b04bd6d0d8"}, - {file = "sphinx-7.4.5.tar.gz", hash = "sha256:a4abe5385bf856df094c1e6cadf24a2351b12057be3670b99a12c05a01d209f5"}, + {file = "sphinx-7.4.6-py3-none-any.whl", hash = "sha256:915760d6188288a1e30c2cd0d9fa31b1b009bc6e6019cc0c32d16c77d20e86d9"}, + {file = "sphinx-7.4.6.tar.gz", hash = "sha256:116918d455c493fff3178edea12b4fe1c1e4894680fd81e7b7431ea21d47ca52"}, ] [package.dependencies] @@ -2196,47 +2187,50 @@ sphinx = ">=7.1" [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.4" +version = "1.0.8" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, - {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +version = "1.0.6" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = true -python-versions = ">=3.5" +python-versions = ">=3.9" files = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.1" +version = "2.0.5" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = true -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, - {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] [[package]] @@ -2255,17 +2249,18 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +version = "1.0.7" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = true -python-versions = ">=3.5" +python-versions = ">=3.9" files = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] From 9a7c539c33e1144ac5d22340682e95a9ee6ae2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Thu, 18 Jul 2024 19:41:29 -0600 Subject: [PATCH 06/20] chore: Add a `packaging` PR category (#2547) --- .github/semantic.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/semantic.yml b/.github/semantic.yml index ff00a1ad4..08bebb333 100644 --- a/.github/semantic.yml +++ b/.github/semantic.yml @@ -15,6 +15,7 @@ types: - docs - feat - fix + - packaging - perf - refactor - revert From 3bd3770a412ae352a4d3cea775b8841aa0d419ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Thu, 18 Jul 2024 21:30:26 -0600 Subject: [PATCH 07/20] test: Add (DuckDB) SQL sink tests (#2548) --- samples/sample_duckdb/__init__.py | 7 ++++ samples/sample_duckdb/connector.py | 24 +++++++++++ tests/core/sinks/test_sql_sink.py | 64 ++++++++++++++++++++++++++++++ tests/core/test_connector_sql.py | 20 +--------- 4 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 samples/sample_duckdb/__init__.py create mode 100644 samples/sample_duckdb/connector.py create mode 100644 tests/core/sinks/test_sql_sink.py diff --git a/samples/sample_duckdb/__init__.py b/samples/sample_duckdb/__init__.py new file mode 100644 index 000000000..bbfa07726 --- /dev/null +++ b/samples/sample_duckdb/__init__.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from .connector import DuckDBConnector + +__all__ = [ + "DuckDBConnector", +] diff --git a/samples/sample_duckdb/connector.py b/samples/sample_duckdb/connector.py new file mode 100644 index 000000000..e8c6c251f --- /dev/null +++ b/samples/sample_duckdb/connector.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +import sqlalchemy as sa + +from singer_sdk.connectors import SQLConnector + + +class DuckDBConnector(SQLConnector): + allow_column_alter = True + + @staticmethod + def get_column_alter_ddl( + table_name: str, + column_name: str, + column_type: sa.types.TypeEngine, + ) -> sa.DDL: + return sa.DDL( + "ALTER TABLE %(table_name)s ALTER COLUMN %(column_name)s TYPE %(column_type)s", # noqa: E501 + { + "table_name": table_name, + "column_name": column_name, + "column_type": column_type, + }, + ) diff --git a/tests/core/sinks/test_sql_sink.py b/tests/core/sinks/test_sql_sink.py new file mode 100644 index 000000000..c20be40f6 --- /dev/null +++ b/tests/core/sinks/test_sql_sink.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import typing as t +from textwrap import dedent + +import pytest + +from samples.sample_duckdb import DuckDBConnector +from singer_sdk.sinks.sql import SQLSink +from singer_sdk.target_base import SQLTarget + + +class DuckDBSink(SQLSink): + connector_class = DuckDBConnector + + +class DuckDBTarget(SQLTarget): + """DuckDB target class.""" + + name = "sql-target-mock" + config_jsonschema: t.ClassVar[dict] = {"type": "object", "properties": {}} + default_sink_class = DuckDBSink + + +class TestDuckDBSink: + @pytest.fixture + def target(self) -> DuckDBTarget: + return DuckDBTarget(config={"sqlalchemy_url": "duckdb:///"}) + + @pytest.fixture + def schema(self) -> dict: + return { + "properties": { + "id": { + "type": ["string", "null"], + }, + "col_ts": { + "format": "date-time", + "type": ["string", "null"], + }, + "table": { + "type": ["string", "null"], + }, + }, + } + + @pytest.fixture + def sink(self, target: DuckDBTarget, schema: dict) -> DuckDBSink: + return DuckDBSink( + target, + stream_name="foo", + schema=schema, + key_properties=["id"], + ) + + def test_generate_insert_statement(self, sink: DuckDBSink, schema: dict): + """Test that the insert statement is generated correctly.""" + expected = dedent( + """\ + INSERT INTO foo + (id, col_ts, "table") + VALUES (:id, :col_ts, :table)""" + ) + assert sink.generate_insert_statement("foo", schema=schema) == expected diff --git a/tests/core/test_connector_sql.py b/tests/core/test_connector_sql.py index ff3ac5735..10ee0c0f4 100644 --- a/tests/core/test_connector_sql.py +++ b/tests/core/test_connector_sql.py @@ -8,6 +8,7 @@ import sqlalchemy as sa from sqlalchemy.dialects import registry, sqlite +from samples.sample_duckdb import DuckDBConnector from singer_sdk.connectors import SQLConnector from singer_sdk.exceptions import ConfigValidationError @@ -289,25 +290,6 @@ def test_engine_json_serialization(self, connector: SQLConnector): ] -class DuckDBConnector(SQLConnector): - allow_column_alter = True - - @staticmethod - def get_column_alter_ddl( - table_name: str, - column_name: str, - column_type: sa.types.TypeEngine, - ) -> sa.DDL: - return sa.DDL( - "ALTER TABLE %(table_name)s ALTER COLUMN %(column_name)s TYPE %(column_type)s", # noqa: E501 - { - "table_name": table_name, - "column_name": column_name, - "column_type": column_type, - }, - ) - - class TestDuckDBConnector: @pytest.fixture def connector(self): From 9901d85589b6316cb7646efb64a779535e401992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:26:56 -0600 Subject: [PATCH 08/20] test: Use DummyJSON instead of Google Analytics for integration tests (#2551) --- .github/workflows/test.yml | 3 - .../.github/dependabot.yml | 41 ++++ .../.github/workflows/build.yml | 45 ++++ .../.github/workflows/test.yml | 32 +++ samples/sample_tap_dummy_json/.gitignore | 139 ++++++++++++ .../.pre-commit-config.yaml | 38 ++++ .../sample_tap_dummy_json/.secrets/.gitignore | 10 + samples/sample_tap_dummy_json/LICENSE | 202 ++++++++++++++++++ samples/sample_tap_dummy_json/README.md | 131 ++++++++++++ samples/sample_tap_dummy_json/__init__.py | 0 samples/sample_tap_dummy_json/meltano.yml | 43 ++++ .../sample_tap_dummy_json/output/.gitignore | 4 + .../loaders/target-jsonl--andyh1203.lock | 34 +++ samples/sample_tap_dummy_json/pyproject.toml | 44 ++++ samples/sample_tap_dummy_json/ruff.toml | 24 +++ .../tap_dummyjson/__init__.py | 1 + .../tap_dummyjson/__main__.py | 7 + .../tap_dummyjson/auth.py | 72 +++++++ .../tap_dummyjson/client.py | 45 ++++ .../tap_dummyjson/schemas/__init__.py | 1 + .../tap_dummyjson/streams.py | 67 ++++++ .../tap_dummyjson/tap.py | 51 +++++ .../sample_tap_dummy_json/tests/__init__.py | 1 + .../sample_tap_dummy_json/tests/test_core.py | 11 + samples/sample_tap_dummy_json/tox.ini | 19 ++ .../sample_tap_google_analytics/__init__.py | 3 - .../sample_tap_google_analytics/__main__.py | 5 - samples/sample_tap_google_analytics/ga_tap.py | 38 ---- .../ga_tap_stream.py | 95 -------- .../resources/default_report_definitions.json | 95 -------- .../schemas/simple-sample.json | 11 - tests/external/test_tap_dummyjson.py | 11 + tests/external/test_tap_google_analytics.py | 26 --- 33 files changed, 1073 insertions(+), 276 deletions(-) create mode 100644 samples/sample_tap_dummy_json/.github/dependabot.yml create mode 100644 samples/sample_tap_dummy_json/.github/workflows/build.yml create mode 100644 samples/sample_tap_dummy_json/.github/workflows/test.yml create mode 100644 samples/sample_tap_dummy_json/.gitignore create mode 100644 samples/sample_tap_dummy_json/.pre-commit-config.yaml create mode 100644 samples/sample_tap_dummy_json/.secrets/.gitignore create mode 100644 samples/sample_tap_dummy_json/LICENSE create mode 100644 samples/sample_tap_dummy_json/README.md create mode 100644 samples/sample_tap_dummy_json/__init__.py create mode 100644 samples/sample_tap_dummy_json/meltano.yml create mode 100644 samples/sample_tap_dummy_json/output/.gitignore create mode 100644 samples/sample_tap_dummy_json/plugins/loaders/target-jsonl--andyh1203.lock create mode 100644 samples/sample_tap_dummy_json/pyproject.toml create mode 100644 samples/sample_tap_dummy_json/ruff.toml create mode 100644 samples/sample_tap_dummy_json/tap_dummyjson/__init__.py create mode 100644 samples/sample_tap_dummy_json/tap_dummyjson/__main__.py create mode 100644 samples/sample_tap_dummy_json/tap_dummyjson/auth.py create mode 100644 samples/sample_tap_dummy_json/tap_dummyjson/client.py create mode 100644 samples/sample_tap_dummy_json/tap_dummyjson/schemas/__init__.py create mode 100644 samples/sample_tap_dummy_json/tap_dummyjson/streams.py create mode 100644 samples/sample_tap_dummy_json/tap_dummyjson/tap.py create mode 100644 samples/sample_tap_dummy_json/tests/__init__.py create mode 100644 samples/sample_tap_dummy_json/tests/test_core.py create mode 100644 samples/sample_tap_dummy_json/tox.ini delete mode 100644 samples/sample_tap_google_analytics/__init__.py delete mode 100644 samples/sample_tap_google_analytics/__main__.py delete mode 100644 samples/sample_tap_google_analytics/ga_tap.py delete mode 100644 samples/sample_tap_google_analytics/ga_tap_stream.py delete mode 100644 samples/sample_tap_google_analytics/resources/default_report_definitions.json delete mode 100644 samples/sample_tap_google_analytics/schemas/simple-sample.json create mode 100644 tests/external/test_tap_dummyjson.py delete mode 100644 tests/external/test_tap_google_analytics.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 960aede32..3acc509fb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -113,9 +113,6 @@ jobs: SAMPLE_TAP_GITLAB_GROUP_IDS: ${{ secrets.SAMPLE_TAP_GITLAB_GROUP_IDS }} SAMPLE_TAP_GITLAB_PROJECT_IDS: ${{ secrets.SAMPLE_TAP_GITLAB_PROJECT_IDS }} SAMPLE_TAP_GITLAB_START_DATE: "2022-01-01T00:00:00Z" - SAMPLE_TAP_GOOGLE_ANALYTICS_CLIENT_EMAIL: ${{ secrets.SAMPLE_TAP_GOOGLE_ANALYTICS_CLIENT_EMAIL }} - SAMPLE_TAP_GOOGLE_ANALYTICS_PRIVATE_KEY: ${{ secrets.SAMPLE_TAP_GOOGLE_ANALYTICS_PRIVATE_KEY }} - SAMPLE_TAP_GOOGLE_ANALYTICS_VIEW_ID: ${{ secrets.SAMPLE_TAP_GOOGLE_ANALYTICS_VIEW_ID }} steps: - uses: actions/checkout@v4 diff --git a/samples/sample_tap_dummy_json/.github/dependabot.yml b/samples/sample_tap_dummy_json/.github/dependabot.yml new file mode 100644 index 000000000..0660ffdd4 --- /dev/null +++ b/samples/sample_tap_dummy_json/.github/dependabot.yml @@ -0,0 +1,41 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: pip + directory: "/" + schedule: + interval: weekly + commit-message: + prefix: "chore(deps): " + prefix-development: "chore(deps-dev): " + groups: + development-dependencies: + dependency-type: development + runtime-dependencies: + dependency-type: production + update-types: + - "patch" + - package-ecosystem: pip + directory: "/.github/workflows" + schedule: + interval: weekly + commit-message: + prefix: "ci: " + groups: + ci: + patterns: + - "*" + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + commit-message: + prefix: "ci: " + groups: + actions: + patterns: + - "*" diff --git a/samples/sample_tap_dummy_json/.github/workflows/build.yml b/samples/sample_tap_dummy_json/.github/workflows/build.yml new file mode 100644 index 000000000..6698b8b6a --- /dev/null +++ b/samples/sample_tap_dummy_json/.github/workflows/build.yml @@ -0,0 +1,45 @@ +name: Release + +on: + push: + +permissions: + contents: write # Needed to upload artifacts to the release + id-token: write # Needed for OIDC PyPI publishing + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: hynek/build-and-inspect-python-package@v2 + + publish: + name: Publish to PyPI + runs-on: ubuntu-latest + needs: [build] + ## TODO: optionally provide the name of the environment for the trusted + ## publisher on PyPI + ## https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment + # environment: pypi + if: startsWith(github.ref, 'refs/tags/') + steps: + - uses: actions/download-artifact@v4 + with: + name: Packages + path: dist + - name: Upload wheel to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{secrets.GITHUB_TOKEN}} + file: dist/*.whl + tag: ${{github.ref}} + overwrite: true + file_glob: true + + - name: Publish + ## TODO: create a trusted publisher on PyPI + ## https://docs.pypi.org/trusted-publishers/ + uses: pypa/gh-action-pypi-publish@v1.9.0 diff --git a/samples/sample_tap_dummy_json/.github/workflows/test.yml b/samples/sample_tap_dummy_json/.github/workflows/test.yml new file mode 100644 index 000000000..6e3d559c0 --- /dev/null +++ b/samples/sample_tap_dummy_json/.github/workflows/test.yml @@ -0,0 +1,32 @@ +### A CI workflow template that runs linting and python testing +### TODO: Modify as needed or as desired. + +name: Test tap-dummyjson + +on: [push] + +jobs: + pytest: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + strategy: + fail-fast: false + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install Poetry + run: | + pip install poetry + - name: Install dependencies + run: | + poetry env use ${{ matrix.python-version }} + poetry install + - name: Test with pytest + run: | + poetry run pytest diff --git a/samples/sample_tap_dummy_json/.gitignore b/samples/sample_tap_dummy_json/.gitignore new file mode 100644 index 000000000..9fd224433 --- /dev/null +++ b/samples/sample_tap_dummy_json/.gitignore @@ -0,0 +1,139 @@ +# Poetry +poetry.lock + +# Secrets and internal config files +**/.secrets/* + +# Ignore meltano internal cache and sqlite systemdb + +.meltano/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/samples/sample_tap_dummy_json/.pre-commit-config.yaml b/samples/sample_tap_dummy_json/.pre-commit-config.yaml new file mode 100644 index 000000000..5cad9ea54 --- /dev/null +++ b/samples/sample_tap_dummy_json/.pre-commit-config.yaml @@ -0,0 +1,38 @@ +ci: + autofix_prs: true + autoupdate_schedule: weekly + autoupdate_commit_msg: 'chore: pre-commit autoupdate' + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-json + exclude: | + (?x)^( + \.vscode/.*\.json + )$ + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.6 + hooks: + - id: check-dependabot + - id: check-github-workflows + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.0 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix, --show-fixes] + - id: ruff-format + +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.10.1 + hooks: + - id: mypy + additional_dependencies: + - types-requests diff --git a/samples/sample_tap_dummy_json/.secrets/.gitignore b/samples/sample_tap_dummy_json/.secrets/.gitignore new file mode 100644 index 000000000..33c6acd03 --- /dev/null +++ b/samples/sample_tap_dummy_json/.secrets/.gitignore @@ -0,0 +1,10 @@ +# IMPORTANT! This folder is hidden from git - if you need to store config files or other secrets, +# make sure those are never staged for commit into your git repo. You can store them here or another +# secure location. +# +# Note: This may be redundant with the global .gitignore for, and is provided +# for redundancy. If the `.secrets` folder is not needed, you may delete it +# from the project. + +* +!.gitignore diff --git a/samples/sample_tap_dummy_json/LICENSE b/samples/sample_tap_dummy_json/LICENSE new file mode 100644 index 000000000..20f02f1bc --- /dev/null +++ b/samples/sample_tap_dummy_json/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + + Copyright 2024 Edgar Ramírez-Mondragón + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/samples/sample_tap_dummy_json/README.md b/samples/sample_tap_dummy_json/README.md new file mode 100644 index 000000000..e154663d9 --- /dev/null +++ b/samples/sample_tap_dummy_json/README.md @@ -0,0 +1,131 @@ +# tap-dummyjson + +`tap-dummyjson` is a Singer tap for DummyJSON. + +Built with the [Meltano Tap SDK](https://sdk.meltano.com) for Singer Taps. + + + +## Configuration + +### Accepted Config Options + + + +A full list of supported settings and capabilities for this +tap is available by running: + +```bash +tap-dummyjson --about +``` + +### Configure using environment variables + +This Singer tap will automatically import any environment variables within the working directory's +`.env` if the `--config=ENV` is provided, such that config values will be considered if a matching +environment variable is set either in the terminal context or in the `.env` file. + +### Source Authentication and Authorization + + + +## Usage + +You can easily run `tap-dummyjson` by itself or in a pipeline using [Meltano](https://meltano.com/). + +### Executing the Tap Directly + +```bash +tap-dummyjson --version +tap-dummyjson --help +tap-dummyjson --config CONFIG --discover > ./catalog.json +``` + +## Developer Resources + +Follow these instructions to contribute to this project. + +### Initialize your Development Environment + +```bash +pipx install poetry +poetry install +``` + +### Create and Run Tests + +Create tests within the `tests` subfolder and + then run: + +```bash +poetry run pytest +``` + +You can also test the `tap-dummyjson` CLI interface directly using `poetry run`: + +```bash +poetry run tap-dummyjson --help +``` + +### Testing with [Meltano](https://www.meltano.com) + +_**Note:** This tap will work in any Singer environment and does not require Meltano. +Examples here are for convenience and to streamline end-to-end orchestration scenarios._ + + + +Next, install Meltano (if you haven't already) and any needed plugins: + +```bash +# Install meltano +pipx install meltano +# Initialize meltano within this directory +cd tap-dummyjson +meltano install +``` + +Now you can test and orchestrate using Meltano: + +```bash +# Test invocation: +meltano invoke tap-dummyjson --version +# OR run a test `elt` pipeline: +meltano elt tap-dummyjson target-jsonl +``` + +### SDK Dev Guide + +See the [dev guide](https://sdk.meltano.com/en/latest/dev_guide.html) for more instructions on how to use the SDK to +develop your own taps and targets. diff --git a/samples/sample_tap_dummy_json/__init__.py b/samples/sample_tap_dummy_json/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/samples/sample_tap_dummy_json/meltano.yml b/samples/sample_tap_dummy_json/meltano.yml new file mode 100644 index 000000000..02a2120e2 --- /dev/null +++ b/samples/sample_tap_dummy_json/meltano.yml @@ -0,0 +1,43 @@ +version: 1 +send_anonymous_usage_stats: true +project_id: "tap-dummyjson" +default_environment: test +environments: +- name: test +plugins: + extractors: + - name: "tap-dummyjson" + namespace: "tap_dummyjson" + + pip_url: -e . + + capabilities: + - state + - catalog + - discover + - about + - stream-maps + + settings: + - name: username + label: Username + - name: password + label: Password + kind: password + sensitive: true + - name: api_url + label: API URL + description: The base URL for the API + - name: start_date + kind: date_iso8601 + + settings_group_validation: + - [username, password] + + config: + start_date: '2024-01-01T00:00:00Z' + + loaders: + - name: target-jsonl + variant: andyh1203 + pip_url: target-jsonl diff --git a/samples/sample_tap_dummy_json/output/.gitignore b/samples/sample_tap_dummy_json/output/.gitignore new file mode 100644 index 000000000..80ff9d2a6 --- /dev/null +++ b/samples/sample_tap_dummy_json/output/.gitignore @@ -0,0 +1,4 @@ +# This directory is used as a target by target-jsonl, so ignore all files + +* +!.gitignore diff --git a/samples/sample_tap_dummy_json/plugins/loaders/target-jsonl--andyh1203.lock b/samples/sample_tap_dummy_json/plugins/loaders/target-jsonl--andyh1203.lock new file mode 100644 index 000000000..11fa0ba2e --- /dev/null +++ b/samples/sample_tap_dummy_json/plugins/loaders/target-jsonl--andyh1203.lock @@ -0,0 +1,34 @@ +{ + "plugin_type": "loaders", + "name": "target-jsonl", + "namespace": "target_jsonl", + "variant": "andyh1203", + "label": "JSON Lines (JSONL)", + "docs": "https://hub.meltano.com/loaders/target-jsonl--andyh1203", + "repo": "https://github.com/andyh1203/target-jsonl", + "pip_url": "target-jsonl", + "description": "JSONL loader", + "logo_url": "https://hub.meltano.com/assets/logos/loaders/jsonl.png", + "settings": [ + { + "name": "destination_path", + "kind": "string", + "value": "output", + "label": "Destination Path", + "description": "Sets the destination path the JSONL files are written to, relative\nto the project root.\n\nThe directory needs to exist already, it will not be created\nautomatically.\n\nTo write JSONL files to the project root, set an empty string (`\"\"`).\n" + }, + { + "name": "do_timestamp_file", + "kind": "boolean", + "value": false, + "label": "Include Timestamp in File Names", + "description": "Specifies if the files should get timestamped.\n\nBy default, the resulting file will not have a timestamp in the file name (i.e. `exchange_rate.jsonl`).\n\nIf this option gets set to `true`, the resulting file will have a timestamp associated with it (i.e. `exchange_rate-{timestamp}.jsonl`).\n" + }, + { + "name": "custom_name", + "kind": "string", + "label": "Custom File Name Override", + "description": "Specifies a custom name for the filename, instead of the stream name.\n\nThe file name will be `{custom_name}-{timestamp}.jsonl`, if `do_timestamp_file` is `true`.\nOtherwise the file name will be `{custom_name}.jsonl`.\n\nIf custom name is not provided, the stream name will be used.\n" + } + ] +} diff --git a/samples/sample_tap_dummy_json/pyproject.toml b/samples/sample_tap_dummy_json/pyproject.toml new file mode 100644 index 000000000..1263b3ca4 --- /dev/null +++ b/samples/sample_tap_dummy_json/pyproject.toml @@ -0,0 +1,44 @@ +[tool.poetry] +name = "tap-dummyjson" +version = "0.0.1" +description = "Singer tap for DummyJSON, built with the Meltano Singer SDK." +readme = "README.md" +authors = ["Edgar Ramírez-Mondragón "] +keywords = [ + "ELT", + "DummyJSON", +] +classifiers = [ + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +license = "Apache-2.0" + +[tool.poetry.dependencies] +python = ">=3.8" +requests = "~=2.32.3" +singer-sdk = { version="~=0.38.0", extras = [] } + +[tool.poetry.group.dev.dependencies] +pytest = ">=8" +singer-sdk = { version="~=0.38.0", extras = ["testing"] } + +[tool.poetry.extras] +s3 = ["fs-s3fs"] + +[tool.mypy] +python_version = "3.12" +warn_unused_configs = true + +[build-system] +requires = ["poetry-core==1.9.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +# CLI declaration +tap-dummyjson = 'tap_dummyjson.tap:TapDummyJSON.cli' diff --git a/samples/sample_tap_dummy_json/ruff.toml b/samples/sample_tap_dummy_json/ruff.toml new file mode 100644 index 000000000..dd2f13459 --- /dev/null +++ b/samples/sample_tap_dummy_json/ruff.toml @@ -0,0 +1,24 @@ +src = ["tap_dummyjson"] +target-version = "py38" + +[lint] +ignore = [ + "ANN", + "ARG", + "D", + "COM812", # missing-trailing-comma + "ISC001", # single-line-implicit-string-concatenation + "S113", # request-without-timeout + "PLR2004", # magic-value-comparison + "RUF012", # mutable-class-default +] +select = ["ALL"] + +[lint.flake8-annotations] +allow-star-arg-any = true + +[lint.isort] +known-first-party = ["tap_dummyjson"] + +[lint.pydocstyle] +convention = "google" diff --git a/samples/sample_tap_dummy_json/tap_dummyjson/__init__.py b/samples/sample_tap_dummy_json/tap_dummyjson/__init__.py new file mode 100644 index 000000000..81bcb2663 --- /dev/null +++ b/samples/sample_tap_dummy_json/tap_dummyjson/__init__.py @@ -0,0 +1 @@ +"""Tap for DummyJSON.""" diff --git a/samples/sample_tap_dummy_json/tap_dummyjson/__main__.py b/samples/sample_tap_dummy_json/tap_dummyjson/__main__.py new file mode 100644 index 000000000..a51459e64 --- /dev/null +++ b/samples/sample_tap_dummy_json/tap_dummyjson/__main__.py @@ -0,0 +1,7 @@ +"""DummyJSON entry point.""" + +from __future__ import annotations + +from tap_dummyjson.tap import TapDummyJSON + +TapDummyJSON.cli() diff --git a/samples/sample_tap_dummy_json/tap_dummyjson/auth.py b/samples/sample_tap_dummy_json/tap_dummyjson/auth.py new file mode 100644 index 000000000..4f21f432f --- /dev/null +++ b/samples/sample_tap_dummy_json/tap_dummyjson/auth.py @@ -0,0 +1,72 @@ +import logging +import time + +import requests +from singer_sdk.authenticators import SingletonMeta + +logger = logging.getLogger(__name__) + +EXPIRES_IN_MINS = 30 + + +class DummyJSONAuthenticator(metaclass=SingletonMeta): + def __init__(self, auth_url, refresh_token_url, username, password): + self.auth_url = auth_url + self.refresh_token_url = refresh_token_url + self.username = username + self.password = password + + self.token = None + self.refresh_token = None + + self.expires = 0 + + def __call__(self, request): + if not self.refresh_token: + logger.info("Retrieving token") + self.auth() + request.headers["Authorization"] = f"Bearer {self.token}" + return request + + if self.needs_refresh(): + logger.info("Refreshing token") + self.refresh() + request.headers["Authorization"] = f"Bearer {self.token}" + return request + + return request + + def needs_refresh(self) -> bool: + return True if self.expires is None else self.expires < time.time() + + def _handle_response(self, response): + if response.status_code != 200: + logger.error("Error: %s", response.text) + response.raise_for_status() + + data = response.json() + self.token = data["token"] + self.refresh_token = data["refreshToken"] + self.expires = time.time() + EXPIRES_IN_MINS * 60 + logger.info("Authenticated") + + def refresh(self): + response = requests.post( + self.refresh_token_url, + json={ + "refreshToken": self.refresh_token, + "expiresInMins": EXPIRES_IN_MINS, + }, + ) + self._handle_response(response) + + def auth(self): + response = requests.post( + self.auth_url, + json={ + "username": self.username, + "password": self.password, + "expiresInMins": EXPIRES_IN_MINS, + }, + ) + self._handle_response(response) diff --git a/samples/sample_tap_dummy_json/tap_dummyjson/client.py b/samples/sample_tap_dummy_json/tap_dummyjson/client.py new file mode 100644 index 000000000..c946675f3 --- /dev/null +++ b/samples/sample_tap_dummy_json/tap_dummyjson/client.py @@ -0,0 +1,45 @@ +"""REST client handling, including DummyJSONStream base class.""" + +from __future__ import annotations + +from singer_sdk.pagination import BaseOffsetPaginator +from singer_sdk.streams import RESTStream + +from .auth import DummyJSONAuthenticator + +PAGE_SIZE = 25 + + +class DummyJSONStream(RESTStream): + """DummyJSON stream class.""" + + records_jsonpath: str = ... + + @property + def url_base(self): + return self.config["api_url"] + + @property + def authenticator(self): + return DummyJSONAuthenticator( + auth_url=f"{self.url_base}/auth/login", + refresh_token_url=f"{self.url_base}/refresh", + username=self.config["username"], + password=self.config["password"], + ) + + @property + def http_headers(self): + return {"User-Agent": "tap-dummyjson"} + + def get_new_paginator(self): + return BaseOffsetPaginator(start_value=0, page_size=PAGE_SIZE) + + def get_url_params(self, context, next_page_token): + return { + "skip": next_page_token, + "limit": PAGE_SIZE, + } + + def post_process(self, row, context=None): + return row diff --git a/samples/sample_tap_dummy_json/tap_dummyjson/schemas/__init__.py b/samples/sample_tap_dummy_json/tap_dummyjson/schemas/__init__.py new file mode 100644 index 000000000..06c0a1988 --- /dev/null +++ b/samples/sample_tap_dummy_json/tap_dummyjson/schemas/__init__.py @@ -0,0 +1 @@ +"""JSON schema files for the REST API.""" diff --git a/samples/sample_tap_dummy_json/tap_dummyjson/streams.py b/samples/sample_tap_dummy_json/tap_dummyjson/streams.py new file mode 100644 index 000000000..b9867bf84 --- /dev/null +++ b/samples/sample_tap_dummy_json/tap_dummyjson/streams.py @@ -0,0 +1,67 @@ +from singer_sdk import typing as th + +from .client import DummyJSONStream + + +class Products(DummyJSONStream): + """Define custom stream.""" + + name = "products" + path = "/products" + + records_jsonpath = "$.products[*]" + + primary_keys = ["id"] + + replication_key = None + + schema = th.PropertiesList( + th.Property("id", th.IntegerType), + th.Property("title", th.StringType), + th.Property("description", th.StringType), + th.Property("category", th.StringType), + th.Property("price", th.NumberType), + th.Property("discountPercentage", th.NumberType), + th.Property("rating", th.NumberType), + th.Property("stock", th.NumberType), + th.Property("tags", th.ArrayType(th.StringType)), + th.Property("brand", th.StringType), + th.Property("sku", th.StringType), + th.Property("weight", th.NumberType), + th.Property( + "dimensions", + th.ObjectType( + th.Property("width", th.NumberType), + th.Property("height", th.NumberType), + th.Property("depth", th.NumberType), + ), + ), + th.Property("warrantyInformation", th.StringType), + th.Property("shippingInformation", th.StringType), + th.Property("availabilityStatus", th.StringType), + th.Property( + "reviews", + th.ArrayType( + th.ObjectType( + th.Property("rating", th.NumberType), + th.Property("comment", th.StringType), + th.Property("date", th.DateTimeType), + th.Property("reviewerName", th.StringType), + th.Property("reviewerEmail", th.StringType), + ) + ), + ), + th.Property("returnPolicy", th.StringType), + th.Property("minimumOrderQuantity", th.NumberType), + th.Property( + "meta", + th.ObjectType( + th.Property("createdAt", th.DateTimeType), + th.Property("updatedAt", th.DateTimeType), + th.Property("barcode", th.StringType), + th.Property("qrCode", th.StringType), + ), + ), + th.Property("images", th.ArrayType(th.StringType)), + th.Property("thumbnail", th.StringType), + ).to_dict() diff --git a/samples/sample_tap_dummy_json/tap_dummyjson/tap.py b/samples/sample_tap_dummy_json/tap_dummyjson/tap.py new file mode 100644 index 000000000..b352d0172 --- /dev/null +++ b/samples/sample_tap_dummy_json/tap_dummyjson/tap.py @@ -0,0 +1,51 @@ +from singer_sdk import Tap +from singer_sdk import typing as th # JSON schema typing helpers + +from . import streams + + +class TapDummyJSON(Tap): + """DummyJSON tap class.""" + + name = "tap-dummyjson" + + config_jsonschema = th.PropertiesList( + th.Property( + "username", + th.StringType, + required=True, + description="Username for the API service", + ), + th.Property( + "password", + th.StringType, + required=True, + secret=True, # Flag config as protected. + description="Password for the API service", + ), + th.Property( + "start_date", + th.DateTimeType, + description="The earliest record date to sync", + ), + th.Property( + "api_url", + th.StringType, + default="https://dummyjson.com", + description="The base url for the API service", + ), + ).to_dict() + + def discover_streams(self): + """Return a list of discovered streams. + + Returns: + A list of discovered streams. + """ + return [ + streams.Products(self), + ] + + +if __name__ == "__main__": + TapDummyJSON.cli() diff --git a/samples/sample_tap_dummy_json/tests/__init__.py b/samples/sample_tap_dummy_json/tests/__init__.py new file mode 100644 index 000000000..1c65c2337 --- /dev/null +++ b/samples/sample_tap_dummy_json/tests/__init__.py @@ -0,0 +1 @@ +"""Test suite for tap-dummyjson.""" diff --git a/samples/sample_tap_dummy_json/tests/test_core.py b/samples/sample_tap_dummy_json/tests/test_core.py new file mode 100644 index 000000000..a9f180afc --- /dev/null +++ b/samples/sample_tap_dummy_json/tests/test_core.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from samples.sample_tap_dummy_json.tap_dummyjson.tap import TapDummyJSON +from singer_sdk.testing import get_tap_test_class + +CONFIG = { + "username": "emilys", + "password": "emilyspass", +} + +TestTapDummyJSON = get_tap_test_class(tap_class=TapDummyJSON, config=CONFIG) diff --git a/samples/sample_tap_dummy_json/tox.ini b/samples/sample_tap_dummy_json/tox.ini new file mode 100644 index 000000000..6be1c116a --- /dev/null +++ b/samples/sample_tap_dummy_json/tox.ini @@ -0,0 +1,19 @@ +# This file can be used to customize tox tests as well as other test frameworks like flake8 and mypy + +[tox] +envlist = py{38,39,310,311,312} +isolated_build = true + +[testenv] +allowlist_externals = poetry +commands = + poetry install -v + poetry run pytest + +[testenv:pytest] +# Run the python tests. +# To execute, run `tox -e pytest` +envlist = py{38,39,310,311,312} +commands = + poetry install -v + poetry run pytest diff --git a/samples/sample_tap_google_analytics/__init__.py b/samples/sample_tap_google_analytics/__init__.py deleted file mode 100644 index dbe8aec91..000000000 --- a/samples/sample_tap_google_analytics/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Google Analytics sample.""" - -from __future__ import annotations diff --git a/samples/sample_tap_google_analytics/__main__.py b/samples/sample_tap_google_analytics/__main__.py deleted file mode 100644 index e274833e6..000000000 --- a/samples/sample_tap_google_analytics/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from samples.sample_tap_google_analytics.ga_tap import SampleTapGoogleAnalytics - -SampleTapGoogleAnalytics.cli() diff --git a/samples/sample_tap_google_analytics/ga_tap.py b/samples/sample_tap_google_analytics/ga_tap.py deleted file mode 100644 index 1528452e5..000000000 --- a/samples/sample_tap_google_analytics/ga_tap.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Sample tap test for tap-google-analytics.""" - -from __future__ import annotations - -import json -from pathlib import Path - -from samples.sample_tap_google_analytics.ga_tap_stream import ( - GASimpleSampleStream, - SampleGoogleAnalyticsStream, -) -from singer_sdk.tap_base import Tap -from singer_sdk.typing import PropertiesList, Property, StringType - -REPORT_DEFS_FILE = ( - "samples/sample_tap_google_analytics/resources/default_report_definitions.json" -) -REPORT_DEFS = json.loads(Path(REPORT_DEFS_FILE).read_text(encoding="utf-8")) - - -class SampleTapGoogleAnalytics(Tap): - """Sample tap for GoogleAnalytics.""" - - name: str = "sample-tap-google-analytics" - config_jsonschema = PropertiesList( - Property("view_id", StringType(), required=True), - Property( - "client_email", - StringType(), - required=True, - examples=["me@example.com"], - ), - Property("private_key", StringType(), required=True, secret=True), - ).to_dict() - - def discover_streams(self) -> list[SampleGoogleAnalyticsStream]: - """Return a list of all streams.""" - return [GASimpleSampleStream(tap=self)] diff --git a/samples/sample_tap_google_analytics/ga_tap_stream.py b/samples/sample_tap_google_analytics/ga_tap_stream.py deleted file mode 100644 index 75cdb4c47..000000000 --- a/samples/sample_tap_google_analytics/ga_tap_stream.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Sample tap stream test for tap-google-analytics.""" - -from __future__ import annotations - -import datetime -import sys -import typing as t - -from singer_sdk.authenticators import OAuthJWTAuthenticator -from singer_sdk.streams import RESTStream - -if sys.version_info < (3, 9): - import importlib_resources -else: - from importlib import resources as importlib_resources - - -GOOGLE_OAUTH_ENDPOINT = "https://oauth2.googleapis.com/token" -GA_OAUTH_SCOPES = "https://www.googleapis.com/auth/analytics.readonly" -SCHEMAS_DIR = importlib_resources.files(__package__) / "schemas" - - -class GoogleJWTAuthenticator(OAuthJWTAuthenticator): - """Class responsible for Google Auth via JWT and OAuth.""" - - @property - def client_id(self) -> str: - """Override since Google auth uses email, not numeric client ID.""" - return t.cast(str, self.config["client_email"]) - - -class SampleGoogleAnalyticsStream(RESTStream): - """Sample tap test for google-analytics.""" - - url_base = "https://analyticsreporting.googleapis.com/v4" - path = "/reports:batchGet" - rest_method = "POST" - - # Child class overrides: - dimensions: tuple[str] = () - metrics: tuple[str] = () - - @property - def authenticator(self) -> GoogleJWTAuthenticator: - """Return authenticator for Google Analytics.""" - return GoogleJWTAuthenticator( - stream=self, - auth_endpoint=GOOGLE_OAUTH_ENDPOINT, - oauth_scopes=GA_OAUTH_SCOPES, - ) - - def prepare_request_payload( - self, - context: dict | None, # noqa: ARG002 - next_page_token: t.Any | None, # noqa: ARG002 - ) -> dict | None: - """Prepare the data payload for the REST API request.""" - request_def = { - "viewId": self.config["view_id"], - "metrics": [{"expression": m} for m in self.metrics], - "dimensions": [{"name": d} for d in self.dimensions], - } - if self.config.get("start_date"): - request_def["dateRanges"] = [ - { - "startDate": self.config.get("start_date"), - "endDate": datetime.datetime.now(datetime.timezone.utc), - }, - ] - return {"reportRequests": [request_def]} - - def parse_response(self, response) -> t.Iterable[dict]: - """Parse Google Analytics API response into individual records.""" - self.logger.info( - "Received raw Google Analytics query response: %s", - response.json(), - ) - report_data = response.json().get("reports", [{}])[0].get("data") - if not report_data: - self.logger.info( - "Received empty Google Analytics query response: %s", - response.json(), - ) - for total in report_data["totals"]: - yield {"totals": total["values"]} - - -class GASimpleSampleStream(SampleGoogleAnalyticsStream): - """A super simple sample report.""" - - name = "simple_sample" - schema_filepath = SCHEMAS_DIR / "simple-sample.json" - - dimensions = ("ga:date",) - metrics = ("ga:users", "ga:sessions") diff --git a/samples/sample_tap_google_analytics/resources/default_report_definitions.json b/samples/sample_tap_google_analytics/resources/default_report_definitions.json deleted file mode 100644 index 33978e57f..000000000 --- a/samples/sample_tap_google_analytics/resources/default_report_definitions.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "project_id": { - "type": "integer" - }, - "short_id": { - "type": "string" - }, - "title": { - "type": [ - "null", - "string" - ] - }, - "author_name": { - "type": [ - "null", - "string" - ] - }, - "author_email": { - "type": [ - "null", - "string" - ] - }, - "authored_date": { - "type": "string", - "format": "date-time" - }, - "committer_name": { - "type": [ - "null", - "string" - ] - }, - "committer_email": { - "type": [ - "null", - "string" - ] - }, - "committed_date": { - "type": "string", - "format": "date-time" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "message": { - "type": [ - "null", - "string" - ] - }, - "allow_failure": { - "type": [ - "null", - "boolean" - ] - }, - "parent_ids": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "null" - } - ] - }, - "stats": { - "type": "object", - "properties": { - "additions": { - "type": "integer" - }, - "deletions": { - "type": "integer" - }, - "total": { - "type": "integer" - } - } - } - } -} \ No newline at end of file diff --git a/samples/sample_tap_google_analytics/schemas/simple-sample.json b/samples/sample_tap_google_analytics/schemas/simple-sample.json deleted file mode 100644 index 0a5f97f4d..000000000 --- a/samples/sample_tap_google_analytics/schemas/simple-sample.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "object", - "properties": { - "totals": { - "type": "array", - "items": { - "type": "string" - } - } - } -} diff --git a/tests/external/test_tap_dummyjson.py b/tests/external/test_tap_dummyjson.py new file mode 100644 index 000000000..a9f180afc --- /dev/null +++ b/tests/external/test_tap_dummyjson.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from samples.sample_tap_dummy_json.tap_dummyjson.tap import TapDummyJSON +from singer_sdk.testing import get_tap_test_class + +CONFIG = { + "username": "emilys", + "password": "emilyspass", +} + +TestTapDummyJSON = get_tap_test_class(tap_class=TapDummyJSON, config=CONFIG) diff --git a/tests/external/test_tap_google_analytics.py b/tests/external/test_tap_google_analytics.py deleted file mode 100644 index 1bed825fd..000000000 --- a/tests/external/test_tap_google_analytics.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Tests standard tap features using the built-in SDK tests library.""" - -from __future__ import annotations - -import warnings - -from samples.sample_tap_google_analytics.ga_tap import SampleTapGoogleAnalytics -from singer_sdk.exceptions import ConfigValidationError -from singer_sdk.testing import get_tap_test_class - -from .conftest import ga_config - -try: - TestSampleTapGoogleAnalytics = get_tap_test_class( - tap_class=SampleTapGoogleAnalytics, - config=ga_config(), - parse_env_config=True, - ) -except ConfigValidationError as e: - warnings.warn( - UserWarning( - "Could not configure external gitlab tests. " - f"Config in CI is expected via env vars.\n{e}", - ), - stacklevel=2, - ) From 119a5ecb03fac892e5fd33abf9c370325b39416e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:34:51 -0600 Subject: [PATCH 09/20] chore: Update `meltano.yml` in templates (#2550) --- .../{{cookiecutter.mapper_id}}/meltano.yml | 12 +++++++-- .../{{cookiecutter.mapper_id}}/pyproject.toml | 4 +-- .../{{cookiecutter.tap_id}}/meltano.yml | 24 ++++++++++++++--- .../{{cookiecutter.tap_id}}/pyproject.toml | 6 ++--- .../{{cookiecutter.target_id}}/meltano.yml | 26 ++++++++++++++----- .../{{cookiecutter.target_id}}/pyproject.toml | 6 ++--- 6 files changed, 58 insertions(+), 20 deletions(-) diff --git a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/meltano.yml b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/meltano.yml index 019015d06..7ee8496d7 100644 --- a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/meltano.yml +++ b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/meltano.yml @@ -13,18 +13,26 @@ plugins: streams: - stream_name: animals input_filename: https://raw.githubusercontent.com/meltano/tap-smoke-test/main/demo-data/animals-data.jsonl + loaders: - name: target-jsonl variant: andyh1203 pip_url: target-jsonl + mappers: - name: "{{cookiecutter.mapper_id}}" - pip_url: -e . namespace: "{{cookiecutter.library_name}}" - # TODO: replace these with the actual settings + pip_url: -e . + + # TODO: Declare settings and their types here: settings: - name: example_config kind: string + label: Example Config + description: An example configuration setting + + # TODO: Declare mapping instances here: + # https://docs.meltano.com/guide/mappers/#example-1 mappings: - name: example config: diff --git a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/pyproject.toml b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/pyproject.toml index 544e2c986..bbf1fe9d7 100644 --- a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/pyproject.toml +++ b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/pyproject.toml @@ -5,7 +5,7 @@ name = "{{cookiecutter.variant}}-{{cookiecutter.mapper_id}}" name = "{{cookiecutter.mapper_id}}" {%- endif %} version = "0.0.1" -description = "`{{cookiecutter.mapper_id}}` is a Singer mapper {{cookiecutter.name}}, built with the Meltano Singer SDK." +description = "Singer mapper {{cookiecutter.name}}, built with the Meltano Singer SDK." readme = "README.md" authors = ["{{ cookiecutter.admin_name }} <{{ cookiecutter.admin_email }}>"] keywords = [ @@ -35,7 +35,7 @@ singer-sdk = { version="~=0.38.0"{{ ', extras = ["faker"]' if cookiecutter.faker fs-s3fs = { version = "~=1.1.1", optional = true } [tool.poetry.group.dev.dependencies] -pytest = ">=7.4.0" +pytest = ">=8" singer-sdk = { version="~=0.38.0", extras = ["testing"] } [tool.poetry.extras] diff --git a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/meltano.yml b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/meltano.yml index c4f2ba480..8855ef34b 100644 --- a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/meltano.yml +++ b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/meltano.yml @@ -15,16 +15,32 @@ plugins: - discover - about - stream-maps - config: - start_date: '2010-01-01T00:00:00Z' + + # TODO: Declare settings and their types here: settings: - # TODO: To configure using Meltano, declare settings and their types here: - name: username + label: Username + description: The username to use for authentication + - name: password kind: password + label: Password + description: The password to use for authentication sensitive: true + - name: start_date - value: '2010-01-01T00:00:00Z' + kind: date_iso8601 + label: Start Date + description: Initial date to start extracting data from + + # TODO: Declare required settings here: + settings_group_validation: + - [username, password] + + # TODO: Declare default configuration values here: + config: + start_date: '2010-01-01T00:00:00Z' + loaders: - name: target-jsonl variant: andyh1203 diff --git a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml index 7b51f4302..ef383b016 100644 --- a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml +++ b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml @@ -5,7 +5,7 @@ name = "{{cookiecutter.variant}}-{{cookiecutter.tap_id}}" name = "{{cookiecutter.tap_id}}" {%- endif %} version = "0.0.1" -description = "`{{cookiecutter.tap_id}}` is a Singer tap for {{cookiecutter.source_name}}, built with the Meltano Singer SDK." +description = "Singer tap for {{cookiecutter.source_name}}, built with the Meltano Singer SDK." readme = "README.md" authors = ["{{ cookiecutter.admin_name }} <{{ cookiecutter.admin_email }}>"] keywords = [ @@ -37,11 +37,11 @@ singer-sdk = { version="~=0.38.0", extras = [ ] } fs-s3fs = { version = "~=1.1.1", optional = true } {%- if cookiecutter.stream_type in ["REST", "GraphQL"] %} -requests = "~=2.32.0" +requests = "~=2.32.3" {%- endif %} [tool.poetry.group.dev.dependencies] -pytest = ">=7.4.0" +pytest = ">=8" {%- if cookiecutter.auth_method == "JWT" %} singer-sdk = { version="~=0.38.0", extras = ["jwt", "testing"] } {%- else %} diff --git a/cookiecutter/target-template/{{cookiecutter.target_id}}/meltano.yml b/cookiecutter/target-template/{{cookiecutter.target_id}}/meltano.yml index c63837370..dab8bf213 100644 --- a/cookiecutter/target-template/{{cookiecutter.target_id}}/meltano.yml +++ b/cookiecutter/target-template/{{cookiecutter.target_id}}/meltano.yml @@ -5,7 +5,15 @@ default_environment: test environments: - name: test plugins: - extractors: [] + extractors: + - name: tap-smoke-test + variant: meltano + pip_url: git+https://github.com/meltano/tap-smoke-test.git + config: + streams: + - stream_name: animals + input_filename: https://raw.githubusercontent.com/meltano/tap-smoke-test/main/demo-data/animals-data.jsonl + loaders: - name: "{{cookiecutter.target_id}}" namespace: "{{cookiecutter.library_name}}" @@ -14,13 +22,19 @@ plugins: - about - stream-maps - record-flattening - config: - start_date: '2010-01-01T00:00:00Z' + + # TODO: Declare settings and their types here: settings: - # TODO: To configure using Meltano, declare settings and their types here: - name: username + label: Username + description: The username to use for authentication + - name: password kind: password + label: Password + description: The password to use for authentication sensitive: true - - name: start_date - value: '2010-01-01T00:00:00Z' + + # TODO: Declare required settings here: + settings_group_validation: + - [username, password] diff --git a/cookiecutter/target-template/{{cookiecutter.target_id}}/pyproject.toml b/cookiecutter/target-template/{{cookiecutter.target_id}}/pyproject.toml index 6fa2615d3..429fb672a 100644 --- a/cookiecutter/target-template/{{cookiecutter.target_id}}/pyproject.toml +++ b/cookiecutter/target-template/{{cookiecutter.target_id}}/pyproject.toml @@ -5,7 +5,7 @@ name = "{{cookiecutter.variant}}-{{cookiecutter.target_id}}" name = "{{cookiecutter.target_id}}" {%- endif %} version = "0.0.1" -description = "`{{cookiecutter.target_id}}` is a Singer target for {{cookiecutter.destination_name}}, built with the Meltano Singer SDK." +description = "Singer target for {{cookiecutter.destination_name}}, built with the Meltano Singer SDK." readme = "README.md" authors = ["{{ cookiecutter.admin_name }} <{{ cookiecutter.admin_email }}>"] keywords = [ @@ -33,11 +33,11 @@ python = ">=3.8" singer-sdk = { version="~=0.38.0"{{ ', extras = ["faker"]' if cookiecutter.faker_extra }} } fs-s3fs = { version = "~=1.1.1", optional = true } {%- if cookiecutter.serialization_method != "SQL" %} -requests = "~=2.32.0" +requests = "~=2.32.3" {%- endif %} [tool.poetry.dev-dependencies] -pytest = ">=7.4.0" +pytest = ">=8" singer-sdk = { version="~=0.38.0", extras = ["testing"] } [tool.poetry.extras] From 5b4f41aa35800ad10d60acf4992ac2ea61f012e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:06:51 -0600 Subject: [PATCH 10/20] fix: Use mapped stream aliases when handling `ACTIVATE_VERSION` messages in the base target class (#2554) Closes https://github.com/meltano/sdk/issues/1055 --- singer_sdk/target_base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/singer_sdk/target_base.py b/singer_sdk/target_base.py index d560a555e..8ecdfa851 100644 --- a/singer_sdk/target_base.py +++ b/singer_sdk/target_base.py @@ -451,8 +451,10 @@ def _process_activate_version_message(self, message_dict: dict) -> None: message_dict: TODO """ stream_name = message_dict["stream"] - sink = self.get_sink(stream_name) - sink.activate_version(message_dict["version"]) + + for stream_map in self.mapper.stream_maps[stream_name]: + sink = self.get_sink(stream_map.stream_alias) + sink.activate_version(message_dict["version"]) def _process_batch_message(self, message_dict: dict) -> None: """Handle the optional BATCH message extension. From 4cafb6fdecf38fd3af76a97e4ed0bdbf8612e7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:00:02 -0600 Subject: [PATCH 11/20] chore: Split settings, python versions and capabilities into their own lines in plain about output (#2555) --- singer_sdk/about.py | 75 ++++++++++++---------- singer_sdk/plugin_base.py | 8 ++- tests/core/test_about.py | 1 + tests/snapshots/about_format/text.snap.txt | 24 ++++++- 4 files changed, 69 insertions(+), 39 deletions(-) diff --git a/singer_sdk/about.py b/singer_sdk/about.py index 87f83a6ef..d1228b43c 100644 --- a/singer_sdk/about.py +++ b/singer_sdk/about.py @@ -67,6 +67,7 @@ class AboutInfo: capabilities: list[CapabilitiesEnum] settings: dict + env_var_prefix: str class AboutFormatter(abc.ABC): @@ -118,17 +119,33 @@ def format_about(self, about_info: AboutInfo) -> str: # noqa: PLR6301 Returns: A formatted string. """ - return dedent( + output = dedent( f"""\ Name: {about_info.name} Description: {about_info.description} Version: {about_info.version} - SDK Version: {about_info.sdk_version} - Supported Python Versions: {about_info.supported_python_versions} - Capabilities: {about_info.capabilities} - Settings: {about_info.settings}""", + SDK Version: {about_info.sdk_version}""" ) + if about_info.supported_python_versions: + output += "\nSupport Python Versions:\n" + output += "\n".join( + [f" - {v}" for v in about_info.supported_python_versions] + ) + + output += "\nSupport Python Versions:\n" + output += "\n".join([f" - {c}" for c in about_info.capabilities]) + + output += "\nSettings:\n" + for setting, schema in about_info.settings.get("properties", {}).items(): + env_var = about_info.env_var_prefix + setting.upper().replace("-", "_") + json_type = schema.get("type") + output += f" - Name: {setting}\n" + output += f" Type: {json_type}\n" + output += f" Environment Variable: {env_var}\n" + + return output + class JSONFormatter(AboutFormatter, format_name="json"): """About formatter for JSON output.""" @@ -227,43 +244,34 @@ def format_about(self, about_info: AboutInfo) -> str: Returns: A formatted string. """ - # Empty list for string parts - md_list = [] - - # Iterate over Dict to set md - md_list.append( - f"# `{about_info.name}`\n\n" - f"{about_info.description}\n\n" - f"Built with the [Meltano Singer SDK](https://sdk.meltano.com).\n\n", - ) - - # Process capabilities and settings - - capabilities = "## Capabilities\n\n" - capabilities += "\n".join([f"* `{v}`" for v in about_info.capabilities]) - capabilities += "\n\n" - md_list.append(capabilities) + # Header + output = dedent(f"""\ + # `{about_info.name}`\n + {about_info.description}\n + Built with the [Meltano Singer SDK](https://sdk.meltano.com).\n + """) + + # Process capabilities + output += "## Capabilities\n\n" + output += "\n".join([f"* `{v}`" for v in about_info.capabilities]) + output += "\n\n" # Process Supported Python Versions - if about_info.supported_python_versions: - supported_python_versions = "## Supported Python Versions\n\n" - supported_python_versions += "\n".join( + output += "## Supported Python Versions\n\n" + output += "\n".join( [f"* {v}" for v in about_info.supported_python_versions], ) - supported_python_versions += "\n\n" - md_list.append(supported_python_versions) + output += "\n\n" # Process settings - - setting = "## Settings\n\n" - settings_table = ( + output += "## Settings\n\n" + output += ( "| Setting | Required | Default | Description |\n" "|:--------|:--------:|:-------:|:------------|\n" ) - settings_table += "\n".join(self._generate_property_rows(about_info.settings)) - setting += settings_table - setting += ( + output += "\n".join(self._generate_property_rows(about_info.settings)) + output += ( "\n\n" + "\n".join( [ @@ -273,6 +281,5 @@ def format_about(self, about_info: AboutInfo) -> str: ) + "\n" ) - md_list.append(setting) - return "".join(md_list) + return output diff --git a/singer_sdk/plugin_base.py b/singer_sdk/plugin_base.py index b88559088..f709fed95 100644 --- a/singer_sdk/plugin_base.py +++ b/singer_sdk/plugin_base.py @@ -229,6 +229,10 @@ def capabilities(self) -> list[CapabilitiesEnum]: # noqa: PLR6301 PluginCapabilities.BATCH, ] + @classproperty + def _env_var_prefix(cls) -> str: # noqa: N805 + return f"{cls.name.upper().replace('-', '_')}_" + @classproperty def _env_var_config(cls) -> dict[str, t.Any]: # noqa: N805 """Return any config specified in environment variables. @@ -239,11 +243,10 @@ def _env_var_config(cls) -> dict[str, t.Any]: # noqa: N805 Returns: Dictionary of configuration parsed from the environment. """ - plugin_env_prefix = f"{cls.name.upper().replace('-', '_')}_" config_jsonschema = cls.config_jsonschema cls.append_builtin_config(config_jsonschema) - return parse_environment_config(config_jsonschema, plugin_env_prefix) + return parse_environment_config(config_jsonschema, cls._env_var_prefix) # Core plugin metadata: @@ -428,6 +431,7 @@ def _get_about_info(cls: type[PluginBase]) -> about.AboutInfo: supported_python_versions=cls.get_supported_python_versions(), capabilities=cls.capabilities, settings=config_jsonschema, + env_var_prefix=cls._env_var_prefix, ) @classmethod diff --git a/tests/core/test_about.py b/tests/core/test_about.py index a25543ca2..70a445bb1 100644 --- a/tests/core/test_about.py +++ b/tests/core/test_about.py @@ -66,6 +66,7 @@ def about_info() -> AboutInfo: }, "required": ["api_key"], }, + env_var_prefix="TAP_EXAMPLE_", ) diff --git a/tests/snapshots/about_format/text.snap.txt b/tests/snapshots/about_format/text.snap.txt index 06a3649c7..028f0541b 100644 --- a/tests/snapshots/about_format/text.snap.txt +++ b/tests/snapshots/about_format/text.snap.txt @@ -2,6 +2,24 @@ Name: tap-example Description: Example tap for Singer SDK Version: 0.1.1 SDK Version: 1.0.0 -Supported Python Versions: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] -Capabilities: [catalog, discover, state] -Settings: {'properties': {'start_date': {'type': 'string', 'format': 'date-time', 'description': 'Start date for the tap to extract data from.'}, 'api_key': {'type': 'string', 'description': 'API key for the tap to use.'}, 'complex_setting': {'type': 'object', 'description': 'A complex setting, with sub-settings.', 'properties': {'sub_setting': {'type': 'string', 'description': 'A sub-setting.'}}}}, 'required': ['api_key']} \ No newline at end of file +Support Python Versions: + - 3.8 + - 3.9 + - 3.10 + - 3.11 + - 3.12 + - 3.13 +Support Python Versions: + - catalog + - discover + - state +Settings: + - Name: start_date + Type: string + Environment Variable: TAP_EXAMPLE_START_DATE + - Name: api_key + Type: string + Environment Variable: TAP_EXAMPLE_API_KEY + - Name: complex_setting + Type: object + Environment Variable: TAP_EXAMPLE_COMPLEX_SETTING From 4ace75aaf5f6062d0db63f069cdd7067d6dc6465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:13:25 -0600 Subject: [PATCH 12/20] docs: Reference state partitioning in stream partitioning page (#2556) * docs: Reference state partitioning in stream partitioning page * Fix extension --- docs/partitioning.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/partitioning.md b/docs/partitioning.md index 95103dc45..144d210cf 100644 --- a/docs/partitioning.md +++ b/docs/partitioning.md @@ -3,6 +3,10 @@ The Tap SDK supports stream partitioning, meaning a set of substreams which each have their own state and their own distinct queryable domain. +You can read more about state partitioning in the +[State Implemetation](./implementation/state.md#partitioned-state) explanation +document. + ## If you do not require partitioning In general, developers can simply ignore the [`context`](./context_object.md) arguments From 9c97d4a37d2fb6a6f58d272b388ad5773d80d448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:45:06 -0600 Subject: [PATCH 13/20] docs: Document that `get_starting_timestamp` requires setting a non-null replication_key (#2557) * docs: Document that `get_starting_timestamp` requires setting a non-null replication_key * Use `note` admonition * Reference as attribute --- singer_sdk/streams/core.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/singer_sdk/streams/core.py b/singer_sdk/streams/core.py index c588a9729..ed9052ae0 100644 --- a/singer_sdk/streams/core.py +++ b/singer_sdk/streams/core.py @@ -249,6 +249,11 @@ def get_starting_replication_key_value( Returns: Starting replication value. + + .. note:: + + This method requires :attr:`~singer_sdk.Stream.replication_key` to be set + to a non-null value, indicating the stream should be synced incrementally. """ state = self.get_context_state(context) @@ -280,6 +285,11 @@ def get_starting_timestamp( Raises: ValueError: If the replication value is not a valid timestamp. + + .. note:: + + This method requires :attr:`~singer_sdk.Stream.replication_key` to be set + to a non-null value, indicating the stream should be synced incrementally. """ value = self.get_starting_replication_key_value(context) From e40603fe6f2a19be0c5bd9d4502854cf8fd9500c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 06:36:43 -0600 Subject: [PATCH 14/20] chore(deps): bump cryptography from 42.0.8 to 43.0.0 (#2561) Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.8 to 43.0.0. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/42.0.8...43.0.0) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 76 +++++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/poetry.lock b/poetry.lock index df017be89..79fc38607 100644 --- a/poetry.lock +++ b/poetry.lock @@ -464,43 +464,38 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "42.0.8" +version = "43.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = true python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, - {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, - {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, - {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, - {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, - {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, - {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, - {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, + {file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"}, + {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"}, + {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"}, + {file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"}, + {file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"}, + {file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"}, + {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"}, + {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"}, + {file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"}, + {file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"}, + {file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"}, ] [package.dependencies] @@ -513,7 +508,7 @@ nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -1464,6 +1459,19 @@ files = [ {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, {file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"}, {file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"}, + {file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, + {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, + {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, ] [package.dependencies] From 11b3197b68f77a27e4e1a90b0e81560c0946a60b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 06:41:01 -0600 Subject: [PATCH 15/20] chore(deps): bump the runtime-dependencies group with 2 updates (#2560) Bumps the runtime-dependencies group with 2 updates: [sphinx](https://github.com/sphinx-doc/sphinx) and [pytest](https://github.com/pytest-dev/pytest). Updates `sphinx` from 7.4.6 to 7.4.7 - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.4.6...v7.4.7) Updates `pytest` from 8.2.2 to 8.3.1 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.2.2...8.3.1) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: runtime-dependencies - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-minor dependency-group: runtime-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 79fc38607..96b87d6ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1524,13 +1524,13 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pytest" -version = "8.2.2" +version = "8.3.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, + {file = "pytest-8.3.1-py3-none-any.whl", hash = "sha256:e9600ccf4f563976e2c99fa02c7624ab938296551f280835ee6516df8bc4ae8c"}, + {file = "pytest-8.3.1.tar.gz", hash = "sha256:7e8e5c5abd6e93cb1cc151f23e57adc31fcf8cfd2a3ff2da63e23f732de35db6"}, ] [package.dependencies] @@ -1538,7 +1538,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] @@ -2074,13 +2074,13 @@ files = [ [[package]] name = "sphinx" -version = "7.4.6" +version = "7.4.7" description = "Python documentation generator" optional = true python-versions = ">=3.9" files = [ - {file = "sphinx-7.4.6-py3-none-any.whl", hash = "sha256:915760d6188288a1e30c2cd0d9fa31b1b009bc6e6019cc0c32d16c77d20e86d9"}, - {file = "sphinx-7.4.6.tar.gz", hash = "sha256:116918d455c493fff3178edea12b4fe1c1e4894680fd81e7b7431ea21d47ca52"}, + {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, + {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, ] [package.dependencies] From 48344385715d1f04d56a594e7c7bf2d58e68d855 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 06:43:24 -0600 Subject: [PATCH 16/20] chore(deps-dev): bump the development-dependencies group with 2 updates (#2559) Bumps the development-dependencies group with 2 updates: [deptry](https://github.com/fpgmaas/deptry) and [mypy](https://github.com/python/mypy). Updates `deptry` from 0.16.2 to 0.17.0 - [Release notes](https://github.com/fpgmaas/deptry/releases) - [Changelog](https://github.com/fpgmaas/deptry/blob/main/CHANGELOG.md) - [Commits](https://github.com/fpgmaas/deptry/compare/0.16.2...0.17.0) Updates `mypy` from 1.10.1 to 1.11.0 - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.10.1...v1.11) --- updated-dependencies: - dependency-name: deptry dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 82 ++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96b87d6ec..79afc20d3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -513,22 +513,22 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "deptry" -version = "0.16.2" +version = "0.17.0" description = "A command line utility to check for unused, missing and transitive dependencies in a Python project." optional = false python-versions = ">=3.8" files = [ - {file = "deptry-0.16.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:24bfbae07bd6533c852c795e8d88d05a8ad0801bec0d3662e1a37db763c52540"}, - {file = "deptry-0.16.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc881688a2eaeafe51c0617d32a6535057bccdb74559cc667109f48f81cd976e"}, - {file = "deptry-0.16.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fed4b692f556e4c80acb42cec93e3b5fdc7fc2323049c2a0cfd9dfc4a9c7033e"}, - {file = "deptry-0.16.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ec508a932d8f06c3bd1aa7a4548d5dbec92c3060d42eedcda3be9729bd7c3b"}, - {file = "deptry-0.16.2-cp38-abi3-win_amd64.whl", hash = "sha256:eb92e9aacde66cfe001d6318eb0851ae0ca26fea441defed4765a47644daf8bb"}, - {file = "deptry-0.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dfdceca2fbc87f4bce04df4207914a5eb37e67fb2107579ad2e88107c22d2456"}, - {file = "deptry-0.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:96ab62dd5f4658735aac72d0e49f6d896eabf50a0e4e2cdecb436a1362aa696b"}, - {file = "deptry-0.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e4408fa5a8d146b55bc40f0829fb875efef33174a2679bd9954ce988b9bc0d7"}, - {file = "deptry-0.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af976afc2a0583f48dc25f616d2566fecd7af5080675c8eccb161def88d93503"}, - {file = "deptry-0.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd86c9d34aa75b91fb72b34110f0660b2277bf9a95fe9cae3ead36d465bc44ac"}, - {file = "deptry-0.16.2.tar.gz", hash = "sha256:f0f752cf6f5e9f7445a79fcf195b772cd2d4b889cd260e23867dd8013caa74c1"}, + {file = "deptry-0.17.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ddd05503cbae9cce608003bc50691cb2a6d714a9da30bc16a99116eedad5a0c2"}, + {file = "deptry-0.17.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:31af1dd2f83bddb6cf5abc9f37a86f8ca4b8572fda971a4e7eb0d552a727f454"}, + {file = "deptry-0.17.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0588827e36f4822517fc66308a85428780e15bbce819e2216d0a5d010edd1998"}, + {file = "deptry-0.17.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce0eb1408aae315fa757fc9877101079ea6b2ebcae18b261e5d3e0141ba517b2"}, + {file = "deptry-0.17.0-cp38-abi3-win_amd64.whl", hash = "sha256:d102754cd1f4ba2ed599fccaec54acb6be56bd00e8d03384d0a2bcb8ba8141e1"}, + {file = "deptry-0.17.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1753b8807c3da82637beb6a0b32df85fea73bcc33a31bcda2087487bd92c336e"}, + {file = "deptry-0.17.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f34309d3c2f28c459f2e55d93b67c81950cb863e1b210788f3491ab973e42f53"}, + {file = "deptry-0.17.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac6b569c9623e41f1a18f722ddf8422ca7b0d5f718f9d6c71bc9dfcd9e28cf5d"}, + {file = "deptry-0.17.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de2feebecb256ccee69b0f8144c678763d7842704959239fa7e7f3fc60f8a1"}, + {file = "deptry-0.17.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e4724e014c0787452962833cc3030170d267fbd3ac34f6c09b8449d8e8147f39"}, + {file = "deptry-0.17.0.tar.gz", hash = "sha256:f48a71bab8f46a896fe507c8be5f2b50bb9bab0c44e4dfad00afe87e9a08c14b"}, ] [package.dependencies] @@ -1096,44 +1096,44 @@ files = [ [[package]] name = "mypy" -version = "1.10.1" +version = "1.11.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, - {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, - {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, - {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, - {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, - {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, - {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, - {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, - {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, - {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, - {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, - {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, - {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, - {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, - {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, - {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, - {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, - {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, - {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, - {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, - {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, - {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, - {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, - {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, - {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, - {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, - {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, + {file = "mypy-1.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229"}, + {file = "mypy-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287"}, + {file = "mypy-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6"}, + {file = "mypy-1.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be"}, + {file = "mypy-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00"}, + {file = "mypy-1.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb"}, + {file = "mypy-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1"}, + {file = "mypy-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3"}, + {file = "mypy-1.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d"}, + {file = "mypy-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a"}, + {file = "mypy-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20"}, + {file = "mypy-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba"}, + {file = "mypy-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd"}, + {file = "mypy-1.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d"}, + {file = "mypy-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2"}, + {file = "mypy-1.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850"}, + {file = "mypy-1.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac"}, + {file = "mypy-1.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9"}, + {file = "mypy-1.11.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7"}, + {file = "mypy-1.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf"}, + {file = "mypy-1.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095"}, + {file = "mypy-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe"}, + {file = "mypy-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c"}, + {file = "mypy-1.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13"}, + {file = "mypy-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac"}, + {file = "mypy-1.11.0-py3-none-any.whl", hash = "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace"}, + {file = "mypy-1.11.0.tar.gz", hash = "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] From 1bccf98d2f89b9e75f5a89e64050d01256e2db0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:55:26 -0600 Subject: [PATCH 17/20] chore: Update dependabot.yml (#2562) --- .github/dependabot.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 658c04fdc..4b12bfa9b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,7 @@ updates: interval: weekly time: "12:00" reviewers: [meltano/engineering] - labels: [deps] + labels: [Dependencies] groups: development-dependencies: dependency-type: development @@ -21,7 +21,7 @@ updates: interval: weekly time: "12:00" reviewers: [meltano/engineering] - labels: [deps] + labels: [Dependencies] groups: ci: patterns: @@ -31,7 +31,7 @@ updates: schedule: interval: weekly reviewers: [meltano/engineering] - labels: [deps] + labels: [Dependencies] groups: actions: patterns: From e0197cd4da6ce343ab17f4153cfea3aa1f1ec2ce Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 18:21:13 -0600 Subject: [PATCH 18/20] chore: pre-commit autoupdate (#2563) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.2 → v0.5.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.2...v0.5.4) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- singer_sdk/typing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ebf5e849c..2be705072 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: - id: check-readthedocs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.2 + rev: v0.5.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/singer_sdk/typing.py b/singer_sdk/typing.py index ca4d917df..908850686 100644 --- a/singer_sdk/typing.py +++ b/singer_sdk/typing.py @@ -612,7 +612,7 @@ class Property(JSONTypeHelper[T], t.Generic[T]): """Generic Property. Should be nested within a `PropertiesList`.""" # TODO: Make some of these arguments keyword-only. This is a breaking change. - def __init__( # noqa: PLR0913 + def __init__( self, name: str, wrapped: JSONTypeHelper[T] | type[JSONTypeHelper[T]], From 078cffddfa8ee514ff740fca035ddac03dc37ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:55:47 -0400 Subject: [PATCH 19/20] refactor: Make `SQLSink` a generic with a `SQLConnector` type parameter (#2564) --- samples/sample_target_sqlite/__init__.py | 2 +- singer_sdk/sinks/sql.py | 12 +++++++----- tests/conftest.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/samples/sample_target_sqlite/__init__.py b/samples/sample_target_sqlite/__init__.py index 07e5172aa..d296c5fb0 100644 --- a/samples/sample_target_sqlite/__init__.py +++ b/samples/sample_target_sqlite/__init__.py @@ -26,7 +26,7 @@ def get_sqlalchemy_url(self, config: dict[str, t.Any]) -> str: # noqa: PLR6301 return f"sqlite:///{config[DB_PATH_CONFIG]}" -class SQLiteSink(SQLSink): +class SQLiteSink(SQLSink[SQLiteConnector]): """The Sink class for SQLite. This class allows developers to optionally override `get_records()` and other diff --git a/singer_sdk/sinks/sql.py b/singer_sdk/sinks/sql.py index 9b8823f27..33a741614 100644 --- a/singer_sdk/sinks/sql.py +++ b/singer_sdk/sinks/sql.py @@ -23,11 +23,13 @@ from singer_sdk.target_base import Target +_C = t.TypeVar("_C", bound=SQLConnector) -class SQLSink(BatchSink): + +class SQLSink(BatchSink, t.Generic[_C]): """SQL-type sink type.""" - connector_class: type[SQLConnector] + connector_class: type[_C] soft_delete_column_name = "_sdc_deleted_at" version_column_name = "_sdc_table_version" @@ -37,7 +39,7 @@ def __init__( stream_name: str, schema: dict, key_properties: t.Sequence[str] | None, - connector: SQLConnector | None = None, + connector: _C | None = None, ) -> None: """Initialize SQL Sink. @@ -48,12 +50,12 @@ def __init__( key_properties: The primary key columns. connector: Optional connector to reuse. """ - self._connector: SQLConnector + self._connector: _C self._connector = connector or self.connector_class(dict(target.config)) super().__init__(target, stream_name, schema, key_properties) @property - def connector(self) -> SQLConnector: + def connector(self) -> _C: """The connector object. Returns: diff --git a/tests/conftest.py b/tests/conftest.py index d2961722f..0b5bc4b74 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -138,7 +138,7 @@ class SQLConnectorMock(SQLConnector): """A Mock SQLConnector class.""" -class SQLSinkMock(SQLSink): +class SQLSinkMock(SQLSink[SQLConnectorMock]): """A mock Sink class.""" name = "sql-sink-mock" From 433b7fe614ea8587e8b76784e6289b852a742dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:42:57 -0400 Subject: [PATCH 20/20] chore: Make it easier to switch Singer reader and writer IO implementations in plugin classes (#2565) --- singer_sdk/_singerlib/encoding/__init__.py | 6 +++--- singer_sdk/_singerlib/encoding/_simple.py | 4 ++-- singer_sdk/_singerlib/messages.py | 2 +- singer_sdk/io_base.py | 16 +++++++++++++++- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/singer_sdk/_singerlib/encoding/__init__.py b/singer_sdk/_singerlib/encoding/__init__.py index 2d64c54dc..65ed5b767 100644 --- a/singer_sdk/_singerlib/encoding/__init__.py +++ b/singer_sdk/_singerlib/encoding/__init__.py @@ -1,12 +1,12 @@ from __future__ import annotations from ._base import GenericSingerReader, GenericSingerWriter, SingerMessageType -from ._simple import SingerReader, SingerWriter +from ._simple import SimpleSingerReader, SimpleSingerWriter __all__ = [ "GenericSingerReader", "GenericSingerWriter", + "SimpleSingerReader", + "SimpleSingerWriter", "SingerMessageType", - "SingerReader", - "SingerWriter", ] diff --git a/singer_sdk/_singerlib/encoding/_simple.py b/singer_sdk/_singerlib/encoding/_simple.py index 5bfb242fb..7ce148fc3 100644 --- a/singer_sdk/_singerlib/encoding/_simple.py +++ b/singer_sdk/_singerlib/encoding/_simple.py @@ -194,7 +194,7 @@ def __post_init__(self) -> None: self.type = SingerMessageType.ACTIVATE_VERSION -class SingerReader(GenericSingerReader[str]): +class SimpleSingerReader(GenericSingerReader[str]): """Base class for all plugins reading Singer messages as strings from stdin.""" default_input = sys.stdin @@ -219,7 +219,7 @@ def deserialize_json(self, line: str) -> dict: # noqa: PLR6301 raise InvalidInputLine(msg) from exc -class SingerWriter(GenericSingerWriter[str, Message]): +class SimpleSingerWriter(GenericSingerWriter[str, Message]): """Interface for all plugins writing Singer messages to stdout.""" def serialize_message(self, message: Message) -> str: # noqa: PLR6301 diff --git a/singer_sdk/_singerlib/messages.py b/singer_sdk/_singerlib/messages.py index 640219de6..271c3bea3 100644 --- a/singer_sdk/_singerlib/messages.py +++ b/singer_sdk/_singerlib/messages.py @@ -2,7 +2,7 @@ from __future__ import annotations -from .encoding import SingerWriter +from .encoding import SimpleSingerWriter as SingerWriter from .encoding._base import SingerMessageType from .encoding._simple import ( ActivateVersionMessage, diff --git a/singer_sdk/io_base.py b/singer_sdk/io_base.py index f9041beea..f24188b4e 100644 --- a/singer_sdk/io_base.py +++ b/singer_sdk/io_base.py @@ -2,4 +2,18 @@ from __future__ import annotations -from singer_sdk._singerlib.encoding import * # noqa: F403 +from singer_sdk._singerlib.encoding import ( + GenericSingerReader, + GenericSingerWriter, + SingerMessageType, +) +from singer_sdk._singerlib.encoding import SimpleSingerReader as SingerReader +from singer_sdk._singerlib.encoding import SimpleSingerWriter as SingerWriter + +__all__ = [ + "GenericSingerReader", + "GenericSingerWriter", + "SingerMessageType", + "SingerReader", + "SingerWriter", +]