Skip to content

Fix skala.ase import failure on CPU-only environments#71

Merged
awvwgk merged 3 commits into
mainfrom
fix/gpu4pyscf-import-on-cpu
May 11, 2026
Merged

Fix skala.ase import failure on CPU-only environments#71
awvwgk merged 3 commits into
mainfrom
fix/gpu4pyscf-import-on-cpu

Conversation

@gncs
Copy link
Copy Markdown
Contributor

@gncs gncs commented May 11, 2026

Root cause

The docs build has been failing since #58 because docs/ase.ipynb cannot execute the very first cell:

from skala.ase import Skala

The CI traceback (myst_nb.core.execute.base.ExecutionError) hides the underlying notebook error; the linkcheck step is just the last to surface it (the HTML build hits the same failure a few seconds earlier).

src/skala/ase/calculator.py does:

try:
    import skala.gpu4pyscf as skala_gpu
except ImportError as e:
    skala_gpu = None
    gpu4pyscf_import_error = e

But src/skala/gpu4pyscf/__init__.py had, at module level:

if not torch.cuda.is_available() and find_spec("pytest") is not None:
    import pytest
    pytest.skip(
        "Skipping gpu4pyscf doctests, because CUDA is not available.",
        allow_module_level=True,
    )

pytest.skip(...) raises pytest.outcomes.Skipped, which derives from BaseException, not ImportError. So on any host where (a) CUDA is unavailable and (b) pytest is installed — which is every CI runner and every dev env that installs .[dev] or .[doc] — the except ImportError does not catch it, and import skala.ase fails with Skipped. This breaks both the docs notebook and any CPU-only use of the ASE calculator.

Repro on main:

$ python -c "from skala.ase import Skala"
...
File "src/skala/gpu4pyscf/__init__.py", line 19, in <module>
    pytest.skip(...)
Skipped: Skipping gpu4pyscf doctests, because CUDA is not available.

Fix

Two coordinated changes.

1. src/skala/gpu4pyscf/__init__.py — replace the module-level pytest.skip with a plain ImportError. The skala.gpu4pyscf package genuinely requires CUDA, so "module is unusable" is best expressed as ImportError, which is the standard signal callers already handle (including calculator.py's try/except ImportError). The find_spec("pytest") gate is dropped: it was both insufficient (any env with pytest installed triggered the bug) and unnecessary (the module being unimportable on non-CUDA hosts is correct independent of pytest).

2. src/skala/conftest.py (new) — the original pytest.skip was, despite the broken mechanism, trying to do something legitimate: prevent pytest --doctest-modules --pyargs skala (used by .github/workflows/test.yml) from failing collection of the gpu4pyscf modules on CPU runners. After change (1), pytest collection sees the ImportError and errors out. Restore the intended behavior with a collect_ignore_glob that skips gpu4pyscf/*.py when CUDA is unavailable. The conftest is placed at src/skala/ rather than inside src/skala/gpu4pyscf/ because, as a regular package, the inner directory's conftest would itself be imported as skala.gpu4pyscf.conftest, triggering the failing __init__.py before pytest could read it.

Dedicated tests in tests/test_gpu4pyscf_*.py already use their own pytest.skip(allow_module_level=True) guard, which is the correct place for that pattern (inside test modules, where pytest does the importing).

Verification

  • from skala.ase import Skala works on a CPU-only env.
  • The ASE notebook's GPU cells (Skala(..., device="cuda") and calc.set(device="cpu")) work, because calculator.py catches the ImportError and only raises at calculate() time if device="cuda" is actually invoked.
  • sphinx-build -b html docs docs/_build/html succeeds end-to-end.
  • pytest --doctest-modules --pyargs skala tests/ → 159 passed, 4 skipped on CPU-only env.
  • ruff format, ruff check, mypy pass on both changed files.

skala.gpu4pyscf/__init__.py used pytest.skip(allow_module_level=True)
at module load time when CUDA was unavailable. pytest.skip raises
pytest.outcomes.Skipped, which derives from BaseException, not
ImportError. The try/except ImportError around 'import skala.gpu4pyscf'
in skala.ase.calculator therefore did not catch it, and importing
skala.ase failed on any host that lacked CUDA but had pytest installed
(all CI runners and dev environments).

This broke the docs build (docs/ase.ipynb fails on 'from skala.ase
import Skala') and any CPU-only use of the ASE calculator since #58.

Replace the pytest.skip with a plain ImportError, which is the correct
signal for an unusable module and is handled by the existing try/except
in calculator.py. The find_spec('pytest') gate is dropped: it was both
insufficient (any env with pytest installed triggered the bug) and
unnecessary (the module being unimportable on non-CUDA hosts is the
right behavior independent of pytest). Test modules already use
pytest.skip themselves, which is the correct place for that pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gncs gncs requested a review from awvwgk May 11, 2026 09:11
After the previous commit, importing skala.gpu4pyscf on hosts without
CUDA raises ImportError. This is the right semantics for normal callers
(skala.ase.calculator catches it), but the Tests workflow runs
'pytest --doctest-modules --pyargs skala', which collects doctests by
importing every module under src/skala/. The ImportError therefore turns
into a collection error for skala.gpu4pyscf.{__init__,dft,gradients,
grids} on CPU runners.

Add src/skala/conftest.py with a collect_ignore_glob that skips
'gpu4pyscf/*.py' when CUDA is unavailable. The conftest lives at the
skala/ level rather than inside gpu4pyscf/ so pytest can load it without
first triggering the gpu4pyscf package import. Dedicated test modules
in tests/test_gpu4pyscf_*.py keep their own pytest.skip guard.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@awvwgk awvwgk merged commit 158deab into main May 11, 2026
30 checks passed
@awvwgk awvwgk deleted the fix/gpu4pyscf-import-on-cpu branch May 11, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants