From 0d1408377f8d47c919314724fa251ed7d4fe7335 Mon Sep 17 00:00:00 2001 From: Vasilis Themelis Date: Sat, 26 Aug 2023 16:14:24 +0100 Subject: [PATCH 1/7] Add `pass` to removed blocks of code Solves #352 more generally. --- .gitignore | 1 + docs/versionhistory.rst | 8 ++++-- src/typeguard/_transformer.py | 24 ++++++++++++++---- tests/test_transformer.py | 47 +++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index d66eb5ae..6d027d31 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .idea .tox .coverage +**/.coverage.* .cache .eggs/ *.egg-info/ diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 63f2db70..96bc32e8 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -7,10 +7,14 @@ This library adheres to `Semantic Versioning 2.0 `_) + **4.1.2** (2023-08-18) - Fixed ``Any`` being removed from a subscript that still contains other elements - (`#373 `_ + (`#373 `_) **4.1.1** (2023-08-16) @@ -21,7 +25,7 @@ This library adheres to `Semantic Versioning 2.0 `_ + (`#371 `_) - Fixed regression where ``Literal`` inside a ``Union`` had quotes stripped from its contents, thus typically causing ``NameError`` to be raised when run (`#372 `_) diff --git a/src/typeguard/_transformer.py b/src/typeguard/_transformer.py index 24c723c3..6ad18145 100644 --- a/src/typeguard/_transformer.py +++ b/src/typeguard/_transformer.py @@ -55,6 +55,7 @@ copy_location, expr, fix_missing_locations, + iter_fields, keyword, walk, ) @@ -64,6 +65,7 @@ from copy import deepcopy from dataclasses import dataclass, field from typing import Any, ClassVar, cast, overload +from typing_extensions import override generator_names = ( "typing.Generator", @@ -365,6 +367,7 @@ def visit(self, node: AST) -> Any: return new_node + @override def generic_visit(self, node: AST) -> AST: if isinstance(node, expr) and self._memo.name_matches(node, *literal_names): return node @@ -498,6 +501,22 @@ def __init__( self.target_node: FunctionDef | AsyncFunctionDef | None = None self.target_lineno = target_lineno + @override + def generic_visit(self, node: AST) -> AST: + non_empty_list_fields = [] + for field_name, val in iter_fields(node): + if isinstance(val, list) and len(val) > 0: + non_empty_list_fields.append(field_name) + + node = super().generic_visit(node) + + # Add `pass` to list fields that were optimised away + for field_name in non_empty_list_fields: + if not hasattr(node, field_name) or not getattr(node, field_name): + setattr(node, field_name, [Pass()]) + + return node + @contextmanager def _use_memo( self, node: ClassDef | FunctionDef | AsyncFunctionDef @@ -1175,11 +1194,6 @@ def visit_If(self, node: If) -> Any: """ self.generic_visit(node) - # Fix empty node body (caused by removal of classes/functions not on the target - # path) - if not node.body: - node.body.append(Pass()) - if ( self._memo is self._module_memo and isinstance(node.test, Name) diff --git a/tests/test_transformer.py b/tests/test_transformer.py index 67890cad..5c63d13e 100644 --- a/tests/test_transformer.py +++ b/tests/test_transformer.py @@ -1523,6 +1523,53 @@ def foo(x: str) -> None: ) +def test_dont_leave_empty_ast_container_nodes_2() -> None: + # Regression test for #352 + node = parse( + dedent( + """ + try: + + class A: + ... + + def func(): + ... + + except: + + class A: + ... + + def func(): + ... + + + def foo(x: str) -> None: + pass + """ + ) + ) + TypeguardTransformer(["foo"]).visit(node) + assert ( + unparse(node) + == dedent( + """ + try: + pass + except: + pass + + def foo(x: str) -> None: + from typeguard import TypeCheckMemo + from typeguard._functions import check_argument_types + memo = TypeCheckMemo(globals(), locals()) + check_argument_types('foo', {'x': (x, str)}, memo) + """ + ).strip() + ) + + def test_dont_parse_annotated_2nd_arg() -> None: # Regression test for #352 node = parse( From 9f91ddc4001dfc17e8be3313f03df128eadeed31 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 26 Aug 2023 19:17:53 +0000 Subject: [PATCH 2/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/typeguard/_transformer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/typeguard/_transformer.py b/src/typeguard/_transformer.py index 6ad18145..c2ebf735 100644 --- a/src/typeguard/_transformer.py +++ b/src/typeguard/_transformer.py @@ -65,6 +65,7 @@ from copy import deepcopy from dataclasses import dataclass, field from typing import Any, ClassVar, cast, overload + from typing_extensions import override generator_names = ( From a87b755d1085b788b5104a5c82f8c50cdcc0089c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Sat, 26 Aug 2023 22:29:04 +0300 Subject: [PATCH 3/7] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6d027d31..52daeeda 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ .idea .tox .coverage -**/.coverage.* +.coverage* .cache .eggs/ *.egg-info/ From d1c20e5671b4165b8102cee5512f2f96c476d691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Sat, 26 Aug 2023 23:05:30 +0300 Subject: [PATCH 4/7] Update docs/versionhistory.rst --- docs/versionhistory.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 96bc32e8..a718dda9 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -6,7 +6,6 @@ This library adheres to `Semantic Versioning 2.0 `_) From 914282cf6c8655e8c07ff13e99cd590da33442c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Sat, 26 Aug 2023 23:10:38 +0300 Subject: [PATCH 5/7] Removed `@override` --- src/typeguard/_transformer.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/typeguard/_transformer.py b/src/typeguard/_transformer.py index c2ebf735..219545de 100644 --- a/src/typeguard/_transformer.py +++ b/src/typeguard/_transformer.py @@ -66,8 +66,6 @@ from dataclasses import dataclass, field from typing import Any, ClassVar, cast, overload -from typing_extensions import override - generator_names = ( "typing.Generator", "collections.abc.Generator", @@ -368,7 +366,6 @@ def visit(self, node: AST) -> Any: return new_node - @override def generic_visit(self, node: AST) -> AST: if isinstance(node, expr) and self._memo.name_matches(node, *literal_names): return node @@ -502,7 +499,6 @@ def __init__( self.target_node: FunctionDef | AsyncFunctionDef | None = None self.target_lineno = target_lineno - @override def generic_visit(self, node: AST) -> AST: non_empty_list_fields = [] for field_name, val in iter_fields(node): From dcaed150bac48c7474dfc8b0102b13e4bdba2ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Sat, 26 Aug 2023 23:11:16 +0300 Subject: [PATCH 6/7] Update .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 52daeeda..1aff8d08 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ .pydevproject .idea .tox -.coverage .coverage* .cache .eggs/ From 945ddf41fbbc0de12191569ae8d6a350a254b9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Sat, 26 Aug 2023 23:41:30 +0300 Subject: [PATCH 7/7] Update src/typeguard/_transformer.py --- src/typeguard/_transformer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typeguard/_transformer.py b/src/typeguard/_transformer.py index 219545de..9a3c6519 100644 --- a/src/typeguard/_transformer.py +++ b/src/typeguard/_transformer.py @@ -509,7 +509,7 @@ def generic_visit(self, node: AST) -> AST: # Add `pass` to list fields that were optimised away for field_name in non_empty_list_fields: - if not hasattr(node, field_name) or not getattr(node, field_name): + if not getattr(node, field_name, None): setattr(node, field_name, [Pass()]) return node