From 303f01cc35d97dae3073e8991c79857aa1a53182 Mon Sep 17 00:00:00 2001 From: Alexey Makridenko Date: Thu, 24 Apr 2025 11:05:17 +0200 Subject: [PATCH 1/6] fix TypeAlias stubgen --- mypy/stubgen.py | 11 ++++++----- test-data/unit/stubgen.test | 9 +++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 881686adc5ed..ce2172425327 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -920,13 +920,14 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: continue if ( isinstance(lvalue, NameExpr) - and not self.is_private_name(lvalue.name) - # it is never an alias with explicit annotation - and not o.unanalyzed_type and self.is_alias_expression(o.rvalue) + and not self.is_private_name(lvalue.name) ): - self.process_typealias(lvalue, o.rvalue) - continue + is_type_alias = o.unanalyzed_type and getattr(o.type, 'name', None) == 'TypeAlias' + if not o.unanalyzed_type or is_type_alias: + self.process_typealias(lvalue, o.rvalue) + continue + if isinstance(lvalue, (TupleExpr, ListExpr)): items = lvalue.items if isinstance(o.unanalyzed_type, TupleType): # type: ignore[misc] diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index bf17c34b99a7..e8698b70c331 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1544,6 +1544,15 @@ from typing import TypeVar T = TypeVar('T') alias = Union[T, List[T]] +[case testTypeAlias] +from typing import TypeAlias + +alias: TypeAlias = tuple[int, str] + +[out] +alias = tuple[int, str] + + [case testEllipsisAliasPreserved] alias = Tuple[int, ...] From 6e1bcea3f7a5a61b73a96bde55380c534b4dc17d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 09:12:11 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/stubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ce2172425327..f20cbb302b9e 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -923,7 +923,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: and self.is_alias_expression(o.rvalue) and not self.is_private_name(lvalue.name) ): - is_type_alias = o.unanalyzed_type and getattr(o.type, 'name', None) == 'TypeAlias' + is_type_alias = o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias" if not o.unanalyzed_type or is_type_alias: self.process_typealias(lvalue, o.rvalue) continue From f48c3bc595abde1d88365452d9e8f406e357a464 Mon Sep 17 00:00:00 2001 From: Alexey Makridenko Date: Thu, 1 May 2025 13:41:11 +0200 Subject: [PATCH 3/6] update TypeAlias annotations in stubgen and test cases --- mypy/stubgen.py | 5 +++-- test-data/unit/stubgen.test | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ce2172425327..e9b13629e633 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -923,7 +923,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: and self.is_alias_expression(o.rvalue) and not self.is_private_name(lvalue.name) ): - is_type_alias = o.unanalyzed_type and getattr(o.type, 'name', None) == 'TypeAlias' + is_type_alias = o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias" if not o.unanalyzed_type or is_type_alias: self.process_typealias(lvalue, o.rvalue) continue @@ -1142,7 +1142,7 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None: p = AliasPrinter(self) - self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") + self.add(f"{self._indent}{lvalue.name}: TypeAlias = {rvalue.accept(p)}\n") self.record_name(lvalue.name) self._vars[-1].append(lvalue.name) @@ -1195,6 +1195,7 @@ def visit_import_from(self, o: ImportFrom) -> None: self.import_tracker.reexport(name) as_name = name import_names.append((name, as_name)) + # here's required = False self.import_tracker.add_import_from("." * relative + module, import_names) self._vars[-1].extend(alias or name for name, alias in import_names) for name, alias in import_names: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index e8698b70c331..d218c69f48a7 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1550,7 +1550,7 @@ from typing import TypeAlias alias: TypeAlias = tuple[int, str] [out] -alias = tuple[int, str] +alias: TypeAlias = tuple[int, str] [case testEllipsisAliasPreserved] From d6a9ff4d32d9f1eb45aef1a73e23e9873546fdcd Mon Sep 17 00:00:00 2001 From: Alexey Makridenko Date: Thu, 1 May 2025 14:36:15 +0200 Subject: [PATCH 4/6] Refactor type alias processing in ASTStubGenerator to differentiate explicit TypeAlias assignments --- mypy/stubgen.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index e9b13629e633..ea7ed8da34f4 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -923,8 +923,12 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: and self.is_alias_expression(o.rvalue) and not self.is_private_name(lvalue.name) ): - is_type_alias = o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias" - if not o.unanalyzed_type or is_type_alias: + is_explicit_type_alias = o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias" + if is_explicit_type_alias: + self.process_typealias(lvalue, o.rvalue, is_explicit_type_alias=True) + continue + + if not o.unanalyzed_type: self.process_typealias(lvalue, o.rvalue) continue @@ -1140,9 +1144,12 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: else: return False - def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None: + def process_typealias(self, lvalue: NameExpr, rvalue: Expression, is_explicit_type_alias: bool = False) -> None: p = AliasPrinter(self) - self.add(f"{self._indent}{lvalue.name}: TypeAlias = {rvalue.accept(p)}\n") + if is_explicit_type_alias: + self.add(f"{self._indent}{lvalue.name}: TypeAlias = {rvalue.accept(p)}\n") + else: + self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") self.record_name(lvalue.name) self._vars[-1].append(lvalue.name) From aa191584f38fc005f2ff0854eba0bec188739ab7 Mon Sep 17 00:00:00 2001 From: Alexey Makridenko Date: Thu, 1 May 2025 14:50:26 +0200 Subject: [PATCH 5/6] Rename test case for explicit TypeAlias and add TypeAlias to require_name in import tracker --- mypy/stubgen.py | 9 +++++++-- test-data/unit/stubgen.test | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index ea7ed8da34f4..823f47a094b9 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -923,7 +923,9 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: and self.is_alias_expression(o.rvalue) and not self.is_private_name(lvalue.name) ): - is_explicit_type_alias = o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias" + is_explicit_type_alias = ( + o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias" + ) if is_explicit_type_alias: self.process_typealias(lvalue, o.rvalue, is_explicit_type_alias=True) continue @@ -1144,9 +1146,12 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: else: return False - def process_typealias(self, lvalue: NameExpr, rvalue: Expression, is_explicit_type_alias: bool = False) -> None: + def process_typealias( + self, lvalue: NameExpr, rvalue: Expression, is_explicit_type_alias: bool = False + ) -> None: p = AliasPrinter(self) if is_explicit_type_alias: + self.import_tracker.require_name("TypeAlias") self.add(f"{self._indent}{lvalue.name}: TypeAlias = {rvalue.accept(p)}\n") else: self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n") diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index d218c69f48a7..76491f458f69 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1544,12 +1544,14 @@ from typing import TypeVar T = TypeVar('T') alias = Union[T, List[T]] -[case testTypeAlias] +[case testExplicitTypeAlias] from typing import TypeAlias alias: TypeAlias = tuple[int, str] [out] +from typing import TypeAlias + alias: TypeAlias = tuple[int, str] From 1572699480a5b02eb0a0975b1ed8f937f8c93793 Mon Sep 17 00:00:00 2001 From: Alexey Makridenko Date: Thu, 1 May 2025 16:19:53 +0200 Subject: [PATCH 6/6] rename alias in testExplicitTypeAlias case to explicit_alias and add implicit_alias for clarity --- mypy/stubgen.py | 1 - test-data/unit/stubgen.test | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 823f47a094b9..ba0a3f9dade6 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1207,7 +1207,6 @@ def visit_import_from(self, o: ImportFrom) -> None: self.import_tracker.reexport(name) as_name = name import_names.append((name, as_name)) - # here's required = False self.import_tracker.add_import_from("." * relative + module, import_names) self._vars[-1].extend(alias or name for name, alias in import_names) for name, alias in import_names: diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 76491f458f69..86d33e3af51d 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1547,12 +1547,14 @@ alias = Union[T, List[T]] [case testExplicitTypeAlias] from typing import TypeAlias -alias: TypeAlias = tuple[int, str] +explicit_alias: TypeAlias = tuple[int, str] +implicit_alias = list[int] [out] from typing import TypeAlias -alias: TypeAlias = tuple[int, str] +explicit_alias: TypeAlias = tuple[int, str] +implicit_alias = list[int] [case testEllipsisAliasPreserved]