From 0519809b72a9bdc9fb285caa36f462a2b1a372e9 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 15 Jul 2023 11:04:44 +0300 Subject: [PATCH 1/4] Add required `...` rhs to `NamedTuple` fields with default values --- mypy/stubgen.py | 19 ++++++++++++++++++- test-data/unit/stubgen.test | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 9084da2053cf..a77ee738d56f 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -102,6 +102,7 @@ OverloadedFuncDef, Statement, StrExpr, + TempNode, TupleExpr, TypeInfo, UnaryExpr, @@ -637,6 +638,7 @@ def __init__( self._state = EMPTY self._toplevel_names: list[str] = [] self._include_private = include_private + self._current_class: ClassDef | None = None self.import_tracker = ImportTracker() # Was the tree semantically analysed before? self.analyzed = analyzed @@ -886,6 +888,7 @@ def get_fullname(self, expr: Expression) -> str: return resolved_name def visit_class_def(self, o: ClassDef) -> None: + self._current_class = o self.method_names = find_method_names(o.defs.body) sep: int | None = None if not self._indent and self._state != EMPTY: @@ -922,6 +925,7 @@ def visit_class_def(self, o: ClassDef) -> None: else: self._state = CLASS self.method_names = set() + self._current_class = None def get_base_types(self, cdef: ClassDef) -> list[str]: """Get list of base classes for a class.""" @@ -1330,7 +1334,20 @@ def get_init( typename += f"[{final_arg}]" else: typename = self.get_str_type_of_node(rvalue) - return f"{self._indent}{lvalue}: {typename}\n" + initializer = self.get_assign_initializer(rvalue) + return f"{self._indent}{lvalue}: {typename}{initializer}\n" + + def get_assign_initializer(self, rvalue: Expression) -> str: + """Does this rvalue need some special initializer value?""" + if self._current_class and self._current_class.info: + # Current rules + # 1. Return `...` if we are dealing with `NamedTuple` and it has an existing default value + if self._current_class.info.is_named_tuple and not isinstance(rvalue, TempNode): + return " = ..." + # TODO: support other possible cases, where initializer is important + + # By default, no initializer is required: + return "" def add(self, string: str) -> None: """Add text to generated stub.""" diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index b387aa840dc9..edf973b54703 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -698,6 +698,27 @@ class Y(NamedTuple): a: int b: str +[case testNamedTupleClassSyntax] +from typing import NamedTuple + +class A(NamedTuple): + x: int + y: str = 'a' + +class B(A): + z1: str + z2 = 1 +[out] +from typing import NamedTuple + +class A(NamedTuple): + x: int + y: str + +class B(A): + z1: str + z2: int + [case testEmptyNamedtuple] import collections, typing X = collections.namedtuple('X', []) From e7e5fb61748acf353fecf12276067c83791ed8d1 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 23 Jul 2023 09:59:07 +0300 Subject: [PATCH 2/4] Fix test case --- test-data/unit/stubgen.test | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index edf973b54703..cbeca05c2ebc 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -698,7 +698,7 @@ class Y(NamedTuple): a: int b: str -[case testNamedTupleClassSyntax] +[case testNamedTupleClassSyntax_semanal] from typing import NamedTuple class A(NamedTuple): @@ -708,16 +708,40 @@ class A(NamedTuple): class B(A): z1: str z2 = 1 + z3: str = 'b' + +class RegularClass: + x: int + y: str = 'a' + class NestedNamedTuple(NamedTuple): + x: int + y: str = 'a' + +# TODO: make sure that nested classes in `NamedTuple` are supported: +class NamedTupleWithNestedClass(NamedTuple): + class Nested: + x: int + y: str = 'a' [out] from typing import NamedTuple class A(NamedTuple): x: int - y: str + y: str = ... class B(A): z1: str z2: int + z3: str + +class RegularClass: + x: int + y: str + class NestedNamedTuple(NamedTuple): + x: int + y: str = ... + +class NamedTupleWithNestedClass(NamedTuple): ... [case testEmptyNamedtuple] import collections, typing From b0df689ef28f0f488ffafea71dffdb34677bc7c9 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 23 Jul 2023 10:18:14 +0300 Subject: [PATCH 3/4] Fix test case --- test-data/unit/stubgen.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index cbeca05c2ebc..14f620cb828e 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -716,6 +716,7 @@ class RegularClass: class NestedNamedTuple(NamedTuple): x: int y: str = 'a' + z: str = 'b' # TODO: make sure that nested classes in `NamedTuple` are supported: class NamedTupleWithNestedClass(NamedTuple): @@ -740,6 +741,7 @@ class RegularClass: class NestedNamedTuple(NamedTuple): x: int y: str = ... + z: str class NamedTupleWithNestedClass(NamedTuple): ... From 29684705de1964dca3f5eddf356299ee268599f4 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 28 Jul 2023 14:07:55 +0300 Subject: [PATCH 4/4] Add xfail --- test-data/unit/stubgen.test | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 14f620cb828e..f6b71a994153 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -717,12 +717,6 @@ class RegularClass: x: int y: str = 'a' z: str = 'b' - -# TODO: make sure that nested classes in `NamedTuple` are supported: -class NamedTupleWithNestedClass(NamedTuple): - class Nested: - x: int - y: str = 'a' [out] from typing import NamedTuple @@ -743,7 +737,22 @@ class RegularClass: y: str = ... z: str -class NamedTupleWithNestedClass(NamedTuple): ... + +[case testNestedClassInNamedTuple_semanal-xfail] +from typing import NamedTuple + +# TODO: make sure that nested classes in `NamedTuple` are supported: +class NamedTupleWithNestedClass(NamedTuple): + class Nested: + x: int + y: str = 'a' +[out] +from typing import NamedTuple + +class NamedTupleWithNestedClass(NamedTuple): + class Nested: + x: int + y: str [case testEmptyNamedtuple] import collections, typing