python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.26k stars 2.79k forks source link

Special methods such as `object.__del__` should have pre-defined types #3056

Open nodakai opened 7 years ago

nodakai commented 7 years ago

This should be rejected, for example

class C(object):
  def __del__(self, a):
    # type: (int) -> int
    return a

Probably this as well

class C(object):
  def __del__(self):
    # type: () -> int
    return 0

Cf. https://docs.python.org/3/reference/datamodel.html#basic-customization

JelleZijlstra commented 7 years ago

Mypy already does this for a few methods (see e.g. check_getattr_method in checker.py); it shouldn't be hard to add more.

gvanrossum commented 7 years ago

Isn't it just a matter of adding those special methods to the object class in typeshed?

nodakai commented 7 years ago

@gvanrossum Hmm, I misunderstood they are only applicable to new-style classes but that was not the case:

class C:
  def __str__(self):
    # type: () -> int
    return 0
/tmp/a.py:2: error: Return type of "__str__" incompatible with supertype "object"      

So the case of __del__ is indeed just a matter of adding a new entry to typeshed.

But I guess we really need built-in rules for __setitem__ and friends which have to care about type variances?

gvanrossum commented 7 years ago

Hm, mypy doesn't really understand the difference between old-style and new-style classes.

For __setitem__, can you provide some code examples for what you're thinking of?

JukkaL commented 7 years ago

object doesn't define __del__, so we maybe shouldn't add that to typeshed. Otherwise code like this would generate false negatives:

object().__del__()  # should not be valid

Also we could use super to call __del__ from object even though it doesn't exist.

These are the attributes object has in Python 2.7:

__class__
__delattr__
__doc__
__format__
__getattribute__
__hash__
__init__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__

We'd need to special case other special methods in mypy.

gvanrossum commented 7 years ago

Hm, I wonder if we couldn't take the legalistic approach that mypy doesn't keep track of whether something is always define, only of its type if it is defined. After all from

class C:
    def foo(self) -> None:
        self.bar = 42
a = C()
print(a.x)  # Runtime error

we infer that C has an attribute bar of type int, and we don't keep track of whether it is set or not.

We could do the same for __del__ and others, so they provide constraints for subclasses (which is IIUC what's asked for in this issue).

JukkaL commented 7 years ago

Mypy doesn't check the definedness of various things because it's hard to do, not because avoiding the checks is the right thing to do. I'd rather move towards eventually catching some cases of undefined attributes rather than making things less strict.

If we'd define all dunder methods in object, then due to inheritance mypy (or users looking at the stubs) might think that x.__del__() is valid for all objects (including instances of user-defined classes), anything can be added to, anything can be indexed, etc. even though these clearly aren't true.