Descriptors misbehave with `NoReturn`/`Never`, with `@overload`s, etc.
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 20.6k
- Fork
- 3.3k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
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.
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia riproducendo l’esempio semplificato del caso collegato su mypy-play.net con mypy 1.8.0 e --warn-unreachable, confrontando le chiamate esplicite a get con l’accesso naturale ai descrittori. Traccia la gestione dei descrittori e degli overload coinvolta in NoReturn/Never, quindi aggiungi una copertura di regressione in modo che entrambe le forme producano diagnostica e tipi rivelati equivalenti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- python
- Ambito
- compilers
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 35/100