{"record":{"id":"f58a4dd2d1ed3ba4","repo":"pytest-dev/pytest","slug":"type-obj-name-r-object-at-ann-has-no-attr","errorCode":null,"errorMessage":"{type(obj).__name__!r} object at {ann} has no attribute {name!r}","messagePattern":"(.+?) object at (.+?) has no attribute (.+?)","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/monkeypatch.py","lineNumber":97,"sourceCode":"        # 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)\n    target = resolve(module)\n    if raising:\n        annotated_getattr(target, attr, ann=module)\n    return attr, target\n\n\n@final\nclass MonkeyPatch:\n    \"\"\"Helper to conveniently monkeypatch attributes/items/environment","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/monkeypatch.py#L79-L115","documentation":"An AttributeError raised by annotated_getattr when an object along the dotted import path does not have the requested attribute. Unlike a bare getattr, this version annotates the error with the type of the object and the human-readable path segment, making it easier to locate which part of 'a.b.c' was missing the attribute.","triggerScenarios":"Calling `monkeypatch.setattr('mymod.MyClass.missing_attr', value)` where MyClass exists but `missing_attr` does not; or `monkeypatch.delattr` on a non-existent attribute with raising=True. The annotated path is the module string passed in.","commonSituations":"Typos in the attribute name, version skew where an attribute was renamed/removed, patching a method that lives on a subclass, or wrong module re-export.","solutions":["Verify the attribute exists: `python -c 'from mymod import MyClass; print(hasattr(MyClass, 'attr'))'`.","Check the spelling and the class/module you are patching; use the leaf object directly.","Pass raising=False if you intend to create a new attribute (for setattr).","Update the path to match the current library version."],"exampleFix":"# before\nmonkeypatch.setattr('mymod.MyClass.notamethod', new)\n# after\nmonkeypatch.setattr('mymod.MyClass.realmethod', new)","handlingStrategy":"validation","validationCode":"def attr_exists(dotted: str) -> bool:\n    parts = dotted.rsplit('.', 1)\n    if len(parts) != 2:\n        return False\n    mod, attr = parts\n    try:\n        import importlib\n        target = importlib.import_module(mod)\n    except ImportError:\n        return False\n    return hasattr(target, attr)","typeGuard":null,"tryCatchPattern":"try:\n    monkeypatch.setattr('mymod.X.attr', value)\nexcept AttributeError as e:\n    # the message names the type and missing attribute\n    ...","preventionTips":["Use hasattr to confirm the attribute before patching.","Patch the object that actually owns the attribute, not a re-export.","Keep attribute names in sync with library upgrades."],"tags":["pytest","monkeypatch","attribute-error","import-path","setattr"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}