Skip to content

Decorator on __init__ causes loss of type info #5398

Closed
softagram/mypy
#2

Description

@campkeith

Here's a simple example:

from __future__ import annotations
from typing import Callable

def decorator(method: Callable[[Foo], None]) -> Callable[[Foo], None]:
    return method

class Foo():
    @decorator
    def __init__(self):
        pass

reveal_type(Foo.__init__)

The output of mypy is:

% mypy --version
mypy 0.620
% mypy foo.py
foo.py:13: error: Revealed type is 'Any'

This seems to be specific to methods as similar type inferences work fine for functions outside of the class scope.

Activity

  1. gvanrossum commented on Jul 27, 2018

    @gvanrossum
    Member

    Try again after adding -> None to the __init__ signature.

  2. campkeith commented on Jul 29, 2018

    @campkeith
    Author

    Adding annotations to __init__ does not change the result; here's the updated code:

    from __future__ import annotations
    
    from typing import Callable
    
    def decorator(method: Callable[[Foo], None]) -> Callable[[Foo], None]:
        return method
    
    class Foo():
        @decorator
        def __init__(self: Foo) -> None:
            pass
    
    reveal_type(Foo.__init__)
    

    I get the same result (the revealed type is Any). If I change the name of the method, the type inference works correctly, so this issue seems to be specific to __init__.

  3. changed the title [-]Method decorator causes loss of type info[/-] [+]Decorator on __init__ causes loss of type info[/+] on Jul 29, 2018
  4. gvanrossum commented on Jul 29, 2018

    @gvanrossum
    Member

    __init__ is special in a number of ways, but this still seems wrong.

  5. ilevkivskyi commented on Sep 17, 2018

    @ilevkivskyi
    Member

    I just stumbled at this again. Even precisely typed decorator on __init__ not just erases types, it completely erases the signature of __init__. For example:

    from typing import Any, Callable, TypeVar
    
    _C = TypeVar("_C", bound=Callable[..., None])
    
    def decorator(f: _C) -> _C:
        return f
        
    class C:
        @decorator
        def __init__(self, x: int) -> None:
            ...
            
    C("whatever", "works", "now") + 42
  6. JukkaL commented on Sep 18, 2018

    @JukkaL
    Collaborator

    Increased priority to "high" since this behavior is very unexpected and can hide bugs.

  7. TV4Fun commented on Oct 13, 2018

    @TV4Fun
    Contributor

    It looks like #1706 is a special case of this. FYI, I am working on a PR to fix this. It seems like the problem is that quite a few places check whether a node represents a callable function by checking if it is an instance of FuncBase, which Decorator is not. We don't want to make Decorator inherit from FuncBase, because whether it is callable or not depends on the type returned by the decorator, and we don't want to assume that it is, so my thought was to override __instancecheck__ in FuncBase to specifically check and return true if the decorated function is callable. There I still some issues I need to work out with my implementation—my current version has some false positives that I am trying to track down, but if anyone would like to offer input on what I'm doing, my code is at https://github.com/TV4Fun/mypy/tree/fix_decorators

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions