Search code examples
pythonmypypython-typingpython-dataclasses

mypy ignores wrong type in dataclass member


This code contains a huge type error, which mypy misses:

from dataclasses import dataclass

class WrongThing:
    def do_wrong_way(self):
        pass

class RightThing:
    def do_right_way(self):
        pass

@dataclass
class Holder:
    thing: RightThing
    def fill_thing(self):
        self.thing.do_wrong_way()  # <--- This is wrong

m = Holder(RightThing())
m.fill_thing()

Why doesn't mypy object that self.thing lacks a do_wrong_way attribute? Am I doing something, er, wrong?

mypy does correctly flag this code with an [attr-defined] error:

r = RightThing()
r.do_wrong_way()   # <--- [attr-defined] error

Solution

  • Let this be a lesson never to leave a function definition unannotated, even if it seems trivial!

    Mypy passes this code because MyPy's default setting is to ignore functions that "have no type annotations", as it is assumed that the user wants these to be "dynamic functions" that the user doesn't want type-checked. This is mentioned in the documentation here.

    In your Holder class, you have no annotations in the signature of the fill_thing method at all, so mypy just doesn't look at it at all. If you change your Holder class to this, so that fill_thing is explicitly annotated as returning None:

    @dataclass
    class Holder:
        thing: RightThing
        def fill_thing(self) -> None:
            self.thing.do_wrong_way()
    

    ... Then Mypy raises an error, just as we'd expect!


    How to avoid these errors in the future

    I recommend always running mypy with the --strict setting, as with strict=True, MyPy will warn you if you leave any functions without type annotations. While the default setting of "we won't raise errors if you leave the function unannotated" is probably more beginner-friendly, I personally find it just leads to an unfortunate number of bugs if you don't use Mypy with the --strict setting.

    Another option, if you'd like unannotated functions to be checked, but don't want MyPy to be quite as strict as it is with the --strict option, is to run MyPy with the --check-untyped-defs option. A full list of command-line options can be found in the documentation here.