diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index b03fcc4..7b7ec7f 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -7,6 +7,8 @@ This library adheres to **UNRELEASED** - Avoid creating reference cycles when type checking unions +- Fixed ``Optional[...]`` being removed from the AST if it was located within a + subscript (`#442 `_) **4.1.5** (2023-09-11) diff --git a/src/typeguard/_transformer.py b/src/typeguard/_transformer.py index aeb15e3..13ac363 100644 --- a/src/typeguard/_transformer.py +++ b/src/typeguard/_transformer.py @@ -457,9 +457,11 @@ def visit_Subscript(self, node: Subscript) -> Any: # If the transformer erased the slice entirely, just return the node # value without the subscript (unless it's Optional, in which case erase # the node entirely - if self._memo.name_matches(node.value, "typing.Optional"): + if self._memo.name_matches( + node.value, "typing.Optional" + ) and not hasattr(node, "slice"): return None - elif sys.version_info >= (3, 9) and not hasattr(node, "slice"): + if sys.version_info >= (3, 9) and not hasattr(node, "slice"): return node.value elif sys.version_info < (3, 9) and not hasattr(node.slice, "value"): return node.value diff --git a/tests/test_transformer.py b/tests/test_transformer.py index 92ac42e..15cf9d4 100644 --- a/tests/test_transformer.py +++ b/tests/test_transformer.py @@ -1042,6 +1042,33 @@ def foo(x: Optional[Hashable]) -> Optional[Hashable]: ).strip() ) + def test_optional_nested(self) -> None: + node = parse( + dedent( + """ + from typing import Any, List, Optional + + def foo(x: List[Optional[int]]) -> None: + pass + """ + ) + ) + TypeguardTransformer().visit(node) + assert ( + unparse(node) + == dedent( + """ + from typeguard import TypeCheckMemo + from typeguard._functions import check_argument_types + from typing import Any, List, Optional + + def foo(x: List[Optional[int]]) -> None: + memo = TypeCheckMemo(globals(), locals()) + check_argument_types('foo', {'x': (x, List[Optional[int]])}, memo) + """ + ).strip() + ) + def test_subscript_within_union(self) -> None: # Regression test for #397 node = parse(