Desk Clarity

A field guide to everyday digital problems.

Developer tools / Practical guide

Python exec Method Cannot Find the Class Cell: Choose an Explicit super Anchor

Diagnose the missing class cell in an exec-generated method and test explicit super anchors across inheritance.

By ON Lab3 min readSources checked November 10, 2026

If a method defined by exec raises RuntimeError: super(): __class__ cell not found, assigning it to a class did not give its compiled body the implicit class context used by zero-argument super(). Prefer an ordinary method when possible. For a genuinely generated method, an explicit super(Derived, self) can avoid that missing context when Derived is the class where the method belongs.

Reproduce the compilation boundary

An October 2026 Python Help question generated just a method inside an otherwise conventional class body. The method could be assigned successfully but failed only when called. The question and its replies already identify the explicit two-argument form; this guide adds an executable inheritance check and explains why using the runtime instance class as the anchor is a different operation.

class Base:
    def label(self):
        return ["base"]

class Broken(Base):
    exec("def label(self): return super().label()")

assert "__class__" not in Broken.label.__code__.co_freevars
try:
    Broken().label()
except RuntimeError as exc:
    assert "__class__ cell not found" in str(exc)
else:
    raise AssertionError("expected missing class context")

The inspected free-variable tuple is diagnostic evidence for these local CPython runs, rather than a portable contract for every implementation. The data model documents the implicit class reference used for the zero-argument form. An independently compiled string is not automatically compiled as if its source had appeared in the surrounding class statement.

Prefer a normal method when generation is unnecessary

class Base:
    def label(self):
        return ["base"]

class Ordinary(Base):
    def label(self):
        return super().label() + ["ordinary"]

assert Ordinary().label() == ["base", "ordinary"]
assert "__class__" in Ordinary.label.__code__.co_freevars

This keeps the method's class context available without editing closure cells. If only data varies, a normal method that consumes generated data can be simpler than generating source code. The best choice depends on the generator's real requirements; replacing an entire class framework is unnecessary just to diagnose this error.

Use the defining class in the explicit form

class Base:
    def label(self):
        return ["base"]

class Derived(Base):
    exec("def label(self): return super(Derived, self).label() + ['derived']")

class Child(Derived):
    pass

assert Derived().label() == ["base", "derived"]
assert Child().label() == ["base", "derived"]

Here the generated function looks up Derived in its global namespace when called, after the class statement has finished binding that name. Calling it before that binding exists is a different failure. If you pass a separate globals dictionary to exec, that namespace must contain the required class binding at call time. The displayed example deliberately uses the normal module namespace.

Do not mechanically substitute super(type(self), self). On an inherited method called through Child, that starts after Child and can find the same Derived.label again, causing recursion. The fixture demonstrates that failure separately. An explicit defining-class anchor starts at the intended point in the instance's method resolution order.

Preserve cooperative inheritance behavior

Calling Base.label(self) directly may appear to fix the small example, but names a specific implementation and can skip another participating class. super(Derived, self) searches after Derived within the instance's method resolution order. Our multiple-inheritance fixture inserts a mixin between Derived and Base and confirms that the explicit form visits it.

Rebinding or reusing the global class name later changes what the generated function looks up. Treat that name as part of the generator's design; the snippet is not a universal method factory. Class methods, unusual descriptors and generators with custom globals require their own review. The article does not recommend closure surgery merely to retain the shorter spelling.

Only the fixed, locally authored strings shown here were executed. Executing customer-supplied source is outside this example. Regression checks passed on local CPython 3.11.6, 3.13.2 and 3.14.3 for the original failure, ordinary methods, explicit anchors, inherited calls and cooperative multiple inheritance. No claim is made that the reader's complete generator was tested.

Sources & further reading