pytest-dev/pytest · error · ImportError

Can't find module {modname} at location {self!s}

Error message

Can't find module {modname} at location {self!s}

What it means

Raised by LocalPath.pyimport() in importlib mode when importlib.util.spec_from_file_location returns a spec without a loader (or None). This means Python's import machinery could not determine how to load a module from the given file path — typically because the file extension is not a recognized importable format (.py, .so, etc.). The error is an ImportError, distinct from the ENOENT raised earlier when the file does not exist.

Source

Thrown at src/_pytest/_py/path.py:1099

        if ensuresyspath is False no modification of syspath happens.

        Special value of ensuresyspath=="importlib" is intended
        purely for using in pytest, it is capable only of importing
        separate .py files outside packages, e.g. for test suite
        without any __init__.py file. It effectively allows having
        same-named test modules in different places and offers
        mild opt-in via this option. Note that it works only in
        recent versions of python.
        """
        if not self.check():
            raise error.ENOENT(self)

        if ensuresyspath == "importlib":
            if modname is None:
                modname = self.purebasename
            spec = importlib.util.spec_from_file_location(modname, str(self))
            if spec is None or spec.loader is None:
                raise ImportError(f"Can't find module {modname} at location {self!s}")
            mod = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(mod)
            return mod

        pkgpath = None
        if modname is None:
            pkgpath = self.pypkgpath()
            if pkgpath is not None:
                pkgroot = pkgpath.dirpath()
                names = self.new(ext="").relto(pkgroot).split(self.sep)
                if names[-1] == "__init__":
                    names.pop()
                modname = ".".join(names)
            else:
                pkgroot = self.dirpath()
                modname = self.purebasename

            self._ensuresyspath(ensuresyspath, pkgroot)

View on GitHub (pinned to 0d6fbdeffa)

Solutions

  1. Ensure the target file has a Python-recognized extension (.py, .pyc, .so).
  2. Register a custom loader/path hook if importing a non-standard extension.
  3. Verify the path exists and is readable before calling pyimport.
  4. If importing source, use importlib.util directly with explicit source handling.

Example fix

// before
mod = path.pyimport()  # path points to 'data.txt'
// after
mod = path_with_py_extension.pyimport()
Defensive patterns

Strategy: validation

Validate before calling

import importlib.util
IMPORTABLE_EXTS = {'.py', '.pyc', '.pyo', '.so'}
def can_import(path) -> bool:
    if not path.check(file=True):
        return False
    if path.ext not in IMPORTABLE_EXTS:
        return False
    spec = importlib.util.spec_from_file_location(path.purebasename, str(path))
    return spec is not None and spec.loader is not None

Type guard

def has_importable_ext(path) -> bool:
    return path.ext in {'.py', '.pyc', '.so'}

Try / catch

try:
    mod = path.pyimport()
except ImportError as e:
    if "Can't find module" in str(e):
        # fallback: load source manually or skip
        ...

Prevention

When it happens

Trigger: Calling path.pyimport(ensuresyspath='importlib') on a .txt, .json, or extension-less file; on a path whose suffix is not registered with any import loader; with an explicit modname that importlib still cannot resolve to a loader.

Common situations: Dynamically importing test data or generated modules with unexpected extensions; cross-platform issues with .pyc vs .py; plugin systems that point at non-Python files.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/f88739777f73315f. Report an issue: GitHub.