{"id":"5d856b5b5869b0d4","repo":"pytest-dev/pytest","slug":"import-error-in-used-ex","errorCode":null,"errorMessage":"import error in {used}: {ex}","messagePattern":"import error in (.+?): (.+?)","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"src/_pytest/monkeypatch.py","lineNumber":88,"sourceCode":"    found: object = importlib.import_module(used)\n    for part in parts:\n        used += \".\" + part\n        try:\n            found = getattr(found, part)\n        except AttributeError:\n            pass\n        else:\n            continue\n        # We use explicit un-nesting of the handling block in order\n        # to avoid nested exceptions.\n        try:\n            importlib.import_module(used)\n        except ImportError as ex:\n            expected = str(ex).split()[-1]\n            if expected == used:\n                raise\n            else:\n                raise ImportError(f\"import error in {used}: {ex}\") from ex\n        found = annotated_getattr(found, part, used)\n    return found\n\n\ndef annotated_getattr(obj: object, name: str, ann: str) -> object:\n    try:\n        obj = getattr(obj, name)\n    except AttributeError as e:\n        raise AttributeError(\n            f\"{type(obj).__name__!r} object at {ann} has no attribute {name!r}\"\n        ) from e\n    return obj\n\n\ndef derive_importpath(import_path: str, raising: bool) -> tuple[str, object]:\n    if not isinstance(import_path, str) or \".\" not in import_path:\n        raise TypeError(f\"must be absolute import path string, not {import_path!r}\")\n    module, attr = import_path.rsplit(\".\", 1)","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/monkeypatch.py#L70-L106","documentation":"`resolve()` walks a dotted import path component by component. When an intermediate or final segment can't be imported as a submodule AND the ImportError's message does not match the segment being imported (meaning the failure is a deeper dependency), it re-raises as `ImportError(\"import error in <used>: <original>\")`. This distinguishes 'this module does not exist' from 'this module exists but one of its imports fails'.","triggerScenarios":"`monkeypatch.setattr('pkg.mod.attr', ...)` where `pkg.mod` imports a missing/broken dependency; or `derive_importpath` on a path whose submodule raises ImportError during import.","commonSituations":"Patching an attribute in a module that itself fails to import (missing optional dep, broken symlink, version mismatch); circular imports; patched path typed wrong so pytest tries the wrong submodule chain.","solutions":["Import the target module in a Python REPL first (`import pkg.mod`) to surface the real ImportError.","Fix or install the missing dependency of the target module.","Verify the dotted path spelling — a wrong segment sends resolve down the wrong submodule chain."],"exampleFix":"# before\nmonkeypatch.setattr('mypkg.svc.client', fake)  # mypkg.svc import fails due to missing dep\n# after\n# fix the import in mypkg.svc (install missing dep), then:\nmonkeypatch.setattr('mypkg.svc.client', fake)","handlingStrategy":"validation","validationCode":"import importlib\ndef module_imports_cleanly(dotted: str) -> bool:\n    module = dotted.rsplit('.', 1)[0]\n    try:\n        importlib.import_module(module)\n    except ImportError:\n        return False\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    monkeypatch.setattr('pkg.mod.attr', fake)\nexcept ImportError as e:\n    # fix the underlying import, then retry\n    ...","preventionTips":["Ensure the target module imports cleanly before patching.","Run `python -c 'import pkg.mod'` to diagnose deeper ImportErrors."],"tags":["pytest","monkeypatch","import","setattr"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}