aosabook/500lines · error · NotImplementedError

The Abstract Node Class doesn't define 'render_self'

Error message

The Abstract Node Class doesn't define 'render_self'

What it means

Raised by Node.render_self in the modeller scene-graph (a 500 Lines OpenGL example). Node is an abstract base: its render_self raises NotImplementedError to enforce that every concrete node type supplies its own drawing logic. Subclasses such as Primitive override render_self (here calling glCallList). The error fires only when a node reaches rendering without an overriding render_self.

Source

Thrown at modeller/modeller.markdown:308

    def render(self):
        """ renders the item to the screen """
        glPushMatrix()
        glMultMatrixf(numpy.transpose(self.translation_matrix))
        glMultMatrixf(self.scaling_matrix)
        cur_color = color.COLORS[self.color_index]
        glColor3f(cur_color[0], cur_color[1], cur_color[2])
        if self.selected:  # emit light if the node is selected
            glMaterialfv(GL_FRONT, GL_EMISSION, [0.3, 0.3, 0.3])
        
        self.render_self()

        if self.selected:
            glMaterialfv(GL_FRONT, GL_EMISSION, [0.0, 0.0, 0.0])
        glPopMatrix()

    def render_self(self):
        raise NotImplementedError(
            "The Abstract Node Class doesn't define 'render_self'")

class Primitive(Node):
    def __init__(self):
        super(Primitive, self).__init__()
        self.call_list = None

    def render_self(self):
        glCallList(self.call_list)


class Sphere(Primitive):
    """ Sphere primitive """
    def __init__(self):
        super(Sphere, self).__init__()
        self.call_list = G_OBJ_SPHERE

View on GitHub (pinned to fba689d101)

Solutions

  1. Confirm the offending node is a concrete subclass, not Node itself.
  2. Implement render_self in the subclass that performs the OpenGL drawing.
  3. If using abc, decorate Node.render_self with @abstractmethod and inherit ABC so instantiation fails early instead of at render time.
  4. Search the node list for any Node() or un-overridden subclass instance.

Example fix

# before: subclass added without overriding render_self
class Cube(Node):
    pass
scene.append(Cube())   # render -> NotImplementedError

# after: implement render_self (or delegate to a call list)
class Cube(Primitive):
    def __init__(self):
        super(Cube, self).__init__()
        self.call_list = build_cube_list()
    # Primitive.render_self calls glCallList(self.call_list)
Defensive patterns

Strategy: type-guard

Validate before calling

# ensure every scene node can render before adding it
from abc import ABCMeta, abstractmethod

class Node(object, metaclass=ABCMeta):
    @abstractmethod
    def render_self(self):
        raise NotImplementedError

Type guard

def can_render(node):
    return node is not None and getattr(type(node).render_self, '__isabstractmethod__', False) is False

Try / catch

try:
    node.render_self()
except NotImplementedError:
    log.error('abstract node %r in scene', type(node).__name__)

Prevention

When it happens

Trigger: Instantiating Node directly and adding it to the scene; subclassing Node (or a non-Primitive intermediate) and forgetting to implement render_self; a node whose render_self was renamed or deleted so the base method resolves.

Common situations: Adding a new node subclass to the scene graph but leaving rendering stubbed; refactoring that accidentally reintroduces the abstract base into the node list; copy-paste inheritance where a subclass still calls super().render_self().


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