Bounded generic type parameter not narrowed past its upper bound, regardless of argument value
还没有人认领这个 Issue。
评估
调研方向
首先,使用所示的 mypy.ini 设置复现 issue 中的示例,并检查针对有界 _E TypeVar 的泛型推断行为。相关的实现示例是 poltergeist/decorator.py;当 reveal_type 的结果保留 TypeError | ValueError,而不是将 _E 扩大为 BaseException 时,即视为完成。
由索引模型根据 Issue 内容生成。
描述
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.
- 主要语言
- Python
- 星标
- 20.6k
- 派生
- 3.3k
- 平均合并
- 1 天 18 小时
- 30 天内合并 PR
- 54
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
python/mypy 的其他 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 75/100
-
bug
难度 2/5 1-3 小时 新手友好度 78/100
-
bug
难度 2/5 1-3 小时 新手友好度 76/100
-
documentation
难度 2/5 1-3 小时 新手友好度 72/100
-
bug topic-configuration topic-error-reporting
难度 2/5 1-3 小时 新手友好度 68/100
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 86/100
zostera/django-bootstrap4#894 ·
-
难度 2/5 1-3 小时 新手友好度 78/100
use-agent-os/agent-os#3276 ·
-
难度 2/5 1-3 小时 新手友好度 88/100
zephyrproject-rtos/zephyr#119726 ·
-
area/auth bug comp/agent P3 platform/discord type/security
难度 2/5 1-3 小时 新手友好度 88/100
NousResearch/hermes-agent#117848 ·
-
难度 2/5 1-3 小时 新手友好度 82/100
zilliztech/memsearch#759 ·