aosabook/500lines · error · AttributeError

fieldname

Error message

fieldname

What it means

Raised by Base.read_attr in the 04-maps objmodel. Lookup order is identical to the 03-customizable variant (instance storage -> class MRO -> __getattr__), but instance storage is backed by a Map/shape mechanism rather than a plain dict. Reaching this raise means the field is absent from the instance's indexed storage, the class MRO, and no __getattr__ resolved it.

Source

Thrown at objmodel/code/04-maps/objmodel.py:24

    def __init__(self, cls, fields):
        """ Every object has a class. """
        self.cls = cls
        self._fields = fields

    def read_attr(self, fieldname):
        """ read field 'fieldname' out of the object """
        result = self._read_dict(fieldname)
        if result is not MISSING:
            return result
        result = self.cls._read_from_class(fieldname)
        if _is_bindable(result):
            return _make_boundmethod(result, self)
        if result is not MISSING:
            return result
        meth = self.cls._read_from_class("__getattr__")
        if meth is not MISSING:
            return meth(self, fieldname)
        raise AttributeError(fieldname)

    def write_attr(self, fieldname, value):
        """ write field 'fieldname' into the object """
        meth = self.cls._read_from_class("__setattr__")
        return meth(self, fieldname, value)

    def isinstance(self, cls):
        """ return True if the object is an instance of class cls """
        return self.cls.issubclass(cls)

    def callmethod(self, methname, *args):
        """ call method 'methname' with arguments 'args' on object """
        meth = self.read_attr(methname)
        return meth(*args)

    def _read_dict(self, fieldname):
        """ read an field 'fieldname' out of the object's dict """
        return self._fields.get(fieldname, MISSING)

View on GitHub (pinned to fba689d101)

Solutions

  1. Store the field through write_attr so it is registered in the instance's Map storage.
  2. Add the attribute to the Class fields so it resolves via _read_from_class.
  3. Provide a __getattr__ on the class to handle unknown names gracefully.

Example fix

// before
obj.read_attr('height')   # AttributeError: height
// after
obj.write_attr('height', 180)
obj.read_attr('height')
Defensive patterns

Strategy: validation

Validate before calling

MISSING = object()

def safe_read(obj, fieldname, default=None):
    if obj._read_dict(fieldname) is not MISSING:
        return obj._read_dict(fieldname)
    cls_val = obj.cls._read_from_class(fieldname)
    if cls_val is not MISSING:
        return cls_val
    return default

Type guard

def has_field_or_getattr(obj, name):
    MISSING = object()
    return (obj._read_dict(name) is not MISSING
            or obj.cls._read_from_class(name) is not MISSING
            or obj.cls._read_from_class('__getattr__') is not MISSING)

Try / catch

try:
    val = obj.read_attr(fieldname)
except AttributeError:
    val = <default>

Prevention

When it happens

Trigger: Reading a field that was never stored on the instance and is not present in the class MRO, while the class defines no usable __getattr__. Because storage is map-indexed, the field must have been added through the Map's add_attribute path to be readable.

Common situations: Writing a field via a different storage path than the Map expects; referencing attributes before they are allocated in the instance's Map; typos; missing __getattr__ coverage.

Related errors


AI-assisted analysis of aosabook/500lines@fba689d101 (2026-08-13). Data as JSON: /api/errors/e6b8ab2e1ff9657f. Report an issue: GitHub.