Skip to content

Order of declarations matters, but it shouldn't #14509

Closed
@finite-state-machine

Description

@finite-state-machine

Bug Report

The order of declarations shouldn't matter to mypy, but in the example that follow, they do.

To Reproduce

(Exactly as written below: mypy-play.net)
(With slightly better comments, but different line numbers: mypy-play.net)

from __future__ import annotations

import functools
from typing import (
        Any,
        Callable,
        cast,
        Counter,
        Optional,
        TypeVar,
        )


Tc = TypeVar('Tc', bound=Callable[..., Any])


def func_wraps(wraps: Tc) -> Callable[[Callable[..., Any]], Tc]:
    '''declare that one function wraps another

    Args:
        wraps: the function being wrapped

    Returns:
        a function 'decorator()', where...
        Args:
            the function which wraps 'wraps'
        Returns:
            the input argument, unchanged

    The type annotations of the return value of the 'decorator()' will
    be those of 'wraps'.
    '''
    def inner(wrapper: Callable[..., Any], /) -> Tc:
        '''decorator

        Args:
            wrapper: the function which wraps 'wraps'
        Returns:
            the same function, unchanged
        '''
        wrapper_ = cast(Tc, wrapper)
        return functools.wraps(wraps)(wrapper_)
    return inner


reveal_type(ParentClass)
reveal_type(ChildClass)
reveal_type(ParentClass.__init__)
reveal_type(ChildClass.__init__)


def function1() -> ChildClass:
    return ChildClass()


class ParentClass:
    def __init__(self, *, param: Optional[int] = None) -> None:
        _ = param

class ChildClass(ParentClass):
    some_counter: Counter[int]

    @func_wraps(ParentClass.__init__)
    def __init__(self, *args: Any, **kwargs: Any):
        super().__init__(*args, **kwargs)
        self.some_counter = Counter()


def function2() -> ChildClass:
    return ChildClass()


reveal_type(ParentClass)
reveal_type(ChildClass)
reveal_type(ParentClass.__init__)
reveal_type(ChildClass.__init__)

Expected Behavior

The reveal_type lines and function1 declaration, found before the class definitions, should result in the same output as those found after the class definitions.

mypy's output should be (lines which differ are marked with a "*"):

  bug.py:47: note: Revealed type is "def (*, param: Union[builtins.int, None] =) -> bug.ParentClass"
* bug.py:48: note: Revealed type is "def (*, param: Union[builtins.int, None] =) -> bug.ChildClass"
  bug.py:49: note: Revealed type is "def (self: bug.ParentClass, *, param: Union[builtins.int, None] =)"
* bug.py:50: note: Revealed type is "def (self: bug.ParentClass, *, param: Union[builtins.int, None] =)"
  [edit: removed line that shouldn't have been here]
  bug.py:74: note: Revealed type is "def (*, param: Union[builtins.int, None] =) -> bug.ParentClass"
  bug.py:75: note: Revealed type is "def (*, param: Union[builtins.int, None] =) -> bug.ChildClass"
  bug.py:76: note: Revealed type is "def (self: bug.ParentClass, *, param: Union[builtins.int, None] =)"
  bug.py:77: note: Revealed type is "def (self: bug.ParentClass, *, param: Union[builtins.int, None] =)"
* Success: no issues found in 1 source file

Actual Behavior

The first group ofreveal_type lines and the function1 declaration misbehave, while everything after the class definitions behaves as expected.

The actual output is:

  bug.py:47: note: Revealed type is "def (*, param: Union[builtins.int, None] =) -> bug.ParentClass"
* bug.py:48: note: Revealed type is "Any"
  bug.py:49: note: Revealed type is "def (self: bug.ParentClass, *, param: Union[builtins.int, None] =)"
* bug.py:50: note: Revealed type is "Any"
* bug.py:54: error: Returning Any from function declared to return "ChildClass"  [no-any-return]
  bug.py:74: note: Revealed type is "def (*, param: Union[builtins.int, None] =) -> bug.ParentClass"
  bug.py:75: note: Revealed type is "def (*, param: Union[builtins.int, None] =) -> bug.ChildClass"
  bug.py:76: note: Revealed type is "def (self: bug.ParentClass, *, param: Union[builtins.int, None] =)"
  bug.py:77: note: Revealed type is "def (self: bug.ParentClass, *, param: Union[builtins.int, None] =)"
* Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 0.991, master (2023-01-23)
  • Mypy command-line flags: --follow-imports=silent --show-error-codes --strict --warn-unreachable
  • Mypy configuration options from mypy.ini (and other config files): N/A
  • Python version used: 3.8, 3.11

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions