{"record":{"id":"94bc920c6deecf25","repo":"aosabook/500lines","slug":"fieldname-94bc92","errorCode":null,"errorMessage":"{fieldname}","messagePattern":"\\{fieldname\\}","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"objmodel/objmodel.markdown","lineNumber":517,"sourceCode":"it needs to be turned into a bound method. To emulate a bound method we simply\nuse a closure. In addition to changing ``Base.read_attr`` we can also change\n``Base.callmethod`` to use the new approach to calling methods to make sure all\nthe tests still pass.\n\n```python\nclass Base(object):\n    ...\n    def read_attr(self, fieldname):\n        \"\"\" read field 'fieldname' out of the object \"\"\"\n        result = self._read_dict(fieldname)\n        if result is not MISSING:\n            return result\n        result = self.cls._read_from_class(fieldname)\n        if _is_bindable(result):\n            return _make_boundmethod(result, self)\n        if result is not MISSING:\n            return result\n        raise AttributeError(fieldname)\n\n    def callmethod(self, methname, *args):\n        \"\"\" call method 'methname' with arguments 'args' on object \"\"\"\n        meth = self.read_attr(methname)\n        return meth(*args)\n\ndef _is_bindable(meth):\n    return callable(meth)\n\ndef _make_boundmethod(meth, self):\n    def bound(*args):\n        return meth(self, *args)\n    return bound\n\n```\n\nThe rest of the code does not need to be changed at all.\n","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/aosabook/500lines/blob/fba689d101eb5600f5c8f4d7fd79912498e950e2/objmodel/objmodel.markdown#L499-L535","documentation":"Raised by Base.read_attr in the minimal object model (objmodel chapter). read_attr first checks the instance dictionary, then the class via _read_from_class; if the value is not MISSING and is callable it is bound as a method, otherwise if still not MISSING it is returned. Only when lookup fully fails does it raise AttributeError(fieldname). This mirrors CPython's attribute-not-found semantics inside the toy object model.","triggerScenarios":"Calling obj.read_attr(name) for a name that was never written to the instance and is not present on the instance's class (or its base chain). It does NOT fire for methods, which are returned as bound methods when found on the class.","commonSituations":"Calling a reader for a field that was mistyped, not initialised in __init__, or shadowed by a write_attr of a different name; traversing the base_class chain that lacks the field.","solutions":["Check the exact fieldname spelling against the write_attr call that set it.","Ensure the field is set on the instance or defined on its Class before reading.","Define a __getattr__ on the class to compute/fallback missing fields (see error 46).","Add a has_attr helper that probes _read_dict and _read_from_class before calling read_attr."],"exampleFix":"# before\nobj.read_attr('colur')   # typo -> AttributeError('colur')\n\n# after\nMISSING = object()\ndef has_attr(obj, name):\n    if obj._read_dict(name) is not MISSING:\n        return True\n    return obj.cls._read_from_class(name) is not MISSING\nif has_attr(obj, 'color'):\n    obj.read_attr('color')","handlingStrategy":"validation","validationCode":"MISSING = object()\ndef has_attr(obj, name):\n    if obj._read_dict(name) is not MISSING:\n        return True\n    found = obj.cls._read_from_class(name)\n    return found is not MISSING\n\nif not has_attr(obj, name):\n    raise KeyError(name)\nobj.read_attr(name)","typeGuard":"def field_present(obj, name):\n    return obj._read_dict(name) is not MISSING or obj.cls._read_from_class(name) is not MISSING","tryCatchPattern":"try:\n    return obj.read_attr(name)\nexcept AttributeError:\n    return default","preventionTips":["Initialise every field in __init__ before any read_attr can run.","Centralise field-name constants to avoid typos.","Prefer adding a __getattr__ for computed fields over scattering lookups.","Write a read_attr-or-default helper so callers never see the raw AttributeError."],"tags":[],"backgroundTag":null,"analyzedSha":"fba689d101eb5600f5c8f4d7fd79912498e950e2","analyzedAt":"2026-08-13T06:26:32.792Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}