Decorated __init__ confuses callback type if decorator is defined with type alias or if decorator is imported using relative import
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
This one's just weird. I'm not sure if this is a dup of #1927, but I figured I'd raise it anyway.
# test_case.py
from typing import Callable, Iterable, TypeVar, Union
_T = TypeVar("_T")
def _identity(__: _T) -> _T:
return __
# decr: Callable[[_T], _T] = _identity # <-- this approach works
DecoratorT = Callable[[_T], _T]
decr: DecoratorT = _identity # <-- this causes the false positives below
class Result:
@decr
def __init__(self):
pass
res = Result() # ; reveal_type(res) # -> Any (should be test_case.Result)
def some_results(op: Callable[[], Union[Result, Iterable[Result]]]) -> Iterable[Result]:
result_or_results = op()
results: Iterable[Result]
if isinstance(result_or_results, Result):
results = (result_or_results,) # ; reveal_type(result_or_results) # -> (should be test_case.Result)
else:
results = result_or_results # ; reveal_type(result_or_results) # -> Union[test_case.Result, typing.Iterable[test_case.Result]] (should be typing.Iterable[test_case.Result])
return results
% python --version ; mypy --version
Python 3.9.7
mypy 0.910
% mypy --config-file=pyproject.toml test_case.py
test_case.py:26: error: Incompatible types in assignment (expression has type "Union[Result, Iterable[Result]]", variable has type "Iterable[Result]")
Found 1 error in 1 file (checked 1 source file)
Note that if DecoratorT is defined as follows, things work again:
# …
class DecoratorT(Protocol):
def __call__(self, __: _T) -> _T:
...
decr: DecoratorT = _identity # <-- all good
# …
But if we move the decorator into its own module and use a relative import, it breaks no matter how we define the decorator type:
# test_case.py
from typing import Callable, Iterable, TypeVar, Union
# from test_case_import import decr # <-- this works
from .test_case_import import decr # <-- this causes the false positives below
class Result:
@decr
def __init__(self):
pass
res = Result() # ; reveal_type(res) # -> Any (should be test_case.Result)
def some_results(op: Callable[[], Union[Result, Iterable[Result]]]) -> Iterable[Result]:
result_or_results = op()
results: Iterable[Result]
if isinstance(result_or_results, Result):
results = (result_or_results,) # ; reveal_type(result_or_results) # -> (should be test_case.Result)
else:
results = result_or_results # ; reveal_type(result_or_results) # -> Union[test_case.Result, typing.Iterable[test_case.Result]] (should be typing.Iterable[test_case.Result])
return results
# test_case_import.py
from typing import Callable, TypeVar
_T = TypeVar("_T")
def _identity(__: _T) -> _T:
return __
decr: Callable[[_T], _T] = _identity # <-- doesn't matter how this is defined
% mypy --config-file=pyproject.toml test_case.py test_case_import.py
test_case.py:19: error: Incompatible types in assignment (expression has type "Union[Result, Iterable[Result]]", variable has type "Iterable[Result]")
Found 1 error in 1 file (checked 2 source files)
Using the Protocol approach does not salvage the second (relative import) scenario.
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne damit, die gemeldeten Diagnosen mit mypy in test_case.py und test_case_import.py zu reproduzieren, und vergleiche dabei die Fälle mit lokalem und relativ importiertem Decorator. Verfolge, wie mypy den dekorierten init und die Typen des importierten Decorators ableitet. Als erledigt gilt die Aufgabe, wenn die Beispiele Result und Iterable[Result] wie annotiert ableiten, ohne den fälschlicherweise ausgelösten Zuweisungsfehler.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- devtools
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100