diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 29f4410..d924dcd 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -1,6 +1,3 @@ -# This workflow will install Python dependencies and run tests with a variety of Python versions -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python - name: Test on: @@ -14,12 +11,13 @@ jobs: build: runs-on: ${{ matrix.os }} strategy: - fail-fast: true + fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] - python-version: ["3.10", "3.11", "3.12", "pypy3.10"] - pytest-version: ["8"] - cython-version: ["0.29", "3"] + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ["3.11", "3.12", "3.13", "3.14"] + pytest-version: ["9"] + cython-version: ["3"] + editable-install: ["true", "false"] steps: - uses: actions/checkout@v6 @@ -38,6 +36,15 @@ jobs: python -m pip install cython==${{ matrix.cython-version }}.* python -m pip install -e . + - name: Install example-project (editable) + run: python -m pip install -e tests/example-project + if: ${{ matrix.editable-install == 'true' }} + + - name: Install example-project (non-editable) + run: python -m pip install tests/example-project + if: ${{ matrix.editable-install == 'false' }} + - name: Test with pytest - run: | - pytest -vv tests src + env: + PY_IGNORE_IMPORTMISMATCH: "1" + run: pytest -vv tests diff --git a/src/pytest_cython/__init__.py b/src/pytest_cython/__init__.py index bf1c790..e441e08 100644 --- a/src/pytest_cython/__init__.py +++ b/src/pytest_cython/__init__.py @@ -1,8 +1,5 @@ -from importlib.metadata import version, PackageNotFoundError +from importlib.metadata import version -try: - __version__ = version("pytest-cython") -except PackageNotFoundError: - import warnings - warnings.warn('could not get pytest-cython version') - __version__ = '0.0.0' +__version__ = version('pytest-cython') + +del version diff --git a/src/pytest_cython/plugin.py b/src/pytest_cython/plugin.py index 306a429..5c0067b 100644 --- a/src/pytest_cython/plugin.py +++ b/src/pytest_cython/plugin.py @@ -9,8 +9,7 @@ from typing import Any, Iterable, Union -from _pytest.nodes import Collector -from _pytest.doctest import skip, DoctestModule, DoctestItem +from _pytest.doctest import DoctestModule, DoctestItem from _pytest.pathlib import resolve_package_path, ImportMode @@ -33,15 +32,11 @@ def pytest_addoption(parser: pytest.Parser): ) -def pytest_collect_file(file_path: pathlib.Path, path, parent: pytest.Collector) -> pytest.Module: +def pytest_collect_file(file_path: pathlib.Path, parent: pytest.Collector) -> pytest.Module: config = parent.config if file_path.suffix not in CYTHON_SUFFIXES or not config.getoption('--doctest-cython'): return - bin_path = file_path.with_suffix(EXT_SUFFIX) - if not bin_path.exists(): - return - # only run test if matching .so and .pyx files exist return _PatchedDoctestModule.from_parent(parent, path=file_path) @@ -62,15 +57,6 @@ def collect(self) -> Iterable[DoctestItem]: os.environ[IGNORE_IMPORTMISMATCH_KEY] = IGNORE_IMPORTMISMATCH module = self.obj # module already imported - - try: - _check_module_import(module, self.path, mode) - except Collector.CollectError: - if self.config.getvalue("doctest_ignore_import_errors"): - skip("unable to import module %r" % self.path) - else: - raise - return _add_line_numbers(module, items) @@ -94,30 +80,6 @@ def _get_module_name(path: pathlib.Path) -> str: return module_name -def _check_module_import(module: Any, path: pathlib.Path, mode: ImportMode) -> None: - # double check that the only difference is the extension else raise an exception - - if mode is ImportMode.importlib or IGNORE_IMPORTMISMATCH == "1": - return - - module_name = _get_module_name(path) - module_file = _without_suffixes(module.__file__) - import_file = _without_suffixes(path) - - if module_file == import_file: - return - - raise Collector.CollectError( - "import file mismatch:\n" - "imported module %r has this __file__ attribute:\n" - " %s\n" - "which is not the same as the test file we want to collect:\n" - " %s\n" - "HINT: remove __pycache__ / .pyc files and/or use a " - "unique basename for your test file modules" % (module_name, module_file, import_file) - ) - - def _add_line_numbers(module: Any, items: Iterable[DoctestItem]) -> Iterable[DoctestItem]: # handle tests from Cython's internal __test__ dict generated by # the autotestdict directive; we exclude the tests from __test__, diff --git a/tests/example-project/pyproject.toml b/tests/example-project/pyproject.toml new file mode 100644 index 0000000..55c4b98 --- /dev/null +++ b/tests/example-project/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools>=74.1", "Cython", "wheel"] diff --git a/tests/example-project/setup.py b/tests/example-project/setup.py index bb616c9..c00c166 100644 --- a/tests/example-project/setup.py +++ b/tests/example-project/setup.py @@ -22,7 +22,7 @@ ] setup( - name='pytest-cython', + name='pytest-cython-example', version='0.3.1', description="Example Cython project for pytest-cython tests", package_dir={'': 'src'}, diff --git a/tests/test_pytest_cython.py b/tests/test_pytest_cython.py index 535f40d..2c792f9 100644 --- a/tests/test_pytest_cython.py +++ b/tests/test_pytest_cython.py @@ -2,20 +2,19 @@ import pathlib import pytest -import shutil +from pathlib import Path -from setuptools.sandbox import run_setup - -# import pytest_cython as a quite check to ensure it was installed before running tests +# Check imports to ensure packages are installed before running tests import pytest_cython.plugin +import pypackage ROOT_PATH = pathlib.Path(__file__).parent PROJECT_PATH = ROOT_PATH.joinpath('example-project') PACKAGE_PATH = PROJECT_PATH.joinpath('src', 'pypackage') -IMPORT_MODES = ["append", "prepend", "importlib"] - +# TODO: Figure out if importlib can be supported. +IMPORT_MODES = ["append", "prepend"] def get_module(basename: str, suffix='.pyx') -> pathlib.Path: return PACKAGE_PATH.joinpath(basename + suffix) @@ -25,21 +24,6 @@ def run_pytest(pytester: pytest.Pytester, module: pathlib.Path, import_mode) -> return pytester.runpytest('-vv', '--doctest-cython', '--import-mode', import_mode, str(module)) -@pytest.fixture(scope='module', autouse=True) -def build_example_project(): - shutil.rmtree(PROJECT_PATH.joinpath('build'), True) - shutil.rmtree(PACKAGE_PATH.joinpath('__pycache__'), True) - - for file in PACKAGE_PATH.glob('*.pyd'): - file.unlink() - - for file in PACKAGE_PATH.glob('*.c'): - file.unlink() - - setup_py = PROJECT_PATH.joinpath('setup.py') - run_setup(str(setup_py), ['build_ext', '--inplace']) - - @pytest.mark.parametrize('import_mode', IMPORT_MODES) def test_cython_ext_module(pytester, import_mode): module = get_module('cython_ext_module')