From 3b90486104a935fa3407ff5cf6dbe58da7a24119 Mon Sep 17 00:00:00 2001 From: Apoorv Darshan Date: Wed, 8 Jul 2026 19:24:09 +0530 Subject: [PATCH] stubgen: handle PEP 604 unions in inspection mode InspectionStubGenerator.get_type_fullname() did getattr(typ, '__qualname__', typ.__name__), which raised AttributeError for a types.UnionType (PEP 604 'X | Y'), crashing 'stubgen --inspect-mode' on any function whose signature uses the new union syntax. Render a UnionType by joining its members, mapping NoneType to None. Fixes #21689. --- mypy/stubgenc.py | 10 ++++++++-- test-data/unit/stubgen.test | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index d86818adf2a43..e7aa670c4f156 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -13,7 +13,7 @@ import keyword import os.path from collections.abc import Callable, Mapping -from types import FunctionType, ModuleType +from types import FunctionType, ModuleType, UnionType from typing import Any from mypy.fastparse import parse_type_comment @@ -768,10 +768,16 @@ def generate_property_stub( rw_properties.append(f"{self._indent}{name}: {inferred_type}") - def get_type_fullname(self, typ: type) -> str: + def get_type_fullname(self, typ: type | UnionType) -> str: """Given a type, return a string representation""" if typ is Any: return "Any" + if isinstance(typ, UnionType): + # PEP 604 unions (``X | Y``) have no ``__name__`` / ``__qualname__``. + return " | ".join( + "None" if arg is type(None) else self.get_type_fullname(arg) + for arg in typ.__args__ + ) typename = getattr(typ, "__qualname__", typ.__name__) module_name = self.get_obj_module(typ) if module_name is None: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 0c8b74ecf29a1..0f583b2c6d500 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -55,6 +55,11 @@ def f(x='foo'): ... [out] def f(x: str = ...): ... +[case testInspectUnionType_inspect] +def f(x: int | str) -> float | None: ... +[out] +def f(x: int | str) -> float | None: ... + [case testDefaultArgBytes] def f(x=b'foo',y=b"what's up",z=b'\xc3\xa0 la une'): ... [out]