From e374bd21d05e7bfbf64f8c3c0c8e407581329a15 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 16 May 2025 16:59:36 +0200 Subject: [PATCH 1/2] stubgen: Don't generate `Incomplete | None = None` argument annotation Closes: #19096 --- mypy/stubgen.py | 10 +++------- test-data/unit/stubgen.test | 25 ++++++++++--------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 3173bfdf9f5c..ec16fbb2fc7b 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -565,7 +565,7 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: default = "..." if arg_.initializer: if not typename: - typename = self.get_str_type_of_node(arg_.initializer, True, False) + typename = self.get_str_type_of_node(arg_.initializer, False) potential_default, valid = self.get_str_default_of_node(arg_.initializer) if valid and len(potential_default) <= 200: default = potential_default @@ -1305,9 +1305,7 @@ def is_private_member(self, fullname: str) -> bool: parts = fullname.split(".") return any(self.is_private_name(part) for part in parts) - def get_str_type_of_node( - self, rvalue: Expression, can_infer_optional: bool = False, can_be_any: bool = True - ) -> str: + def get_str_type_of_node(self, rvalue: Expression, can_be_incomplete: bool = True) -> str: rvalue = self.maybe_unwrap_unary_expr(rvalue) if isinstance(rvalue, IntExpr): @@ -1327,9 +1325,7 @@ def get_str_type_of_node( return "complex" if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"): return "bool" - if can_infer_optional and isinstance(rvalue, NameExpr) and rvalue.name == "None": - return f"{self.add_name('_typeshed.Incomplete')} | None" - if can_be_any: + if can_be_incomplete: return self.add_name("_typeshed.Incomplete") else: return "" diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 86d33e3af51d..5ff458736436 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -30,12 +30,10 @@ def g(b=-1, c=0): ... def f(a, b: int = 2) -> None: ... def g(b: int = -1, c: int = 0) -> None: ... -[case testDefaultArgNone] +[case testFuncDefaultArgNone] def f(x=None): ... [out] -from _typeshed import Incomplete - -def f(x: Incomplete | None = None) -> None: ... +def f(x=None) -> None: ... [case testDefaultArgBool] def f(x=True, y=False): ... @@ -1379,7 +1377,7 @@ async def f(a): [out] async def f(a) -> None: ... -[case testInferOptionalOnlyFunc] +[case testMethodDefaultArgNone] class A: x = None def __init__(self, a=None): @@ -1391,8 +1389,8 @@ from _typeshed import Incomplete class A: x: Incomplete - def __init__(self, a: Incomplete | None = None) -> None: ... - def method(self, a: Incomplete | None = None) -> None: ... + def __init__(self, a=None) -> None: ... + def method(self, a=None) -> None: ... [case testAnnotationImportsFrom] import foo @@ -2618,32 +2616,29 @@ class A(metaclass=abc.ABCMeta): @abc.abstractmethod def x(self): ... -[case testClassWithNameIncompleteOrOptional] +[case testClassWithNameIncomplete] Y = object() -def g(x=None): pass +def g(): + yield 1 x = g() class Incomplete: pass -def Optional(): - return 0 - [out] from _typeshed import Incomplete as _Incomplete +from collections.abc import Generator Y: _Incomplete -def g(x: _Incomplete | None = None) -> None: ... +def g() -> Generator[_Incomplete]: ... x: _Incomplete class Incomplete: ... -def Optional(): ... - [case testExportedNameImported] # modules: main a b from a import C From 804f2cb736f09e18c90f8052b03cb7506dc37cbe Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 16 May 2025 18:20:24 +0200 Subject: [PATCH 2/2] Make can_be_incomplete kwonly --- mypy/stubgen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ec16fbb2fc7b..e51469b5ab7d 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -565,7 +565,7 @@ def _get_func_args(self, o: FuncDef, ctx: FunctionContext) -> list[ArgSig]: default = "..." if arg_.initializer: if not typename: - typename = self.get_str_type_of_node(arg_.initializer, False) + typename = self.get_str_type_of_node(arg_.initializer, can_be_incomplete=False) potential_default, valid = self.get_str_default_of_node(arg_.initializer) if valid and len(potential_default) <= 200: default = potential_default @@ -1305,7 +1305,7 @@ def is_private_member(self, fullname: str) -> bool: parts = fullname.split(".") return any(self.is_private_name(part) for part in parts) - def get_str_type_of_node(self, rvalue: Expression, can_be_incomplete: bool = True) -> str: + def get_str_type_of_node(self, rvalue: Expression, *, can_be_incomplete: bool = True) -> str: rvalue = self.maybe_unwrap_unary_expr(rvalue) if isinstance(rvalue, IntExpr):