MyPy fails type narrowing on `not issubclass(X, type[T]): raise`
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- Python
- Estrellas
- 20.6k
- Forks
- 3.3k
- Métricas de merge de PR
- Métricas de PR pendientes
Descripción
Bug Report
MyPy fails to narrow types using issubclass, and expects an isinstance check which is wrong.
MyPy fails type narrowing on not issubclass(X, type[T]): raise and complains about returning X() later for the function marked as returning T.
To Reproduce
[Mypy Playground example](https://gist.github.com/mypy-play/dcd17343b289523c197a31f850eca90f)
```python from collections.abc import Iterator from typing import TYPE_CHECKING
class MySentinel:
pass
# Works as expected
class OptionalSentinel:
def __init__(self, value: object) -> None:
self._value = value
def get_value[T](self, expected: type[T]) -> Iterator[T]:
if not isinstance(self._value, expected):
raise TypeError
yield self._value
# Reveals bug
class MagicIterator:
def raw[T](self, expected: type[T]) -> Iterator[T]:
"""Get a list of repositories which accept a specific lint to run."""
if not issubclass(MySentinel, expected):
raise ValueError
# if TYPE_CHECKING:
# # an ugly workaround to make mypy happy
# if not isinstance(expected, MySentinel):
# raise ValueError
for project in [MySentinel(), MySentinel()]:
yield project # error: Incompatible types in "yield" (actual type "MySentinel", expected type "T") [misc]
```
Expected Behavior
I expected mypy to infer that after an if not issubclass(T, X): raise block X must be subclass T and it should be ok to return X in place of T.
Since I pass a type and want to compare it to a type an isinstance check is not appropriate for MagicIterator, neiter o I think the issubclass check is the most appropriate (I would prefer to check using expected is not T to guarantee that expected is actually exactly T but ìs`i not listed in the list of supported narrowing expressions.
Actual Behavior
Mypy is only happy if I add an isinstance check, which I deem wrong since I dont pass an instance but a type to the function. Thus, if I do not put the incorrect code in a if TYPE_CHECKCING block my program crashes
src/typeTest.py:31: error: Incompatible types in "yield" (actual type "MySentinel", expected type "T") [misc]
Found 1 error in 1 file (checked 1 source file)
Your Environment
- Mypy version used: mypy 1.18.2 (compiled: yes)
- Mypy command-line flags: none (
mypy src) - Mypy configuration options from
pyproject.toml(and other config files): - Python version used: 3.13.7 (uv)
** Possibly related **:
- https://github.com/python/mypy/issues/10680 (I think these go hand in hand)
- https://github.com/python/mypy/issues/19529
- https://github.com/python/mypy/issues/9003 (might be related, not sure)
- https://github.com/python/mypy/issues/17728 (similar but seems different)
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Comienza con el ejemplo enlazado de MyPy Playground y compara la ruta de narrowing de issubclass que falla con el ejemplo de isinstance que funciona. Lee los issues relacionados #10680, #19529, #9003 y #17728 antes de localizar las pruebas de narrowing relevantes y la implementación; se considera terminado cuando el ejemplo pasa la comprobación de tipos sin el workaround de ejecución incorrecto.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- python
- Área
- devtools
- Tipo de issue
- Error
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Estado de actividad
- Estancado
- Claridad
- Bastante claro
- Aptitud para principiantes
- 35/100