`stubgen --inspect-mode` crashes with `AttributeError on PEP 604 union syntax (X | Y)` *(mypy 2.1.0)*
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 76/100
Hướng nghiên cứu
Bắt đầu trong mypy/stubgenc.py, đặc biệt là get_type_fullname và đường dẫn inspect-mode được hiển thị trong traceback. Chạy stubgen --inspect-mode với ví dụ test_union_pep604.py được cung cấp và so sánh stub được tạo với đầu ra mong đợi. Hoàn thành khi lệnh kết thúc thành công và giữ nguyên các chú thích union PEP 604 mà không làm hồi quy hành vi hiện có được mô tả trong issue.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Environment
- mypy version: 2.1.0 (compiled: yes)
- Python: 3.11
- OS: Debian/Linux
- Install method: pip, user install (
~/.local/lib/python3.11/site-packages)
Steps to Reproduce
- Create a module using PEP 604 union syntax:
# test_union_pep604.py
def greet(name: str) -> str:
return f"Hello, {name}"
def process(value: int | str) -> float | None:
if isinstance(value, int):
return float(value)
return None
class Container:
def __init__(self, data: list[str] | dict[str, int]) -> None:
self.data = data
def get(self, key: str) -> int | None:
if isinstance(self.data, dict):
return self.data.get(key)
return None
- Run:
stubgen --inspect-mode -o /tmp/stubs_test test_union_pep604.py
Expected Behavior
stubgen generates a .pyi stub file preserving the X | Y annotations, the same way it succeeds on the equivalent Union[X, Y] syntax:
stubgen --inspect-mode -o /tmp/stubs_test test_union_old.py
# → Generated /tmp/stubs_test/test_union_old.pyi (exit 0)
Actual Behavior
stubgen crashes with an unhandled AttributeError and exits with code 1:
Traceback (most recent call last):
File "/home/<user>/.local/bin/stubgen", line 6, in <module>
sys.exit(main())
File "mypy/stubgen.py", line 2057, in main
File "mypy/stubgen.py", line 1860, in generate_stubs
File "mypy/stubgen.py", line 1818, in generate_stub_for_py_module
File "/home/<user>/.local/lib/python3.11/site-packages/mypy/stubgenc.py", line 452, in generate_module
self.generate_function_stub(name, obj, output=functions)
File "/home/<user>/.local/lib/python3.11/site-packages/mypy/stubgenc.py", line 631, in generate_function_stub
default_sig = self.get_default_function_sig(obj, ctx)
File "/home/<user>/.local/lib/python3.11/site-packages/mypy/stubgenc.py", line 344, in get_default_function_sig
add_args(args, get_pos_default)
File "/home/<user>/.local/lib/python3.11/site-packages/mypy/stubgenc.py", line 336, in add_args
arglist.append(ArgSig(arg, get_annotation(arg), default=False))
File "/home/<user>/.local/lib/python3.11/site-packages/mypy/stubgenc.py", line 311, in get_annotation
return self.get_type_fullname(argtype)
File "/home/<user>/.local/lib/python3.11/site-packages/mypy/stubgenc.py", line 775, in get_type_fullname
typename = getattr(typ, "__qualname__", typ.__name__)
AttributeError: 'types.UnionType' object has no attribute '__name__'
Root Cause
--inspect-mode inspects live runtime objects rather than parsing source. When Python evaluates a X | Y annotation, the resulting object is an instance of types.UnionType (introduced in Python 3.10, PEP 604). stubgenc.py's get_type_fullname assumes any annotation object falls back to having a __name__, which types.UnionType does not define — hence the crash.
Proposed Fix
File: mypy/stubgenc.py
1. Import UnionType (near line 25):
- from types import FunctionType, ModuleType
+ from types import FunctionType, ModuleType, UnionType
2. Special-case it in get_type_fullname (near line 775):
if typ is Any:
return "Any"
+ if isinstance(typ, UnionType):
+ return " | ".join(self.get_type_fullname(a) for a in typ.__args__)
typename = getattr(typ, "__qualname__", typ.__name__)
This recursively resolves each member of the union and rejoins them with |, matching the original annotation's syntax. types.UnionType is available in all Python versions mypy 2.1.0 targets (3.10+), so no version gating is needed.
Verification
With the patch applied, test_union_pep604.py generates a stub with unions preserved:
$ stubgen --inspect-mode -o /tmp/stubs_test test_union_pep604.py
Processed 1 modules
Generated /tmp/stubs_test/test_union_pep604.pyi
$ cat /tmp/stubs_test/test_union_pep604.pyi
def greet(name: str) -> str: ...
def process(value: int | str) -> float | None: ...
class Container:
def __init__(self, data: list | dict) -> None: ...
def get(self, key: str) -> int | None: ...
test_union_old.py (using typing.Union) no longer crashes either, but it does not preserve the union — it falls back to Incomplete for those annotations, which is a pre-existing, separate limitation of runtime inspection unrelated to this patch:
Existing Issue
test_union_old.py
"""Module using classic Union[X, Y] syntax — should work with --inspect-mode."""
from typing import Union, Optional
def greet(name: str) -> str:
return f"Hello, {name}"
def process(value: Union[int, str]) -> Optional[float]:
if isinstance(value, int):
return float(value)
return None
class Container:
def __init__(self, data: Union[list[str], dict[str, int]]) -> None:
self.data = data
def get(self, key: str) -> Optional[int]:
if isinstance(self.data, dict):
return self.data.get(key)
return None
produces
$ stubgen --inspect-mode -o /tmp/stubs_test test_union_old.py
Processed 1 modules
Generated /tmp/stubs_test/test_union_old.pyi
$ cat /tmp/stubs_test/test_union_old.pyi
from _typeshed import Incomplete
def greet(name: str) -> str: ...
def process(value) -> Incomplete: ...
class Container:
def __init__(self, data) -> None: ...
def get(self, key: str) -> Incomplete: ...
So: the patch fixes the crash for PEP 604 syntax and, as a bonus, correctly resolves those unions in the stub. It does not fix typing.Union resolution — that gap already existed before this patch and is out of scope here.
- Ngôn ngữ chính
- Python
- Star
- 20.6k
- Fork
- 3.3k
- Merge trung bình
- 1 ngày 18 giờ
- Pull request đã merge (30 ngày)
- 54
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của python/mypy
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
-
documentation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
-
bug topic-configuration topic-error-reporting
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
-
bug topic-attrs
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
qgis/QGIS-Documentation#11275 ·
-
bug priority:normal ready-for-dev
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
OpenHands/extensions#626 · 1 bình luận ·
-
Change observation tooltip text Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
CSCfi/sd-search-api#39 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
-
please add to porn list Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
StevenBlack/hosts#3255 ·