diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 235a368..09eb033 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -75,3 +75,33 @@ jobs: conda install -c conda-forge cadet>=5.0.3 pytest tests --rootdir=tests -m "not slow and not local" + test-pypi-job: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest] + python-version: ["3.12", "3.13"] + include: + - os: windows-latest + python-version: "3.12" + - os: macos-latest + python-version: "3.12" + + steps: + - uses: actions/checkout@v4 + + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install CADET-Python and cadet-core (PyPI) + run: | + pip install . --group testing + pip install cadet-core + + - name: Test with pytest + run: | + pytest tests --rootdir=tests -m "not slow and not local" + diff --git a/cadet/cadet.py b/cadet/cadet.py index f886b8a..45ec25a 100644 --- a/cadet/cadet.py +++ b/cadet/cadet.py @@ -93,20 +93,47 @@ def _find_in_prefix(name: str) -> Optional[Path]: ) if platform.system() == 'Windows': - dll_path = cadet_root / 'bin' / 'cadet.dll' - dll_debug_path = cadet_root / 'bin' / 'cadet_d.dll' + dll_dir = cadet_root / 'bin' + dll_names = ['cadet.dll'] + dll_debug_names = ['cadet_d.dll'] elif platform.system() == 'Darwin': - dll_path = cadet_root / 'lib' / 'libcadet.dylib' - dll_debug_path = cadet_root / 'lib' / 'libcadet_d.dylib' + dll_dir = cadet_root / 'lib' + # The unversioned name is a symlink to the SONAME-versioned file and may not + # survive wheel packaging, so also look for the versioned file directly. + dll_names = ['libcadet.dylib', 'libcadet.0.dylib'] + dll_debug_names = ['libcadet_d.dylib', 'libcadet_d.0.dylib'] else: - dll_path = cadet_root / 'lib' / 'libcadet.so' - dll_debug_path = cadet_root / 'lib' / 'libcadet_d.so' + dll_dir = cadet_root / 'lib' + dll_names = ['libcadet.so'] + dll_debug_names = ['libcadet_d.so'] - # Look for debug dll if dll is not found. - if not dll_path.is_file() and dll_debug_path.is_file(): - dll_path = dll_debug_path + def _find_dll(directory: Path, names: list[str]) -> Optional[Path]: + for name in names: + p = directory / name + if p.is_file(): + return p + return None - cadet_dll_path = dll_path if dll_path.is_file() else None + # Look for debug dll if dll is not found. + cadet_dll_path = _find_dll(dll_dir, dll_names) or _find_dll(dll_dir, dll_debug_names) + + # On PyPI cadet-core installs, cadet-cli/createLWE may be console-script wrappers + # whose enclosing prefix does not match the prefix that the cadet_core package + # (and its shared library) was actually installed into. If the dll could not be + # found under cadet_root, fall back to the cadet_core package directory directly. + if cadet_dll_path is None: + try: + import cadet_core + package_dir = Path(cadet_core.__file__).parent + except ImportError: + package_dir = None + + if package_dir is not None: + package_dll_dir = package_dir / dll_dir.name + cadet_dll_path = ( + _find_dll(package_dll_dir, dll_names) + or _find_dll(package_dll_dir, dll_debug_names) + ) return root_path, cadet_cli_path, cadet_dll_path, cadet_create_lwe_path diff --git a/tests/test_cadet.py b/tests/test_cadet.py index f01cd50..a83ad41 100644 --- a/tests/test_cadet.py +++ b/tests/test_cadet.py @@ -2,7 +2,7 @@ Test general properties of Cadet. """ - +from pathlib import Path import re import pytest from cadet import Cadet @@ -16,5 +16,25 @@ def test_version(use_dll): assert re.match(r"\d\.\d\.\d", cadet.version), "Version format should be X.X.X" +@pytest.mark.parametrize("install_path", [ + pytest.param(None, id="conda"), + pytest.param("pypi", id="pypi"), +]) +def test_create_lwe_and_run_cli_smoke(install_path, tmp_path): + """createLWE + cadet-cli smoke test for both the conda and the PyPI install.""" + if install_path == "pypi": + cadet_core = pytest.importorskip("cadet_core") + install_path = Path(cadet_core.__file__).parent + else: + install_path = Cadet.autodetect_cadet() + + cadet = Cadet(install_path=install_path, use_dll=False) + + model = cadet.create_lwe(file_path=tmp_path / "LWE.h5") + return_information = model.run_simulation() + + assert return_information.return_code == 0 + + if __name__ == '__main__': pytest.main([__file__]) diff --git a/tests/test_install_path_settings.py b/tests/test_install_path_settings.py index 9606218..86342d6 100644 --- a/tests/test_install_path_settings.py +++ b/tests/test_install_path_settings.py @@ -19,8 +19,11 @@ def test_autodetection(): sim = Cadet() assert sim.install_path == install_path_conda - assert sim.cadet_dll_path.parent.parent == install_path_conda - assert sim.cadet_cli_path.parent.parent == install_path_conda + # On some installs (e.g. cadet-core from PyPI), cadet-cli and the shared library + # are not necessarily nested under the same root, so only check that both were + # actually found rather than asserting a shared parent directory. + assert sim.cadet_dll_path is not None and sim.cadet_dll_path.is_file() + assert sim.cadet_cli_path is not None and sim.cadet_cli_path.is_file() assert sim.cadet_runner.cadet_path.suffix not in [".dll", ".so"]