Descriptors misbehave with `NoReturn`/`Never`, with `@overload`s, etc.
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Bug Report
To Reproduce
Gists:
mypy-play.net
pyright-play.net (pyright doesn't handle this well, issuing no warnings at all; it does respond to reveal_type() in the descriptors-used-as-intended case, suggesting it doesn't understand that directive is unreachable)
from __future__ import annotations
from typing_extensions import (
NoReturn,
Optional,
overload,
Self,
Type,
)
# Note: This is a simplified example.
#
# - obviously, it would be more expedient to simply omit the definition
# of '__get__()' for this trivial case
#
# - similar problems affect implicitly calling '__set__()' through a
# descriptor
class WriteOnlyDescr:
'''some kind of write-only descriptor (to demonstrate the general issue)
'''
@overload # descriptor accessible through class ('SomeClass')
def __get__(self, owner: None, objtype: Type[object]) -> Self: ...
@overload
def __get__( # access on an instance of 'SomeClass' (forbidden)
self, owner: object, objtype: Optional[Type[object]] = None
) -> NoReturn: ...
def __get__(
self, owner: Optional[object],
objtype: Optional[Type[object]] = None,
) -> Self:
# allow access to this descriptor via the class:
if owner is None:
return self
raise AttributeError
# '__set__()' omitted for brevity
class SomeClass:
'''a class with a write-only descriptor
'''
descr = WriteOnlyDescr()
descr = SomeClass.descr
inst = SomeClass()
if bool():
# First case:
#
# First, we'll "spell out" the descriptor mechanics; this should
# give the same results as using the descriptor in a natural way
# (the second case)
# EXPECTED for the second case:
# ──────────────────────────────────────
get = descr.__get__(inst) # ← mypy: [var-annotated]
# "Need type annotation for 'get'"
reveal_type(get) # ← (no mypy output; this is unreachable)
_ = True # ← mypy: [unreachable]
else: # ↑ mypy output _should_ be identical ↑
# ↓ above and below this point ↓
# Second case:
#
# This _should_ give the same output as above, since the first
# lines of the two cases are virtually synonymous, while the
# remaining lines are identical.
# ACTUAL for the second case:
# ──────────────────────────────────────
get = inst.descr # ← (no mypy output)
reveal_type(get) # ← mypy: "Revealed type is 'Never'"
_ = True # ← (no mypy output)
Expected Behavior
Descriptors should behave the same way when used as intended and when the mechanics are spelled out (explicit calls to __get__(), etc.).
Actual Behavior
This isn't the case, especially when an argument has type Never or a function is marked NoReturn. (This can come up when @overloads are in use.)
Your Environment
- Mypy version used: 1.8.0
- Mypy command-line flags:
--warn-unreachableis informative - Mypy configuration options from
mypy.ini(and other config files): none - Python version used: 3.8, 3.12
Further context
...follows in a comment.
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, das vereinfachte Beispiel aus dem verlinkten Fall auf mypy-play.net mit mypy 1.8.0 und --warn-unreachable zu reproduzieren, wobei explizite get-Aufrufe mit dem natürlichen Zugriff auf Deskriptoren verglichen werden. Verfolge die Behandlung von Deskriptoren und Overloads im Zusammenhang mit NoReturn/Never und füge anschließend Regressionstests hinzu, sodass beide Formen gleichwertige Diagnosen und aufgedeckte Typen erzeugen.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- compilers
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100