From 2c6cafa47b16353cbe80a3fc8d9698fa3e6407ce Mon Sep 17 00:00:00 2001 From: Reuben Frankel Date: Tue, 5 Mar 2024 23:04:30 +0000 Subject: [PATCH 1/4] Use pre-commit for linting/formatting --- .pre-commit-config.yaml | 53 ++-- Makefile | 19 +- poetry.lock | 522 ++++++++-------------------------------- pyproject.toml | 72 ++++-- 4 files changed, 185 insertions(+), 481 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b96a5c6..d2661e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,38 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks +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: v3.2.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files - - repo: local - hooks: - - id: lint - name: Lint - language: system - entry: make lint - pass_filenames: false +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.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.27.3 + hooks: + - id: check-dependabot + - id: check-github-workflows + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.14 + 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.8.0 + hooks: + - id: mypy + additional_dependencies: + - types-requests diff --git a/Makefile b/Makefile index a4cb0f3..2620f4b 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,15 @@ -.PHONY: help init lint lint-fix test +.PHONY: help init lint test help: @echo AVAILABLE COMMANDS @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-23s\033[0m%s\n", $$1, $$2}' init: ## Initialise repo for local development - @poetry install -v - @! test -d ~/.cache/pre-commit || poetry run pre-commit clean - @poetry run pre-commit install -f --install-hooks + @poetry install --sync --with dev + @poetry run pre-commit install --install-hooks -lint: ## Lint source files - poetry run autoflake --check --recursive --exclude tests --remove-all-unused-imports --remove-duplicate-keys --remove-unused-variables --verbose tap_spotify - poetry run isort --check --diff tap_spotify - poetry run black --check --diff tap_spotify - poetry run flake8 --max-complexity 10 tap_spotify - -lint-fix: ## Lint source files and fix any issues - poetry run autoflake --in-place --recursive --exclude tests --remove-all-unused-imports --remove-duplicate-keys --remove-unused-variables --verbose tap_spotify - poetry run isort tap_spotify - poetry run black tap_spotify +lint: ## Lint files + poetry run pre-commit run ruff test: ## Run tests @poetry run pytest diff --git a/poetry.lock b/poetry.lock index 5786070..043c6af 100644 --- a/poetry.lock +++ b/poetry.lock @@ -29,21 +29,6 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib- tests = ["attrs[tests-no-zope]", "zope-interface"] tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -[[package]] -name = "autoflake" -version = "1.7.8" -description = "Removes unused imports and unused variables" -optional = false -python-versions = ">=3.7" -files = [ - {file = "autoflake-1.7.8-py3-none-any.whl", hash = "sha256:46373ef69b6714f5064c923bb28bd797c4f8a9497f557d87fc36665c6d956b39"}, - {file = "autoflake-1.7.8.tar.gz", hash = "sha256:e7e46372dee46fa1c97acf310d99d922b63d369718a270809d7c278d34a194cf"}, -] - -[package.dependencies] -pyflakes = ">=1.1.0,<3" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - [[package]] name = "backoff" version = "2.2.1" @@ -124,62 +109,46 @@ files = [ tzdata = ["tzdata"] [[package]] -name = "black" -version = "24.2.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" +name = "boto3" +version = "1.34.56" +description = "The AWS SDK for Python" +optional = true +python-versions = ">= 3.8" files = [ - {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, - {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, - {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, - {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, - {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, - {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, - {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, - {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, - {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, - {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, - {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, - {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, - {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, - {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, - {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, - {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, - {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, - {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, - {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, - {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, - {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, - {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, + {file = "boto3-1.34.56-py3-none-any.whl", hash = "sha256:300888f0c1b6f32f27f85a9aa876f50f46514ec619647af7e4d20db74d339714"}, + {file = "boto3-1.34.56.tar.gz", hash = "sha256:b26928f9a21cf3649cea20a59061340f3294c6e7785ceb6e1a953eb8010dc3ba"}, ] [package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} +botocore = ">=1.34.56,<1.35.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.10.0,<0.11.0" [package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] -name = "cachetools" -version = "5.3.3" -description = "Extensible memoizing collections and decorators" -optional = false -python-versions = ">=3.7" +name = "botocore" +version = "1.34.56" +description = "Low-level, data-driven core of boto 3." +optional = true +python-versions = ">= 3.8" files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, + {file = "botocore-1.34.56-py3-none-any.whl", hash = "sha256:fff66e22a5589c2d58fba57d1d95c334ce771895e831f80365f6cff6453285ec"}, + {file = "botocore-1.34.56.tar.gz", hash = "sha256:bffeb71ab21d47d4ecf947d9bdb2fbd1b0bbd0c27742cea7cf0b77b701c41d9f"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = [ + {version = ">=1.25.4,<1.27", markers = "python_version < \"3.10\""}, + {version = ">=1.25.4,<2.1", markers = "python_version >= \"3.10\""}, ] +[package.extras] +crt = ["awscrt (==0.19.19)"] + [[package]] name = "certifi" version = "2023.7.22" @@ -267,28 +236,6 @@ files = [ [package.dependencies] pycparser = "*" -[[package]] -name = "cfgv" -version = "3.3.1" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] - -[[package]] -name = "chardet" -version = "5.2.0" -description = "Universal encoding detector for Python 3" -optional = false -python-versions = ">=3.7" -files = [ - {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, - {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, -] - [[package]] name = "charset-normalizer" version = "3.3.1" @@ -467,17 +414,6 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] -[[package]] -name = "distlib" -version = "0.3.7" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, -] - [[package]] name = "exceptiongroup" version = "1.2.0" @@ -492,38 +428,6 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "filelock" -version = "3.13.1" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] - -[[package]] -name = "flake8" -version = "5.0.4" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" - [[package]] name = "fs" version = "2.4.16" @@ -543,6 +447,22 @@ six = ">=1.10,<2.0" [package.extras] scandir = ["scandir (>=1.5,<2.0)"] +[[package]] +name = "fs-s3fs" +version = "1.1.1" +description = "Amazon S3 filesystem for PyFilesystem2" +optional = true +python-versions = "*" +files = [ + {file = "fs-s3fs-1.1.1.tar.gz", hash = "sha256:b57f8c7664460ff7b451b4b44ca2ea9623a374d74e1284c2d5e6df499dc7976c"}, + {file = "fs_s3fs-1.1.1-py2.py3-none-any.whl", hash = "sha256:9ba160eaa93390cc5992a857675666cb2fbb3721b872474dfdc659a715c39280"}, +] + +[package.dependencies] +boto3 = ">=1.9,<2.0" +fs = ">=2.4,<3.0" +six = ">=1.10,<2.0" + [[package]] name = "greenlet" version = "3.0.0" @@ -618,20 +538,6 @@ files = [ docs = ["Sphinx"] test = ["objgraph", "psutil"] -[[package]] -name = "identify" -version = "2.5.24" -description = "File identification library for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, - {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, -] - -[package.extras] -license = ["ukkonen"] - [[package]] name = "idna" version = "3.4" @@ -663,21 +569,21 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", [[package]] name = "importlib-resources" -version = "5.12.0" +version = "6.1.2" description = "Read resources from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, + {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"}, + {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "inflection" @@ -702,22 +608,16 @@ files = [ ] [[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = true +python-versions = ">=3.7" files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] -[package.dependencies] -colorama = {version = ">=0.4.6", optional = true, markers = "extra == \"colors\""} - -[package.extras] -colors = ["colorama (>=0.4.6)"] - [[package]] name = "joblib" version = "1.3.2" @@ -764,89 +664,6 @@ pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy" -version = "1.8.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, -] - -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" version = "23.2" @@ -858,17 +675,6 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] -[[package]] -name = "pathspec" -version = "0.11.2" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, -] - [[package]] name = "pendulum" version = "3.0.0" @@ -981,21 +787,6 @@ files = [ {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, ] -[[package]] -name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] - [[package]] name = "pluggy" version = "1.4.0" @@ -1022,35 +813,6 @@ files = [ {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, ] -[[package]] -name = "pre-commit" -version = "3.5.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, - {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pycodestyle" -version = "2.9.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, - {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, -] - [[package]] name = "pycparser" version = "2.21" @@ -1062,34 +824,6 @@ files = [ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] -[[package]] -name = "pydocstyle" -version = "6.3.0" -description = "Python docstring style checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, - {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, -] - -[package.dependencies] -snowballstemmer = ">=2.2.0" - -[package.extras] -toml = ["tomli (>=1.2.3)"] - -[[package]] -name = "pyflakes" -version = "2.5.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, -] - [[package]] name = "pyjwt" version = "2.8.0" @@ -1107,25 +841,6 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] -[[package]] -name = "pyproject-api" -version = "1.6.1" -description = "API to interact with the python pyproject.toml based projects" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, - {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, -] - -[package.dependencies] -packaging = ">=23.1" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] - [[package]] name = "pyrsistent" version = "0.19.3" @@ -1184,6 +899,20 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-durations" +version = "1.2.0" +description = "Pytest plugin reporting fixtures and test functions execution time." +optional = false +python-versions = ">=3.6.2" +files = [ + {file = "pytest-durations-1.2.0.tar.gz", hash = "sha256:75793f7c2c393a947de4a92cc205e8dcb3d7fcde492628926cca97eb8e87077d"}, + {file = "pytest_durations-1.2.0-py3-none-any.whl", hash = "sha256:210c649d989fdf8e864b7f614966ca2c8be5b58a5224d60089a43618c146d7fb"}, +] + +[package.dependencies] +pytest = ">=4.6" + [[package]] name = "python-dateutil" version = "2.8.2" @@ -1416,6 +1145,23 @@ files = [ {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, ] +[[package]] +name = "s3transfer" +version = "0.10.0" +description = "An Amazon S3 Transfer Manager" +optional = true +python-versions = ">= 3.8" +files = [ + {file = "s3transfer-0.10.0-py3-none-any.whl", hash = "sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e"}, + {file = "s3transfer-0.10.0.tar.gz", hash = "sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b"}, +] + +[package.dependencies] +botocore = ">=1.33.2,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] + [[package]] name = "setuptools" version = "68.0.0" @@ -1576,6 +1322,8 @@ jsonschema = ">=4.16.0" packaging = ">=23.1" pendulum = ">=2.1.0,<4" PyJWT = ">=2.4,<3.0" +pytest = {version = ">=7.2.1", optional = true, markers = "extra == \"docs\" or extra == \"testing\""} +pytest-durations = {version = ">=1.2.0", optional = true, markers = "extra == \"testing\""} python-dateutil = ">=2.8.2" python-dotenv = ">=0.20" PyYAML = ">=6.0" @@ -1605,17 +1353,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -optional = false -python-versions = "*" -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - [[package]] name = "sqlalchemy" version = "2.0.22" @@ -1713,58 +1450,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "tox" -version = "4.13.0" -description = "tox is a generic virtualenv management and test command line tool" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tox-4.13.0-py3-none-any.whl", hash = "sha256:1143c7e2489c68026a55d3d4ae84c02c449f073b28e62f80e3e440a3b72a4afa"}, - {file = "tox-4.13.0.tar.gz", hash = "sha256:dd789a554c16c4b532924ba393c92fc8991323c4b3d466712bfecc8c9b9f24f7"}, -] - -[package.dependencies] -cachetools = ">=5.3.2" -chardet = ">=5.2" -colorama = ">=0.4.6" -filelock = ">=3.13.1" -packaging = ">=23.2" -platformdirs = ">=4.1" -pluggy = ">=1.3" -pyproject-api = ">=1.6.1" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -virtualenv = ">=20.25" - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] -testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"] - -[[package]] -name = "types-requests" -version = "2.31.0.6" -description = "Typing stubs for requests" -optional = false -python-versions = ">=3.7" -files = [ - {file = "types-requests-2.31.0.6.tar.gz", hash = "sha256:cd74ce3b53c461f1228a9b783929ac73a666658f223e28ed29753771477b3bd0"}, - {file = "types_requests-2.31.0.6-py3-none-any.whl", hash = "sha256:a2db9cb228a81da8348b49ad6db3f5519452dd20a9c1e1a868c83c5fe88fd1a9"}, -] - -[package.dependencies] -types-urllib3 = "*" - -[[package]] -name = "types-urllib3" -version = "1.26.25.14" -description = "Typing stubs for urllib3" -optional = false -python-versions = "*" -files = [ - {file = "types-urllib3-1.26.25.14.tar.gz", hash = "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f"}, - {file = "types_urllib3-1.26.25.14-py3-none-any.whl", hash = "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e"}, -] - [[package]] name = "typing-extensions" version = "4.7.1" @@ -1803,26 +1488,6 @@ brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotl secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "virtualenv" -version = "20.25.1" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - [[package]] name = "zipp" version = "3.15.0" @@ -1838,7 +1503,10 @@ files = [ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +[extras] +s3 = ["fs-s3fs"] + [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "ee84621c6eb28b3a8bf62848a75890c0ae99624504a0141d2805f5c52a8fd0cc" +content-hash = "709f0d0d3320ab93612f371951f5f59c5e43dcbd77744b31a4a243e68767f0d6" diff --git a/pyproject.toml b/pyproject.toml index 0665ad7..557b789 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,44 +2,68 @@ name = "tap-spotify" version = "0.5.0" description = "`tap-spotify` is a Singer tap for Spotify, built with the Meltano SDK for Singer Taps." -authors = ["Reuben Frankel"] +readme = "README.md" +authors = ["Reuben Frankel "] keywords = [ "ELT", "Spotify", ] +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" +packages = [ + { include = "tap_spotify" }, +] [tool.poetry.dependencies] python = "^3.8" -requests = "^2.31.0" +importlib-resources = { version = "==6.1.*", python = "<3.9" } singer-sdk = "^0.36.0" +fs-s3fs = { version = "^1.1.1", optional = true } +requests = "^2.31.0" [tool.poetry.group.dev.dependencies] pytest = "^8.1.0" -tox = "^4.13.0" -flake8 = "^5.0.4" -black = "^24.2.0" -pydocstyle = "^6.1.1" -mypy = "^1.8" -types-requests = "^2.25.8" -isort = {extras = ["colors"], version = "^5.13.2"} -pre-commit = "^3.5.0" -autoflake = "^1.7" - -[tool.black] -include = "tap_spotify" -extend-exclude = "tests" -color = "true" - -[tool.isort] -profile = "black" -multi_line_output = 3 # Vertical Hanging Indent -src_paths = "tap_spotify" -extend_skip = "tests" -color_output = "true" +singer-sdk = { version="^0.36.0", extras = ["testing"] } + +[tool.poetry.extras] +s3 = ["fs-s3fs"] + +[tool.mypy] +python_version = "3.11" +warn_unused_configs = true + +[tool.ruff] +src = ["tap_spotify"] +target-version = "py38" + +[tool.ruff.lint] +ignore = [ + "ANN101", # missing-type-self + "ANN102", # missing-type-cls + "COM812", # missing-trailing-comma + "ISC001", # single-line-implicit-string-concatenation +] +select = ["ALL"] + +[tool.ruff.flake8-annotations] +allow-star-arg-any = true + +[tool.ruff.isort] +known-first-party = ["tap_spotify"] + +[tool.ruff.pydocstyle] +convention = "google" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core==1.8.1"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] From eebbe9c453c2bf225d446caeed53bbfabb8b2eb0 Mon Sep 17 00:00:00 2001 From: Reuben Frankel Date: Tue, 5 Mar 2024 23:04:46 +0000 Subject: [PATCH 2/4] Ignore some Ruff rules --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 557b789..495d30d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,13 +46,21 @@ target-version = "py38" [tool.ruff.lint] ignore = [ + "ANN001", # missing-type-function-argument "ANN101", # missing-type-self "ANN102", # missing-type-cls + "ANN201", # missing-return-type-undocumented-public-function + "ANN202", # missing-return-type-private-function "COM812", # missing-trailing-comma "ISC001", # single-line-implicit-string-concatenation ] select = ["ALL"] +[tool.ruff.lint.per-file-ignores] +"tap_spotify/schemas/**/*" = [ + "D101", # undocumented-public-class +] + [tool.ruff.flake8-annotations] allow-star-arg-any = true From 7b83b879848680b4707a0badff2a612d8df9181a Mon Sep 17 00:00:00 2001 From: Reuben Frankel Date: Wed, 6 Mar 2024 01:39:53 +0000 Subject: [PATCH 3/4] Lint/formatting fixes --- tap_spotify/__init__.py | 1 + tap_spotify/auth.py | 5 +- tap_spotify/client.py | 13 +++-- tap_spotify/pagination.py | 6 +++ tap_spotify/schemas/__init__.py | 1 + tap_spotify/schemas/album.py | 2 +- tap_spotify/schemas/artist.py | 2 +- tap_spotify/schemas/audio_features.py | 8 +-- tap_spotify/schemas/external.py | 2 +- tap_spotify/schemas/followers.py | 2 +- tap_spotify/schemas/image.py | 2 +- tap_spotify/schemas/restriction.py | 2 +- tap_spotify/schemas/track.py | 4 +- tap_spotify/schemas/utils/__init__.py | 1 + tap_spotify/schemas/utils/custom_object.py | 22 ++++++--- tap_spotify/schemas/utils/rank.py | 2 +- tap_spotify/schemas/utils/synced_at.py | 2 +- tap_spotify/streams.py | 57 ++++++++++++---------- tap_spotify/tap.py | 3 +- tests/test_core.py | 3 -- 20 files changed, 79 insertions(+), 61 deletions(-) diff --git a/tap_spotify/__init__.py b/tap_spotify/__init__.py index e69de29..27a4f63 100644 --- a/tap_spotify/__init__.py +++ b/tap_spotify/__init__.py @@ -0,0 +1 @@ +"""Tap for Spotify.""" diff --git a/tap_spotify/auth.py b/tap_spotify/auth.py index 620c79d..fd11a0b 100644 --- a/tap_spotify/auth.py +++ b/tap_spotify/auth.py @@ -1,12 +1,14 @@ """Spotify Authentication.""" from singer_sdk.authenticators import OAuthAuthenticator, SingletonMeta +from typing_extensions import Self, override class SpotifyAuthenticator(OAuthAuthenticator, metaclass=SingletonMeta): """Authenticator class for Spotify.""" @property + @override def oauth_request_body(self): return { "grant_type": "refresh_token", @@ -16,7 +18,8 @@ def oauth_request_body(self): } @classmethod - def create_for_stream(cls, stream): + def create_for_stream(cls, stream) -> Self: + """Create authenticator instance for a stream.""" return cls( stream=stream, auth_endpoint="https://accounts.spotify.com/api/token", diff --git a/tap_spotify/client.py b/tap_spotify/client.py index 7e05929..a0545e4 100644 --- a/tap_spotify/client.py +++ b/tap_spotify/client.py @@ -1,11 +1,11 @@ """REST client handling, including SpotifyStream base class.""" -from typing import Iterable, Optional -from urllib.parse import ParseResult, parse_qsl - from functools import cached_property +from typing import Iterable +from urllib.parse import parse_qsl from singer_sdk.streams import RESTStream +from typing_extensions import override from tap_spotify.auth import SpotifyAuthenticator from tap_spotify.pagination import BodyLinkPaginator @@ -19,17 +19,20 @@ class SpotifyStream(RESTStream): chunk_size = None @cached_property + @override def authenticator(self): return SpotifyAuthenticator.create_for_stream(self) + @override def get_new_paginator(self): return BodyLinkPaginator() - def get_url_params(self, context, next_page_token: Optional[ParseResult]): + @override + def get_url_params(self, context, next_page_token): params = super().get_url_params(context, next_page_token) return dict(parse_qsl(next_page_token.query)) if next_page_token else params - def chunk_records(self, records: Iterable[dict]): + def chunk_records(self, records: Iterable[dict]): # noqa: D102 if not self.chunk_size: return [records] diff --git a/tap_spotify/pagination.py b/tap_spotify/pagination.py index ecdfe4d..0647118 100644 --- a/tap_spotify/pagination.py +++ b/tap_spotify/pagination.py @@ -1,7 +1,13 @@ +"""Pagination classes for tap-spotify.""" + from singer_sdk.pagination import BaseHATEOASPaginator +from typing_extensions import override class BodyLinkPaginator(BaseHATEOASPaginator): + """Body `next` link paginator.""" + + @override def get_next_url(self, response): data: dict = response.json() return data.get("next") diff --git a/tap_spotify/schemas/__init__.py b/tap_spotify/schemas/__init__.py index e69de29..37e9fbc 100644 --- a/tap_spotify/schemas/__init__.py +++ b/tap_spotify/schemas/__init__.py @@ -0,0 +1 @@ +"""Schema definitions for tap-spotify.""" diff --git a/tap_spotify/schemas/album.py b/tap_spotify/schemas/album.py index a2ece0f..69d7a6e 100644 --- a/tap_spotify/schemas/album.py +++ b/tap_spotify/schemas/album.py @@ -1,4 +1,4 @@ -"""Schema definitions for album objects""" +"""Schema definitions for album objects.""" from singer_sdk import typing as th diff --git a/tap_spotify/schemas/artist.py b/tap_spotify/schemas/artist.py index 45dd678..e1b9a98 100644 --- a/tap_spotify/schemas/artist.py +++ b/tap_spotify/schemas/artist.py @@ -1,4 +1,4 @@ -"""Schema definitions for artist objects""" +"""Schema definitions for artist objects.""" from singer_sdk import typing as th diff --git a/tap_spotify/schemas/audio_features.py b/tap_spotify/schemas/audio_features.py index c72a14b..59be6d7 100644 --- a/tap_spotify/schemas/audio_features.py +++ b/tap_spotify/schemas/audio_features.py @@ -1,4 +1,4 @@ -"""Schema definitions for audio features objects""" +"""Schema definitions for audio features objects.""" from singer_sdk.typing import ( IntegerType, @@ -12,12 +12,6 @@ class AudioFeaturesObject(CustomObject): - """ - https://developer.spotify.com/documentation/web-api/reference/#/operations/get-audio-features - - https://developer.spotify.com/documentation/web-api/reference/#/operations/get-several-audio-features - """ - properties = PropertiesList( Property("acousticness", NumberType), Property("analysis_url", StringType), diff --git a/tap_spotify/schemas/external.py b/tap_spotify/schemas/external.py index 5e8cdc3..8c77551 100644 --- a/tap_spotify/schemas/external.py +++ b/tap_spotify/schemas/external.py @@ -1,4 +1,4 @@ -"""Schema definitions for external objects""" +"""Schema definitions for external objects.""" from singer_sdk import typing as th diff --git a/tap_spotify/schemas/followers.py b/tap_spotify/schemas/followers.py index 77957a5..52eefd6 100644 --- a/tap_spotify/schemas/followers.py +++ b/tap_spotify/schemas/followers.py @@ -1,4 +1,4 @@ -"""Schema definitions for followers objects""" +"""Schema definitions for followers objects.""" from singer_sdk import typing as th diff --git a/tap_spotify/schemas/image.py b/tap_spotify/schemas/image.py index d0156dd..1f53253 100644 --- a/tap_spotify/schemas/image.py +++ b/tap_spotify/schemas/image.py @@ -1,4 +1,4 @@ -"""Schema definitions for image objects""" +"""Schema definitions for image objects.""" from singer_sdk import typing as th diff --git a/tap_spotify/schemas/restriction.py b/tap_spotify/schemas/restriction.py index 977678b..9374c6f 100644 --- a/tap_spotify/schemas/restriction.py +++ b/tap_spotify/schemas/restriction.py @@ -1,4 +1,4 @@ -"""Schema definitions for restriction objects""" +"""Schema definitions for restriction objects.""" from singer_sdk import typing as th diff --git a/tap_spotify/schemas/track.py b/tap_spotify/schemas/track.py index e966f38..e76a5bf 100644 --- a/tap_spotify/schemas/track.py +++ b/tap_spotify/schemas/track.py @@ -1,4 +1,4 @@ -"""Schema definitions for track objects""" +"""Schema definitions for track objects.""" from singer_sdk import typing as th @@ -23,7 +23,7 @@ class TrackObject(CustomObject): th.Property("id", th.StringType), th.Property("is_local", th.BooleanType), th.Property("is_playable", th.BooleanType), - # th.Property("linked_from", TrackObject), + # th.Property("linked_from", TrackObject), # noqa: ERA001 th.Property("name", th.StringType), th.Property("popularity", th.IntegerType), th.Property("preview_url", th.StringType), diff --git a/tap_spotify/schemas/utils/__init__.py b/tap_spotify/schemas/utils/__init__.py index e69de29..92ee3d6 100644 --- a/tap_spotify/schemas/utils/__init__.py +++ b/tap_spotify/schemas/utils/__init__.py @@ -0,0 +1 @@ +"""Schema utils for tap-spotify.""" diff --git a/tap_spotify/schemas/utils/custom_object.py b/tap_spotify/schemas/utils/custom_object.py index dc7dfe2..4365e2c 100644 --- a/tap_spotify/schemas/utils/custom_object.py +++ b/tap_spotify/schemas/utils/custom_object.py @@ -1,23 +1,31 @@ -"""Base custom object defintion""" +"""Base custom object defintion.""" + +from __future__ import annotations from singer_sdk import typing as th -from singer_sdk.helpers._classproperty import classproperty +from typing_extensions import Self, override + +# ruff: noqa: N805 class CustomObject(th.JSONTypeHelper): + """Custom object.""" + properties: th.PropertiesList - @classproperty + @th.DefaultInstanceProperty + @override def type_dict(cls): return cls.properties.to_dict() - @classproperty - def schema(cls): + @th.DefaultInstanceProperty + def schema(cls): # noqa: D102 return cls.type_dict @classmethod - def extend_with(cls, *extras: "CustomObject"): + def extend_with(cls, *extras: type[Self]) -> type[Self]: + """Extend a custom object schema with other custom object types.""" for e in extras: - for _, p in e.properties.items(): + for _, p in e.properties.items(): # noqa: PERF102 cls.properties.append(p) return cls diff --git a/tap_spotify/schemas/utils/rank.py b/tap_spotify/schemas/utils/rank.py index 632397c..0208013 100644 --- a/tap_spotify/schemas/utils/rank.py +++ b/tap_spotify/schemas/utils/rank.py @@ -1,4 +1,4 @@ -"""Schema definition for rank schema wrapper""" +"""Schema definition for rank schema wrapper.""" from singer_sdk import typing as th diff --git a/tap_spotify/schemas/utils/synced_at.py b/tap_spotify/schemas/utils/synced_at.py index c513f4f..e06a9b0 100644 --- a/tap_spotify/schemas/utils/synced_at.py +++ b/tap_spotify/schemas/utils/synced_at.py @@ -1,4 +1,4 @@ -"""Schema definition for synced at schema wrapper""" +"""Schema definition for synced at schema wrapper.""" from singer_sdk import typing as th diff --git a/tap_spotify/streams.py b/tap_spotify/streams.py index 6bb99a6..abb5616 100644 --- a/tap_spotify/streams.py +++ b/tap_spotify/streams.py @@ -2,10 +2,11 @@ from __future__ import annotations -from datetime import datetime +from datetime import datetime, timezone from typing import Iterable from singer_sdk.streams.rest import RESTStream +from typing_extensions import override from tap_spotify.client import SpotifyStream from tap_spotify.schemas.artist import ArtistObject @@ -16,12 +17,10 @@ class _RankStream(RESTStream): - """Define a rank stream.""" - rank = 1 + @override def post_process(self, row, context): - """Apply rank integer to stream""" row = super().post_process(row, context) row["rank"] = self.rank self.rank += 1 @@ -29,12 +28,10 @@ def post_process(self, row, context): class _SyncedAtStream(RESTStream): - """Define a synced at stream.""" - - synced_at = datetime.utcnow() + synced_at = datetime.now(tz=timezone.utc) + @override def post_process(self, row, context): - """Apply synced at datetime to stream""" row = super().post_process(row, context) row["synced_at"] = self.synced_at return row @@ -49,18 +46,24 @@ class _AudioFeaturesStream(SpotifyStream): schema = AudioFeaturesObject.schema max_tracks = 100 - def __init__(self, tracks_stream: _TracksStream, track_records: Iterable[dict]): - super().__init__(tracks_stream._tap) - - total_tracks = len(track_records) - - if total_tracks > self.max_tracks: - msg = f"Cannot get audio features for more than {self.max_tracks} tracks at a time: {total_tracks} requested" + def __init__( + self, + tracks_stream: _TracksStream, + track_records: Iterable[dict], + ) -> None: + super().__init__(tracks_stream._tap) # noqa: SLF001 + + if total_tracks := len(track_records) > self.max_tracks: + msg = ( + f"Cannot get audio features for more than {self.max_tracks} tracks at a" + f" time: {total_tracks} requested" + ) raise ValueError(msg) self._track_records = track_records - def get_url_params(self, *args, **kwargs): + @override + def get_url_params(self, context, next_page_token): return {"ids": ",".join([track["id"] for track in self._track_records])} @@ -136,14 +139,14 @@ class UserTopTracksShortTermStream( """Define user top tracks short-term stream.""" name = "user_top_tracks_st_stream" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class UserTopTracksMediumTermStream(_UserTopTracksStream): """Define user top tracks medium-term stream.""" name = "user_top_tracks_mt_stream" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class UserTopTracksLongTermStream( @@ -153,7 +156,7 @@ class UserTopTracksLongTermStream( """Define user top tracks long-term stream.""" name = "user_top_tracks_lt_stream" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class UserTopArtistsShortTermStream( @@ -163,14 +166,14 @@ class UserTopArtistsShortTermStream( """Define user top artists short-term stream.""" name = "user_top_artists_st_stream" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class UserTopArtistsMediumTermStream(_UserTopArtistsStream): """Define user top artists medium-term stream.""" name = "user_top_artists_mt_stream" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class UserTopArtistsLongTermStream( @@ -180,7 +183,7 @@ class UserTopArtistsLongTermStream( """Define user top artists long-term stream.""" name = "user_top_artists_lt_stream" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class _PlaylistTracksStream(_RankStream, _SyncedAtStream, _TracksStream): @@ -188,7 +191,7 @@ class _PlaylistTracksStream(_RankStream, _SyncedAtStream, _TracksStream): records_jsonpath = "$.tracks.items[*].track" schema = TrackObject.extend_with(Rank, SyncedAt, AudioFeaturesObject).schema - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") def parse_response(self, response): for track in super().parse_response(response): @@ -201,7 +204,7 @@ class GlobalTopTracksDailyStream(_PlaylistTracksStream): name = "global_top_tracks_daily_stream" path = "/playlists/37i9dQZEVXbMDoHDwVN2tF" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class GlobalTopTracksWeeklyStream(_PlaylistTracksStream): @@ -209,7 +212,7 @@ class GlobalTopTracksWeeklyStream(_PlaylistTracksStream): name = "global_top_tracks_weekly_stream" path = "/playlists/37i9dQZEVXbNG2KDcFcKOF" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class GlobalViralTracksDailyStream(_PlaylistTracksStream): @@ -217,7 +220,7 @@ class GlobalViralTracksDailyStream(_PlaylistTracksStream): name = "global_viral_tracks_daily_stream" path = "/playlists/37i9dQZEVXbLiRSasKsNU9" - primary_keys = ["rank", "synced_at"] + primary_keys = ("rank", "synced_at") class UserSavedTracksStream(_SyncedAtStream, SpotifyStream): @@ -225,7 +228,7 @@ class UserSavedTracksStream(_SyncedAtStream, SpotifyStream): name = "user_saved_tracks_stream" path = "/me/tracks" - primary_keys = ["id", "synced_at"] + primary_keys = ("id", "synced_at") limit = 50 schema = TrackObject.extend_with(SyncedAt).schema records_jsonpath = "$.items[*].track" diff --git a/tap_spotify/tap.py b/tap_spotify/tap.py index 36e573c..46160f1 100644 --- a/tap_spotify/tap.py +++ b/tap_spotify/tap.py @@ -1,8 +1,8 @@ """Spotify tap class.""" - from singer_sdk import Tap from singer_sdk import typing as th +from typing_extensions import override from tap_spotify import streams @@ -48,6 +48,7 @@ class TapSpotify(Tap): ), ).to_dict() + @override def discover_streams(self): return [stream_class(tap=self) for stream_class in STREAM_TYPES] diff --git a/tests/test_core.py b/tests/test_core.py index 73e3043..14c8f6c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -13,6 +13,3 @@ tap_class=TapSpotify, config=SAMPLE_CONFIG, ) - - -# TODO: Create additional tests as appropriate for your tap. From 6bed83678bd4e52cbdbdc6ee0114c51516c1d03a Mon Sep 17 00:00:00 2001 From: Reuben Frankel Date: Wed, 6 Mar 2024 09:23:32 +0000 Subject: [PATCH 4/4] Fix mypy issues --- .pre-commit-config.yaml | 1 + tap_spotify/client.py | 6 ++++-- tap_spotify/streams.py | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d2661e3..ee92e86 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,3 +36,4 @@ repos: - id: mypy additional_dependencies: - types-requests + exclude: tests/ diff --git a/tap_spotify/client.py b/tap_spotify/client.py index a0545e4..d5e4793 100644 --- a/tap_spotify/client.py +++ b/tap_spotify/client.py @@ -1,5 +1,7 @@ """REST client handling, including SpotifyStream base class.""" +from __future__ import annotations + from functools import cached_property from typing import Iterable from urllib.parse import parse_qsl @@ -16,7 +18,7 @@ class SpotifyStream(RESTStream): url_base = "https://api.spotify.com/v1" records_jsonpath = "$.items[*]" - chunk_size = None + chunk_size: int | None @cached_property @override @@ -36,7 +38,7 @@ def chunk_records(self, records: Iterable[dict]): # noqa: D102 if not self.chunk_size: return [records] - chunk = [] + chunk: list[dict] = [] for i, record in enumerate(records): if i and not i % self.chunk_size: diff --git a/tap_spotify/streams.py b/tap_spotify/streams.py index abb5616..4930a11 100644 --- a/tap_spotify/streams.py +++ b/tap_spotify/streams.py @@ -3,7 +3,7 @@ from __future__ import annotations from datetime import datetime, timezone -from typing import Iterable +from typing import Collection from singer_sdk.streams.rest import RESTStream from typing_extensions import override @@ -49,7 +49,7 @@ class _AudioFeaturesStream(SpotifyStream): def __init__( self, tracks_stream: _TracksStream, - track_records: Iterable[dict], + track_records: Collection[dict], ) -> None: super().__init__(tracks_stream._tap) # noqa: SLF001