diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6b0c235 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# 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://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: ".github/workflows" # Location of package manifests + schedule: + interval: "weekly" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 35107c7..f263406 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,10 +13,10 @@ jobs: if: ((github.event_name == 'push' && startsWith(github.ref, 'refs/tags')) || contains(github.event.pull_request.labels.*.name, 'Build wheels')) steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: 3.8 diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml new file mode 100644 index 0000000..5b91fae --- /dev/null +++ b/.github/workflows/python-tests.yml @@ -0,0 +1,90 @@ +name: Run unit tests + +on: + pull_request: + push: + branches: [ main ] + tags: + - '*' + workflow_dispatch: + schedule: + # Run every Thursday at 03:53 UTC + - cron: 53 3 * * 4 + +jobs: + tests: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + python-version: 3.8 + toxenv: py38-test-pytestoldest + - os: windows-latest + python-version: 3.8 + toxenv: py38-test-pytest50 + - os: macos-latest + python-version: 3.8 + toxenv: py38-test-pytest51 + - os: ubuntu-latest + python-version: 3.8 + toxenv: py38-test-pytest52 + - os: windows-latest + python-version: 3.8 + toxenv: py38-test-pytest53 + - os: ubuntu-latest + python-version: 3.8 + toxenv: py38-test-pytest60 + - os: ubuntu-latest + python-version: 3.9 + toxenv: py39-test-pytest61 + - os: ubuntu-latest + python-version: 3.9 + toxenv: py39-test-pytest62 + - os: ubuntu-latest + python-version: '3.10' + toxenv: py310-test-pytest70 + - os: ubuntu-latest + python-version: '3.10' + toxenv: py310-test-pytest71 + - os: windows-latest + python-version: '3.11' + toxenv: py311-test-pytest72 + - os: ubuntu-latest + python-version: '3.11' + toxenv: py311-test-pytest73 + - os: ubuntu-latest + python-version: '3.11' + toxenv: py311-test-pytest74 + - os: ubuntu-latest + python-version: '3.12' + toxenv: py312-test-pytest80 + - os: macos-latest + python-version: '3.12' + toxenv: py312-test-pytest80 + - os: windows-latest + python-version: '3.12' + toxenv: py312-test-pytest80 + - os: macos-latest + python-version: '3.11' + toxenv: py311-test-pytestdev + - os: windows-latest + python-version: '3.11' + toxenv: py311-test-pytestdev + - os: ubuntu-latest + python-version: '3.12' + toxenv: py312-test-pytestdev + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install Tox + run: python -m pip install tox + - name: Run Tox + run: tox ${{ matrix.toxargs }} -v -e ${{ matrix.toxenv }} diff --git a/CHANGES.rst b/CHANGES.rst index 42f9588..39edf59 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,9 @@ 0.1.3 (unreleased) ================== -- No changes yet. +- pytest 8.1 compatibility. [#15] + +- Dropped Python 3.7 support. Minimum supported pytest is now 4.6. [#15] 0.1.2 (2022-12-11) ================== diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index c767ed8..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,34 +0,0 @@ -resources: - repositories: - - repository: OpenAstronomy - type: github - endpoint: astropy - name: OpenAstronomy/azure-pipelines-templates - ref: master - -jobs: -- template: run-tox-env.yml@OpenAstronomy - parameters: - envs: - # Code style - - linux: codestyle - # Standard tests - - linux: py37-test - - linux: py38-test - - linux: py39-test - - linux: py310-test - - linux: py311-test - - macos: py37-test - - macos: py38-test - - macos: py39-test - - macos: py310-test - - windows: py38-test - - windows: py39-test - - windows: py310-test - -# Until https://github.com/OpenAstronomy/azure-pipelines-templates/issues/86 is fixed, then move this back to the previous job -- template: run-tox-env.yml@OpenAstronomy - parameters: - toxverspec: <4 - envs: - - windows: py37-test diff --git a/pytest_filter_subpackage/plugin.py b/pytest_filter_subpackage/plugin.py index ac92016..da0d502 100644 --- a/pytest_filter_subpackage/plugin.py +++ b/pytest_filter_subpackage/plugin.py @@ -6,7 +6,14 @@ """ import os + import pytest +from packaging.version import Version + +_pytest_version = Version(pytest.__version__) +PYTEST_GE_8_0 = any([_pytest_version.is_devrelease, + _pytest_version.is_prerelease, + _pytest_version >= Version('8.0')]) def pytest_addoption(parser): @@ -17,52 +24,106 @@ def pytest_addoption(parser): "string to specify multiple packages.") -@pytest.hookimpl(tryfirst=True) -def pytest_ignore_collect(path, config): +if PYTEST_GE_8_0: - # NOTE: it is important that when we don't want to skip a file we return - # None and not False - if we return False pytest will not call any other - # pytest_ignore_collect function in other plugins, e.g. pytest-doctestplus. + @pytest.hookimpl(tryfirst=True) + def pytest_ignore_collect(collection_path, config): - # If the --package/-P option wasn't specified, don't do anything - if config.getvalue('package') is None: - return None + # NOTE: it is important that when we don't want to skip a file we return + # None and not False - if we return False pytest will not call any other + # pytest_ignore_collect function in other plugins, e.g. pytest-doctestplus. - # If the path is a directory, never skip - just do the filtering on a file - # by file basis. - if os.path.isdir(path): - return None + # If the --package/-P option wasn't specified, don't do anything + if config.getvalue('package') is None: + return None - # Otherwise ignore filename for remainder of checks - path = os.path.dirname(path) + # If the path is a directory, never skip - just do the filtering on a file + # by file basis. + if collection_path.is_dir(): + return None - # Split path into components - path = path.split(os.path.sep) + # Otherwise ignore filename for remainder of checks + path = str(collection_path.parent) + + # Split path into components + path = path.split(os.path.sep) + + # Now cycle through and find the top level of the package - this is the + # last one that contains an ``__init__.py`` or ``index.rst`` file. We need + # to make sure that at least one of these files was found before escaping. + found_prev = False + for i in range(len(path), -1, -1): + subpath = os.path.sep.join(path[:i]) + found = (os.path.exists(os.path.join(subpath, '__init__.py')) or + os.path.exists(os.path.join(subpath, 'index.rst'))) + if found_prev and not found: + break + found_prev = found - # Now cycle through and find the top level of the package - this is the - # last one that contains an ``__init__.py`` or ``index.rst`` file. We need - # to make sure that at least one of these files was found before escaping. - found_prev = False - for i in range(len(path), -1, -1): - subpath = os.path.sep.join(path[:i]) - found = (os.path.exists(os.path.join(subpath, '__init__.py')) or - os.path.exists(os.path.join(subpath, 'index.rst'))) - if found_prev and not found: - break - found_prev = found + subpackage_path = path[i+1:] - subpackage_path = path[i+1:] + # Find selected sub-packages + selected = config.getvalue('package').strip().split(',') - # Find selected sub-packages - selected = config.getvalue('package').strip().split(',') + # Finally, we check if this is one of the specified ones + for subpackage_target in selected: + for i, target in enumerate(subpackage_target.split('.')): + if i >= len(subpackage_path) or target != subpackage_path[i]: + break - # Finally, we check if this is one of the specified ones - for subpackage_target in selected: - for i, target in enumerate(subpackage_target.split('.')): - if i >= len(subpackage_path) or target != subpackage_path[i]: - break + else: + return None + + return True + +else: + + @pytest.hookimpl(tryfirst=True) + def pytest_ignore_collect(path, config): - else: + # NOTE: it is important that when we don't want to skip a file we return + # None and not False - if we return False pytest will not call any other + # pytest_ignore_collect function in other plugins, e.g. pytest-doctestplus. + + # If the --package/-P option wasn't specified, don't do anything + if config.getvalue('package') is None: + return None + + # If the path is a directory, never skip - just do the filtering on a file + # by file basis. + if os.path.isdir(path): return None - return True + # Otherwise ignore filename for remainder of checks + path = os.path.dirname(path) + + # Split path into components + path = path.split(os.path.sep) + + # Now cycle through and find the top level of the package - this is the + # last one that contains an ``__init__.py`` or ``index.rst`` file. We need + # to make sure that at least one of these files was found before escaping. + found_prev = False + for i in range(len(path), -1, -1): + subpath = os.path.sep.join(path[:i]) + found = (os.path.exists(os.path.join(subpath, '__init__.py')) or + os.path.exists(os.path.join(subpath, 'index.rst'))) + if found_prev and not found: + break + found_prev = found + + subpackage_path = path[i+1:] + + # Find selected sub-packages + selected = config.getvalue('package').strip().split(',') + + # Finally, we check if this is one of the specified ones + for subpackage_target in selected: + for i, target in enumerate(subpackage_target.split('.')): + if i >= len(subpackage_path) or target != subpackage_path[i]: + break + + else: + return None + + return True diff --git a/setup.cfg b/setup.cfg index e0785c3..9d2905d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,18 +4,18 @@ url = https://github.com/astropy/pytest-filter-subpackage author = The Astropy Developers author_email = astropy.team@gmail.com classifiers = - Development Status :: 3 - Alpha + Development Status :: 5 - Production/Stable Framework :: Pytest Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 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 :: Testing Topic :: Utilities license = BSD @@ -27,9 +27,9 @@ keywords = pytest, py.test [options] zip_safe = False packages = find: -python_requires = >=3.7 +python_requires = >=3.8 install_requires = - pytest>=3.0 + pytest>=4.6 [options.entry_points] pytest11 = @@ -42,7 +42,7 @@ test = pytest-cov [tool:pytest] -minversion = 3.0 +minversion = 4.6 testpaths = tests pytest_filter_subpackage xfail_strict = true filterwarnings = @@ -50,5 +50,8 @@ filterwarnings = ignore:file format.*:UserWarning ignore:.*non-empty pattern match.*:FutureWarning +[flake8] +max-line-length = 100 + [bdist_wheel] universal = 1 diff --git a/tox.ini b/tox.ini index 80cd61e..fdf1a78 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{37,38,39,310,311}-test + py{38,39,310,311,312}-test codestyle requires = setuptools >= 30.3.0 @@ -8,15 +8,37 @@ requires = isolated_build = true [testenv] -changedir = - test: .tmp/{envname} -description = - test: run tests with pytest +changedir = .tmp/{envname} +description = run tests with pytest +deps = + pytestoldest: pytest==4.6.0 + pytest50: pytest==5.0.* + pytest51: pytest==5.1.* + pytest52: pytest==5.2.* + pytest53: pytest==5.3.* + pytest60: pytest==6.0.* + pytest61: pytest==6.1.* + pytest62: pytest==6.2.* + pytest70: pytest==7.0.* + pytest71: pytest==7.1.* + pytest72: pytest==7.2.* + pytest73: pytest==7.3.* + pytest74: pytest==7.4.* + pytest80: pytest==8.0.* + pytestdev: git+https://github.com/pytest-dev/pytest#egg=pytest + pytestdev: git+https://github.com/scientific-python/pytest-doctestplus + extras = - test: test + test + +# Temporary measures to be able to test on 8.0.x in its RC cycle +pip_pre = + pytest80: true + !pytest80: false + commands = pip freeze - test: pytest {toxinidir}/tests {posargs} + pytest {toxinidir}/tests {posargs} [testenv:codestyle] skip_install = true