Remove mypy [attr-defined] error from SQLAlchemy empty class

2023-11-05

In SQLAlchemy, empty classes are commonly used for imperative mapping and database reflection.

If you’re using mypy, then when you access their attributes, you will get an [attr-defined] error which can (and will) very quickly drown the useful mypy errors into a wall of noise you won’t even look at.

Assuming you are in control of the empty class definition, you can use the following inheritance trick with typing.Any to remove that noise.

import typing

if typing.TYPE_CHECKING:
    _EmptyClass = typing.Any
else:
    _EmptyClass = object

class User(_EmptyClass):
    pass

This will make User be a subclass of typing.Any when running mypy, which will stop all those [attr-defined] errors, but be a regular class1 at runtime.

We need this trick because until python 3.11, typing.Any could not be inherited at run time as it would cause

TypeError: Cannot subclass typing.Any

If you can limit your support to python >= 3.11, then you can simplify the recipe by inheriting typing.Any directly.


  1. Classes without an inheritance list inherit, by default, from the base class object; hence, class Foo: pass is equivalent to class Foo(object): pass. (python documentation↩︎