Bounded generic type parameter not narrowed past its upper bound, regardless of argument value
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 38/100
Piste de recherche
Commencez par reproduire l’exemple de l’issue avec les paramètres mypy.ini indiqués et examinez le comportement de l’inférence générique pour le _E TypeVar borné. L’exemple d’implémentation associé est poltergeist/decorator.py ; le travail est terminé lorsque le résultat de reveal_type conserve TypeError | ValueError au lieu d’élargir _E à BaseException.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Bug Report
If a generic type variable _E has an upper bound B, the upper bound B seems to be used as the inferred type of _E when inferring other type variables that depend on _E, even if there is a more specific type that _E could be narrowed to.
This becomes an issue when trying to infer error types returned by poltergeist (a simple library that provides a generic Result = Ok[_T] | Err[_E] utility type).
To Reproduce
from poltergeist import catch
@catch(TypeError, ValueError)
def validate_positive_number(int_or_str: int | str) -> int:
if isinstance(int_or_str, str):
raise TypeError("Input must be an integer")
if int_or_str <= 0:
raise ValueError("Input must be positive")
return int_or_str
def do_something_with_positive_number(int_or_str: int | str) -> None:
validated_number_result = validate_positive_number(int_or_str)
reveal_type(validated_number_result) # Revealed type is "Union[poltergeist.result.Ok[builtins.int], poltergeist.result.Err[builtins.Exception]]" Mypy
The implementation of catch can be found here:
Source code for the `catch` decorator
import functools
from collections.abc import Awaitable
from typing import Callable, ParamSpec, TypeVar, reveal_type
from poltergeist.result import Err, Ok, Result
_T = TypeVar("_T")
_E = TypeVar("_E", bound=BaseException)
_P = ParamSpec("_P")
def catch(
*errors: type[_E],
) -> Callable[[Callable[_P, _T]], Callable[_P, Result[_T, _E]]]:
def decorator(func: Callable[_P, _T]) -> Callable[_P, Result[_T, _E]]:
@functools.wraps(func)
def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> Result[_T, _E]:
try:
result = func(*args, **kwargs)
except errors as e:
return Err(e)
return Ok(result)
return wrapper
return decorator
def catch_async(
*errors: type[_E],
) -> Callable[[Callable[_P, Awaitable[_T]]], Callable[_P, Awaitable[Result[_T, _E]]]]:
def decorator(
func: Callable[_P, Awaitable[_T]]
) -> Callable[_P, Awaitable[Result[_T, _E]]]:
@functools.wraps(func)
async def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> Result[_T, _E]:
try:
result = await func(*args, **kwargs)
except errors as e:
return Err(e)
return Ok(result)
return wrapper
return decorator
Expected Behavior
mypy should be able to accurately use the value of errors to narrow the inferred type of _E within catch, and thus infer that validated_number_result is of type Result[int, TypeError | ValueError].
Actual Behavior
Mypy keeps _E inferred to its upper bound BaseException, thus inferring validated_number_result as Result[int, BaseException].
By contrast, pylance's type checker is able to narrow validated_number_result as expected:
Your Environment
- Mypy version used: 1.15.0
- Mypy configuration options from
mypy.ini(and other config files):
mypy.ini
[mypy]
python_version = 3.13
mypy_path = typings
ignore_missing_imports = True
check_untyped_defs = True
disallow_untyped_defs = True
disallow_untyped_calls = True
strict_equality = True
disallow_any_unimported = True
warn_return_any = True
no_implicit_optional = True
pretty = True
show_error_context = True
show_error_codes = True
show_error_code_links = True
no_namespace_packages = True
- Python version used: 3.13.0
Unsure if related to #19081 , but it's possible, given that both these issues are related to retaining information via generic type parameters.
- Langage dominant
- Python
- Étoiles
- 20.6k
- Forks
- 3.3k
- Merge moyen
- 1 j 18 h
- PR mergées (30 j)
- 54
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de python/mypy
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 75/100
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
-
documentation
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
-
bug topic-configuration topic-error-reporting
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
Toutes les issues de python/mypy
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
bancolombia/sentinel#23 ·
-
test md OuverteCI
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
-
integration:quickjs org:external priority:backlog topic:code-interpreter topic:middleware type:feature
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
langchain-ai/deepagents#6450 ·
-
bug client
Difficulté 2/5 1-3 heures Accessibilité débutants 88/100
-
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100