Source code for simvx.core.testing.optional
"""Guards for tests that need an optional package.
:func:`importlib.util.find_spec` is the obvious way to write "skip unless X is
installed", and it is wrong for any dotted name: it imports the parent package
to read its ``__path__``, so a missing parent raises ``ModuleNotFoundError``
instead of returning ``None``. ``find_spec("simvx.physics.jolt")`` therefore
answers "not installed" on a machine that has ``simvx-physics-jolt`` minus the
submodule, and *raises at collection time* on one that has neither -- which is
every machine that installed only the engine.
:func:`module_installed` is the answer those guards actually want.
"""
import importlib.util
__all__ = ["module_installed"]
[docs]
def module_installed(name: str) -> bool:
"""Return whether *name* can be imported, without importing it.
Handles dotted names whose parent package is itself absent, and treats a
parent that exists but fails to import as "not installed" rather than
letting the import error escape into collection.
Args:
name: A module name, dotted or not (``"pymunk"``, ``"simvx.physics.jolt"``).
Returns:
``True`` when the module can be located, ``False`` otherwise.
"""
try:
return importlib.util.find_spec(name) is not None
except (ImportError, ValueError):
return False