diff --git a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.pre-commit-config.yaml b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.pre-commit-config.yaml index 62f5ab41da..5a684f0887 100644 --- a/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.pre-commit-config.yaml +++ b/cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/.pre-commit-config.yaml @@ -14,13 +14,13 @@ repos: - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.27.3 + rev: 0.27.4 hooks: - id: check-dependabot - id: check-github-workflows - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.11 + rev: v0.1.15 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/.pre-commit-config.yaml b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/.pre-commit-config.yaml index 640e62aa2d..6d35b8875a 100644 --- a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/.pre-commit-config.yaml +++ b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/.pre-commit-config.yaml @@ -18,13 +18,13 @@ repos: - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.27.3 + rev: 0.27.4 hooks: - id: check-dependabot - id: check-github-workflows - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.14 + rev: v0.1.15 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml index a48b5f5510..4b37fd3fc1 100644 --- a/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml +++ b/cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml @@ -31,15 +31,29 @@ packages = [ [tool.poetry.dependencies] python = ">=3.8" importlib-resources = { version = "==6.1.*", python = "<3.9" } -singer-sdk = { version="~=0.34.1"{{ ', extras = ["faker"]' if cookiecutter.faker_extra }} } fs-s3fs = { version = "~=1.1.1", optional = true } {%- if cookiecutter.stream_type in ["REST", "GraphQL"] %} requests = "~=2.31.0" {%- endif %} +[tool.poetry.dependencies.singer-sdk] +version = "~=0.34.1" +extras = [ + {%- if cookiecutter.auth_method == "JWT" %} + "crypto", + {%- endif %} + {%- if cookiecutter.faker_extra %} + "faker", + {%- endif %} +] + [tool.poetry.group.dev.dependencies] pytest = ">=7.4.0" +{%- if cookiecutter.auth_method == "JWT" %} +singer-sdk = { version="~=0.34.1", extras = ["crypto", "testing"] } +{%- else %} singer-sdk = { version="~=0.34.1", extras = ["testing"] } +{%- endif %} [tool.poetry.extras] s3 = ["fs-s3fs"] diff --git a/cookiecutter/target-template/{{cookiecutter.target_id}}/.pre-commit-config.yaml b/cookiecutter/target-template/{{cookiecutter.target_id}}/.pre-commit-config.yaml index c89f290a1a..3922d67480 100644 --- a/cookiecutter/target-template/{{cookiecutter.target_id}}/.pre-commit-config.yaml +++ b/cookiecutter/target-template/{{cookiecutter.target_id}}/.pre-commit-config.yaml @@ -14,13 +14,13 @@ repos: - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.27.3 + rev: 0.27.4 hooks: - id: check-dependabot - id: check-github-workflows - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.11 + rev: v0.1.15 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix, --show-fixes] diff --git a/docs/dev_guide.md b/docs/dev_guide.md index af198e9b89..9b56eb7e22 100644 --- a/docs/dev_guide.md +++ b/docs/dev_guide.md @@ -17,7 +17,7 @@ Create taps with the SDK requires overriding just two or three classes: `http_headers` property in the stream class. - `OAuthAuthenticator` - This class performs an OAuth 2.0 authentication flow. - `OAuthJWTAuthenticator` - This class performs an JWT (JSON Web Token) authentication - flow. + flow. Requires installing the `singer-sdk[crypto]` extra. ## Target Development Overview @@ -181,6 +181,7 @@ Some APIs instead return the records as values inside an object where each key i The following [extra features](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#extras) are available for the Singer SDK: +- `crypto` - Enables the `OAuthJWTAuthenticator` class for JWT (JSON Web Token) authentication. - `faker` - Enables the use of [Faker](https://faker.readthedocs.io/en/master/) in [stream maps](stream_maps.md). - `s3` - Enables AWS S3 as a [BATCH storage](batch.md#the-batch-message). - `parquet` - Enables as [BATCH encoding](batch.md#encoding). diff --git a/noxfile.py b/noxfile.py index 875e6670d7..972093857c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -65,7 +65,7 @@ def _clean_py312_deps(session: Session, dependencies: list[str]) -> None: def mypy(session: Session) -> None: """Check types with mypy.""" args = session.posargs or ["singer_sdk"] - session.install(".[faker,parquet,s3,testing]") + session.install(".[crypto,faker,parquet,s3,testing]") session.install( "exceptiongroup", "mypy", @@ -87,7 +87,7 @@ def mypy(session: Session) -> None: def tests(session: Session) -> None: """Execute pytest tests and compute coverage.""" _clean_py312_deps(session, test_dependencies) - session.install(".[faker,parquet,s3]") + session.install(".[crypto,faker,parquet,s3]") session.install(*test_dependencies) sqlalchemy_version = os.environ.get("SQLALCHEMY_VERSION") @@ -121,7 +121,7 @@ def tests(session: Session) -> None: def benches(session: Session) -> None: """Run benchmarks.""" _clean_py312_deps(session, test_dependencies) - session.install(".[s3]") + session.install(".[crypto,s3]") session.install(*test_dependencies) sqlalchemy_version = os.environ.get("SQLALCHEMY_VERSION") if sqlalchemy_version: @@ -144,7 +144,7 @@ def update_snapshots(session: Session) -> None: args = session.posargs or ["-m", "snapshot"] _clean_py312_deps(session, test_dependencies) - session.install(".[faker]") + session.install(".[crypto,faker]") session.install(*test_dependencies) session.run("pytest", "--snapshot-update", *args) diff --git a/poetry.lock b/poetry.lock index 0d923c7675..6d5950e624 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2626,6 +2626,7 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.link testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [extras] +crypto = ["cryptography"] docs = ["furo", "myst-parser", "sphinx", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinx-reredirects"] faker = ["faker"] parquet = ["numpy", "numpy", "pyarrow"] @@ -2635,4 +2636,4 @@ testing = ["pytest", "pytest-durations"] [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "cf326e02c7b02fbe1d12a80b3d16a4485357799c06585b3a36b025ad02bde08f" +content-hash = "ad2aa7cac11f4058e50b76515ccd2f8490607f73f76ad352cb6aec9b38a0bc7e" diff --git a/pyproject.toml b/pyproject.toml index 4bafb308d9..29dd7b1888 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,9 @@ pytest-durations = {version = ">=1.2.0", optional = true} faker = {version = "~=22.5", optional = true} [tool.poetry.extras] +crypto = [ + "cryptography", +] docs = [ "sphinx", "furo", @@ -175,7 +178,7 @@ prerelease_offset = 1 tag_format = "v$major.$minor.$patch$prerelease" version_files = [ "docs/conf.py:^release =", - "cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml:singer-sdk", + "cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml:version", "cookiecutter/target-template/{{cookiecutter.target_id}}/pyproject.toml:singer-sdk", "cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/pyproject.toml:singer-sdk", ".github/ISSUE_TEMPLATE/bug.yml:^ placeholder:", diff --git a/singer_sdk/authenticators.py b/singer_sdk/authenticators.py index 82e0556bc3..3098539a1a 100644 --- a/singer_sdk/authenticators.py +++ b/singer_sdk/authenticators.py @@ -11,8 +11,6 @@ import jwt import requests -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import serialization from singer_sdk.helpers._util import utc_now @@ -564,6 +562,9 @@ def oauth_request_payload(self) -> dict: Raises: ValueError: If the private key is not set. """ + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import serialization + if not self.private_key: msg = "Missing 'private_key' property for OAuth payload." raise ValueError(msg)