aosabook/500lines · error · AttributeError

{fieldname}

Error message

{fieldname}

What it means

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.

Source

Thrown at objmodel/objmodel.markdown:517

it needs to be turned into a bound method. To emulate a bound method we simply
use a closure. In addition to changing ``Base.read_attr`` we can also change
``Base.callmethod`` to use the new approach to calling methods to make sure all
the tests still pass.

```python
class Base(object):
    ...
    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
        raise AttributeError(fieldname)

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

def _is_bindable(meth):
    return callable(meth)

def _make_boundmethod(meth, self):
    def bound(*args):
        return meth(self, *args)
    return bound

```

The rest of the code does not need to be changed at all.

View on GitHub (pinned to fba689d101)

Solutions

  1. Check the exact fieldname spelling against the write_attr call that set it.
  2. Ensure the field is set on the instance or defined on its Class before reading.
  3. Define a __getattr__ on the class to compute/fallback missing fields (see error 46).
  4. Add a has_attr helper that probes _read_dict and _read_from_class before calling read_attr.

Example fix

# before
obj.read_attr('colur')   # typo -> AttributeError('colur')

# after
MISSING = object()
def has_attr(obj, name):
    if obj._read_dict(name) is not MISSING:
        return True
    return obj.cls._read_from_class(name) is not MISSING
if has_attr(obj, 'color'):
    obj.read_attr('color')
Defensive patterns

Strategy: validation

Validate before calling

MISSING = object()
def has_attr(obj, name):
    if obj._read_dict(name) is not MISSING:
        return True
    found = obj.cls._read_from_class(name)
    return found is not MISSING

if not has_attr(obj, name):
    raise KeyError(name)
obj.read_attr(name)

Type guard

def field_present(obj, name):
    return obj._read_dict(name) is not MISSING or obj.cls._read_from_class(name) is not MISSING

Try / catch

try:
    return obj.read_attr(name)
except AttributeError:
    return default

Prevention

When it happens

Trigger: 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.

Common situations: 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.


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