From 042746f2c8a2000d21ebb86c46ee63f337e72b6a Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 30 Aug 2024 11:07:47 +0200 Subject: [PATCH] chore: migrate to Poetry and update tooling just like in Crawlee (#262) ### Description - Migrate to Poetry for packaging and dependency management. - Update versions of dev tools. - Take some tooling settings from Crawlee. - Drop support for Python 3.8 as it's gonna be deprecated very soon - gonna release this as 1.8.0. ### Issues - Closes: #170 ### Testing - N/A ### Checklist - [x] CI passed --- .github/workflows/check_pr_title.yaml | 14 ++ .github/workflows/integration_tests.yaml | 2 +- .github/workflows/lint_and_type_checks.yaml | 2 +- .github/workflows/release.yaml | 12 +- .github/workflows/unit_tests.yaml | 2 +- .github/workflows/update_new_issue.yaml | 25 +++ .pre-commit-config.yaml | 16 +- CHANGELOG.md | 6 +- Makefile | 57 +++--- mypy.ini | 16 -- pyproject.toml | 191 +++++++++++------- pytest.ini | 3 - renovate.json | 29 ++- scripts/update_version_for_prerelease.py | 3 +- src/apify_client/_utils.py | 2 +- .../clients/resource_clients/dataset.py | 45 +++-- tests/integration/conftest.py | 4 +- 17 files changed, 260 insertions(+), 169 deletions(-) create mode 100644 .github/workflows/check_pr_title.yaml create mode 100644 .github/workflows/update_new_issue.yaml delete mode 100644 mypy.ini delete mode 100644 pytest.ini diff --git a/.github/workflows/check_pr_title.yaml b/.github/workflows/check_pr_title.yaml new file mode 100644 index 00000000..6970d93c --- /dev/null +++ b/.github/workflows/check_pr_title.yaml @@ -0,0 +1,14 @@ +name: Check PR title + +on: + pull_request_target: + types: [opened, edited, synchronize] + +jobs: + check_pr_title: + name: Check PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5.5.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/integration_tests.yaml b/.github/workflows/integration_tests.yaml index f414bae1..eb899f47 100644 --- a/.github/workflows/integration_tests.yaml +++ b/.github/workflows/integration_tests.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12"] max-parallel: 1 # no concurrency on this level, to not overshoot the test user limits steps: diff --git a/.github/workflows/lint_and_type_checks.yaml b/.github/workflows/lint_and_type_checks.yaml index 5f567759..ca410ae4 100644 --- a/.github/workflows/lint_and_type_checks.yaml +++ b/.github/workflows/lint_and_type_checks.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout repository diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 006783b1..373f239f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -109,16 +109,12 @@ jobs: if: steps.get-release-type.outputs.release_type != 'final' run: python ./scripts/update_version_for_prerelease.py ${{ steps.get-release-type.outputs.release_type }} - - # Build a source distribution and a python3-only wheel - name: Build distribution files + # Builds the package. + - name: Build package run: make build - - # Check whether the package description will render correctly on PyPI - name: Check package rendering on PyPI - run: make twine-check - - - # Publish package to PyPI using their official GitHub action - name: Publish package to PyPI + # Publishes the package to PyPI using PyPA official GitHub action with OIDC authentication. + - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - # Tag the current commit with the version tag if this is not made from the release event (releases are tagged with the release process) diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml index afc00e79..8bce48d9 100644 --- a/.github/workflows/unit_tests.yaml +++ b/.github/workflows/unit_tests.yaml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12"] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/update_new_issue.yaml b/.github/workflows/update_new_issue.yaml new file mode 100644 index 00000000..7946544c --- /dev/null +++ b/.github/workflows/update_new_issue.yaml @@ -0,0 +1,25 @@ +name: Update new issue + +on: + issues: + types: + - opened + +jobs: + label_issues: + name: Label issues + runs-on: ubuntu-latest + permissions: + issues: write + + steps: + # Add the "t-tooling" label to all new issues + - uses: actions/github-script@v7 + with: + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["t-tooling"] + }) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2a8466fd..5d342e21 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,12 +7,6 @@ repos: language: system pass_filenames: false - - id: check-async-docstrings - name: Check whether async docstrings are aligned with sync ones - entry: make check-async-docstrings - language: system - pass_filenames: false - - id: type-check name: Type-check codebase entry: make type-check @@ -25,8 +19,14 @@ repos: language: system pass_filenames: false - - id: check-changelog - name: Check whether current version is mentioned in changelog + - id: check-async-docstrings + name: Check whether async docstrings are aligned with sync ones + entry: make check-async-docstrings + language: system + pass_filenames: false + + - id: check-changelog-entry + name: Check changelog entry entry: make check-changelog-entry language: system pass_filenames: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ba7c0d..57383ef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ # Changelog -## [1.7.2](../../releases/tag/v1.7.2) - Unreleased +## [1.8.0](../../releases/tag/v1.8.0) - Unreleased ### Added - add `headers_template` kwarg to webhook create and update - allow passing list of fields to `unwind` parameter in dataset item listing endpoints +### Others + +- drop support for Python 3.8 + ## [1.7.1](../../releases/tag/v1.7.1) - 2024-07-11 ### Fixed diff --git a/Makefile b/Makefile index 5d655cec..3a8994f9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: clean install-dev build publish twine-check lint unit-tests integration-tests type-check check-code format check-async-docstrings fix-async-docstrings check-version-availability check-changelog-entry build-api-reference +.PHONY: clean install-dev build publish-to-pypi lint type-check unit-tests unit-tests-cov integration-tests format check-code check-async-docstrings fix-async-docstrings check-version-availability check-changelog-entry build-api-reference run-doc DIRS_WITH_CODE = src tests scripts @@ -6,54 +6,59 @@ DIRS_WITH_CODE = src tests scripts INTEGRATION_TESTS_CONCURRENCY = 1 clean: - rm -rf build dist .mypy_cache .pytest_cache .ruff_cache src/*.egg-info __pycache__ + rm -rf .mypy_cache .pytest_cache .ruff_cache build dist htmlcov .coverage install-dev: - python3 -m pip install --upgrade pip - pip install --no-cache-dir -e ".[dev]" - pre-commit install + python3 -m pip install --upgrade pip poetry + poetry install --all-extras + poetry run pre-commit install build: - python3 -m build + poetry build --no-interaction -vv -publish: - python3 -m twine upload dist/* - -twine-check: - python3 -m twine check dist/* +# APIFY_PYPI_TOKEN_CRAWLEE is expected to be set in the environment +publish-to-pypi: + poetry config pypi-token.pypi "${APIFY_PYPI_TOKEN_CRAWLEE}" + poetry publish --no-interaction -vv lint: - python3 -m ruff check $(DIRS_WITH_CODE) + poetry run ruff format --check $(DIRS_WITH_CODE) + poetry run ruff check $(DIRS_WITH_CODE) + +type-check: + poetry run mypy $(DIRS_WITH_CODE) unit-tests: - python3 -m pytest --numprocesses=auto --verbose -ra --cov=src/apify_client tests/unit + poetry run pytest --numprocesses=auto --verbose --cov=src/apify_client tests/unit unit-tests-cov: - python3 -m pytest --numprocesses=auto --verbose -ra --cov=src/apify_client --cov-report=html tests/unit + poetry run pytest --numprocesses=auto --verbose --cov=src/apify_client --cov-report=html tests/unit integration-tests: - python3 -m pytest --numprocesses=$(INTEGRATION_TESTS_CONCURRENCY) --verbose -ra tests/integration - -type-check: - python3 -m mypy $(DIRS_WITH_CODE) - -check-code: lint check-async-docstrings type-check unit-tests + poetry run pytest --numprocesses=$(INTEGRATION_TESTS_CONCURRENCY) tests/integration format: - python3 -m ruff check --fix $(DIRS_WITH_CODE) - python3 -m ruff format $(DIRS_WITH_CODE) + poetry run ruff check --fix $(DIRS_WITH_CODE) + poetry run ruff format $(DIRS_WITH_CODE) + +# The check-code target runs a series of checks equivalent to those performed by pre-commit hooks +# and the run_checks.yaml GitHub Actions workflow. +check-code: lint type-check unit-tests check-async-docstrings: - python3 scripts/check_async_docstrings.py + poetry run python scripts/check_async_docstrings.py fix-async-docstrings: - python3 scripts/fix_async_docstrings.py + poetry run python scripts/fix_async_docstrings.py check-version-availability: - python3 scripts/check_version_availability.py + poetry run python scripts/check_version_availability.py check-changelog-entry: - python3 scripts/check_version_in_changelog.py + poetry run python scripts/check_version_in_changelog.py build-api-reference: cd website && ./build_api_reference.sh + +run-doc: build-api-reference + cd website && npm clean-install && npm run start diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index c57367ff..00000000 --- a/mypy.ini +++ /dev/null @@ -1,16 +0,0 @@ -[mypy] -python_version = 3.8 -files = - scripts, - src, - tests -check_untyped_defs = True -disallow_incomplete_defs = True -disallow_untyped_calls = True -disallow_untyped_decorators = True -disallow_untyped_defs = True -no_implicit_optional = True -warn_redundant_casts = True -warn_return_any = True -warn_unreachable = True -warn_unused_ignores = True diff --git a/pyproject.toml b/pyproject.toml index b6b6c52a..8a00c238 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,71 +1,67 @@ -[project] +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] name = "apify_client" -version = "1.7.2" +version = "1.8.0" description = "Apify API client for Python" +authors = ["Apify Technologies s.r.o. "] +license = "Apache-2.0" readme = "README.md" -license = { text = "Apache Software License" } -authors = [{ name = "Apify Technologies s.r.o.", email = "support@apify.com" }] -keywords = ["apify", "api", "client", "scraping", "automation"] - +packages = [{ include = "apify_client", from = "src" }] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "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", "Topic :: Software Development :: Libraries", ] - -requires-python = ">=3.8" - -# We use inclusive ordered comparison clause for non-Apify packages intentionally in order to enhance the Apify client's -# compatibility with a wide range of external packages. This decision was discussed in detail in the following PR: -# https://github.com/apify/apify-sdk-python/pull/154 -dependencies = [ - "apify-shared ~= 1.1.1", - "httpx >= 0.25.0", -] - -[project.optional-dependencies] -dev = [ - "build ~= 1.2.0", - "mypy ~= 1.10.0", - "pre-commit ~= 3.5.0", - "pydoc-markdown ~= 4.8.0", - "pytest ~= 8.2.0", - "pytest-asyncio ~= 0.23.0", - "pytest-cov ~= 5.0.0", - "pytest-only ~= 2.1.0", - "pytest-timeout ~= 2.3.0", - "pytest-xdist ~= 3.6.0", - "redbaron ~= 0.9.0", - "ruff ~= 0.5.0", - "setuptools ~= 70.3.0", # setuptools are used by pytest, but not explicitly required - "twine ~= 5.1.0", +keywords = [ + "apify", + "api", + "client", + "automation", + "crawling", + "scraping", ] -[project.urls] +[tool.poetry.urls] "Homepage" = "https://docs.apify.com/api/client/python/" -"Documentation" = "https://docs.apify.com/api/client/python/" -"Source" = "https://github.com/apify/apify-client-python" -"Issue tracker" = "https://github.com/apify/apify-client-python/issues" -"Changelog" = "https://github.com/apify/apify-client-python/blob/master/CHANGELOG.md" "Apify Homepage" = "https://apify.com" +"Changelog" = "https://github.com/apify/apify-client-python/blob/master/CHANGELOG.md" +"Documentation" = "https://docs.apify.com/api/client/python/" +"Issue Tracker" = "https://github.com/apify/apify-client-python/issues" +"Repository" = "https://github.com/apify/apify-client-python" -[build-system] -requires = ["setuptools ~= 70.3.0", "wheel"] -build-backend = "setuptools.build_meta" - -[tool.setuptools.packages.find] -where = ["src"] -include = ["apify_client*"] +# We use inclusive ordered comparison clauses for external packages intentionally in order to enhance SDK's +# compatibility with external packages. This decision was discussed in detail in the following PR: +# https://github.com/apify/apify-sdk-python/pull/154. +[tool.poetry.dependencies] +python = "^3.9" +apify-shared = ">=1.1.2" +httpx = ">=0.25.0" -[tool.setuptools.package-data] -apify_client = ["py.typed"] +[tool.poetry.group.dev.dependencies] +build = "~1.2.0" +griffe = "~1.2.0" +ipdb = "~0.13.0" +mypy = "~1.11.0" +pre-commit = "~3.8.0" +pydoc-markdown = "~4.8.0" +pytest = "~8.3.0" +pytest-asyncio = "~0.24.0" +pytest-cov = "~5.0.0" +pytest-only = "~2.1.0" +pytest-timeout = "~2.3.0" +pytest-xdist = "~3.6.0" +redbaron = "~0.9.0" +ruff = "~0.6.0" +setuptools = "~74.0.0" # setuptools are used by pytest but not explicitly required [tool.ruff] line-length = 150 @@ -73,31 +69,35 @@ line-length = 150 [tool.ruff.lint] select = ["ALL"] ignore = [ - "ANN401", # Dynamically typed expressions (typing.Any) are disallowed in {filename} - "BLE001", # Do not catch blind exception - "C901", # `{name}` is too complex - "COM812", # This rule may cause conflicts when used with the formatter - "D100", # Missing docstring in public module - "D104", # Missing docstring in public package - "EM", # flake8-errmsg - "G004", # Logging statement uses f-string - "ISC001", # This rule may cause conflicts when used with the formatter - "FIX", # flake8-fixme - "PGH003", # Use specific rule codes when ignoring type issues - "PLR0911", # Too many return statements - "PLR0913", # Too many arguments in function definition - "PLR0915", # Too many statements - "PTH", # flake8-use-pathlib - "PYI034", # `__aenter__` methods in classes like `{name}` usually return `self` at runtime - "PYI036", # The second argument in `__aexit__` should be annotated with `object` or `BaseException | None` - "S102", # Use of `exec` detected - "S105", # Possible hardcoded password assigned to - "S106", # Possible hardcoded password assigned to argument: "{name}" - "S301", # `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue - "S303", # Use of insecure MD2, MD4, MD5, or SHA1 hash function - "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes - "TD002", # Missing author in TODO; try: `# TODO(): ...` or `# TODO @: ... - "TRY003", # Avoid specifying long messages outside the exception class + "ANN101", # Missing type annotation for `self` in method + "ANN102", # Missing type annotation for `{name}` in classmethod + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed in {filename} + "ASYNC109", # Async function definition with a `timeout` parameter + "BLE001", # Do not catch blind exception + "C901", # `{name}` is too complex + "COM812", # This rule may cause conflicts when used with the formatter + "D100", # Missing docstring in public module + "D104", # Missing docstring in public package + "D107", # Missing docstring in `__init__` + "EM", # flake8-errmsg + "G004", # Logging statement uses f-string + "ISC001", # This rule may cause conflicts when used with the formatter + "FIX", # flake8-fixme + "PGH003", # Use specific rule codes when ignoring type issues + "PLR0911", # Too many return statements + "PLR0913", # Too many arguments in function definition + "PLR0915", # Too many statements + "PTH", # flake8-use-pathlib + "PYI034", # `__aenter__` methods in classes like `{name}` usually return `self` at runtime + "PYI036", # The second argument in `__aexit__` should be annotated with `object` or `BaseException | None` + "S102", # Use of `exec` detected + "S105", # Possible hardcoded password assigned to + "S106", # Possible hardcoded password assigned to argument: "{name}" + "S301", # `pickle` and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue + "S303", # Use of insecure MD2, MD4, MD5, or SHA1 hash function + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes + "TD002", # Missing author in TODO; try: `# TODO(): ...` or `# TODO @: ... + "TRY003", # Avoid specifying long messages outside the exception class ] [tool.ruff.format] @@ -119,16 +119,57 @@ indent-style = "space" "INP001", # File {filename} is part of an implicit namespace package, add an __init__.py "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable "S101", # Use of assert detected + "SLF001", # Private member accessed: `{name}` "T20", # flake8-print "TRY301", # Abstract `raise` to an inner function + "TID252", # Prefer absolute imports over relative imports from parent modules ] [tool.ruff.lint.flake8-quotes] docstring-quotes = "double" inline-quotes = "single" -[tool.ruff.lint.isort] -known-local-folder = ["apify_client"] +[tool.ruff.lint.flake8-builtins] +builtins-ignorelist = ["id"] [tool.ruff.lint.pydocstyle] convention = "google" + +[tool.ruff.lint.isort] +known-local-folder = ["apify_client"] + +[tool.ruff.lint.pylint] +max-branches = 18 + +[tool.pytest.ini_options] +addopts = "-ra" +asyncio_mode = "auto" +timeout = 1200 + +[tool.mypy] +python_version = "3.9" +files = ["scripts", "src", "tests"] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_redundant_casts = true +warn_return_any = true +warn_unreachable = true +warn_unused_ignores = true +exclude = [] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "assert_never()" +] + +[tool.basedpyright] +typeCheckingMode = "standard" + +[tool.ipdb] +context = 7 diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 5231254c..00000000 --- a/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -asyncio_mode = auto -timeout = 1200 diff --git a/renovate.json b/renovate.json index ea1447bc..3204883a 100644 --- a/renovate.json +++ b/renovate.json @@ -1,9 +1,26 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" + "extends": ["config:base", ":semanticCommitTypeAll(chore)"], + "pinVersions": false, + "separateMajorMinor": false, + "dependencyDashboard": false, + "semanticCommits": "enabled", + "lockFileMaintenance": { + "enabled": true, + "schedule": ["before 1am on monday"], + "automerge": true, + "automergeType": "branch" + }, + "packageRules": [ + { + "matchPaths": ["pyproject.toml"], + "matchDepTypes": ["devDependencies"], + "matchUpdateTypes": ["major", "minor"], + "groupName": "major/minor dev dependencies", + "groupSlug": "dev-dependencies", + "automerge": true, + "automergeType": "branch" + } ], - "ignorePaths": [ - "website/**" - ] + "schedule": ["before 1am on monday"], + "ignoreDeps": ["apify_client", "docusaurus-plugin-typedoc-api"] } diff --git a/scripts/update_version_for_prerelease.py b/scripts/update_version_for_prerelease.py index 51ddf655..236fbf3e 100755 --- a/scripts/update_version_for_prerelease.py +++ b/scripts/update_version_for_prerelease.py @@ -41,8 +41,7 @@ for version in published_versions: if version.startswith(f'{current_version}{prerelease_prefix}'): prerelease_version = int(version.split(prerelease_prefix)[1]) - if prerelease_version > latest_prerelease: - latest_prerelease = prerelease_version + latest_prerelease = max(prerelease_version, latest_prerelease) # Write the latest prerelease version number to pyproject.toml new_prerelease_version_number = f'{current_version}{prerelease_prefix}{latest_prerelease + 1}' diff --git a/src/apify_client/_utils.py b/src/apify_client/_utils.py index 95ef843a..bd5cae70 100644 --- a/src/apify_client/_utils.py +++ b/src/apify_client/_utils.py @@ -22,7 +22,7 @@ StopRetryingType = Callable[[], None] -def to_safe_id(id: str) -> str: # noqa: A002 +def to_safe_id(id: str) -> str: # Identificators of resources in the API are either in the format `resource_id` or `username/resource_id`. # Since the `/` character has a special meaning in URL paths, # we replace it with `~` for proper route parsing on the API, where after parsing the URL it's replaced back to `/`. diff --git a/src/apify_client/clients/resource_clients/dataset.py b/src/apify_client/clients/resource_clients/dataset.py index 077a9a4c..bc343bee 100644 --- a/src/apify_client/clients/resource_clients/dataset.py +++ b/src/apify_client/clients/resource_clients/dataset.py @@ -66,8 +66,9 @@ def list_items( desc: bool | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_hidden: bool | None = None, flatten: list[str] | None = None, @@ -147,8 +148,9 @@ def iterate_items( desc: bool | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_hidden: bool | None = None, ) -> Iterator[dict]: @@ -229,8 +231,9 @@ def download_items( delimiter: str | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_header_row: bool | None = None, skip_hidden: bool | None = None, @@ -318,8 +321,9 @@ def get_items_as_bytes( delimiter: str | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_header_row: bool | None = None, skip_hidden: bool | None = None, @@ -409,8 +413,9 @@ def stream_items( delimiter: str | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_header_row: bool | None = None, skip_hidden: bool | None = None, @@ -567,8 +572,9 @@ async def list_items( desc: bool | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_hidden: bool | None = None, flatten: list[str] | None = None, @@ -648,8 +654,9 @@ async def iterate_items( desc: bool | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_hidden: bool | None = None, ) -> AsyncIterator[dict]: @@ -731,8 +738,9 @@ async def get_items_as_bytes( delimiter: str | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_header_row: bool | None = None, skip_hidden: bool | None = None, @@ -822,8 +830,9 @@ async def stream_items( delimiter: str | None = None, fields: list[str] | None = None, omit: list[str] | None = None, - unwind: str | list[str] | None = None, # TODO: change to list[str] only when doing a breaking release - # https://github.com/apify/apify-client-python/issues/255 + # TODO: change to list[str] only when doing a breaking release + # https://github.com/apify/apify-client-python/issues/255 + unwind: str | list[str] | None = None, skip_empty: bool | None = None, skip_header_row: bool | None = None, skip_hidden: bool | None = None, diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index b266e950..5114a4b1 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -8,7 +8,7 @@ API_URL_ENV_VAR = 'APIFY_INTEGRATION_TESTS_API_URL' -@pytest.fixture() +@pytest.fixture def apify_client() -> ApifyClient: api_token = os.getenv(TOKEN_ENV_VAR) api_url = os.getenv(API_URL_ENV_VAR) @@ -24,7 +24,7 @@ def apify_client() -> ApifyClient: # because `httpx.AsyncClient` in `ApifyClientAsync` tries to reuse the same event loop across requests, # but `pytest-asyncio` closes the event loop after each test, # and uses a new one for the next test. -@pytest.fixture() +@pytest.fixture def apify_client_async() -> ApifyClientAsync: api_token = os.getenv(TOKEN_ENV_VAR) api_url = os.getenv(API_URL_ENV_VAR)