Skip to content

Commit

Permalink
Fixed AttributeError when a subscript's slice is removed
Browse files Browse the repository at this point in the history
Fixes #397.
  • Loading branch information
agronholm committed Sep 9, 2023
1 parent 6e96b75 commit 4c04b12
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 5 deletions.
4 changes: 4 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ This library adheres to `Semantic Versioning 2.0 <https://semver.org/#semantic-v

- Fixed ``AttributeError`` where the transformer removed elements from a PEP 604 union
(`#384 <https://github.com/agronholm/typeguard/issues/384>`_)
- Fixed ``AttributeError: 'Subscript' object has no attribute 'slice'`` when
encountering an annotation with a subscript containing an ignored type (imported
within an ``if TYPE_CHECKING:`` block)
(`#397 <https://github.com/agronholm/typeguard/issues/397>`_)

**4.1.3** (2023-08-27)

Expand Down
5 changes: 2 additions & 3 deletions src/typeguard/_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,12 @@ def visit_Subscript(self, node: Subscript) -> Any:
# forward reference
items = cast(
typing.List[expr],
[self.generic_visit(slice_value.elts[0])]
+ slice_value.elts[1:],
[self.visit(slice_value.elts[0])] + slice_value.elts[1:],
)
else:
items = cast(
typing.List[expr],
[self.generic_visit(item) for item in slice_value.elts],
[self.visit(item) for item in slice_value.elts],
)

# If this is a Union and any of the items is Any, erase the entire
Expand Down
34 changes: 32 additions & 2 deletions tests/test_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,6 @@ def test_optional(self) -> None:
dedent(
"""
from typing import Any, Optional, TYPE_CHECKING
from collections.abc import Generator
if TYPE_CHECKING:
from typing import Hashable
Expand All @@ -1002,7 +1001,6 @@ def foo(x: Optional[Hashable]) -> Optional[Hashable]:
== dedent(
"""
from typing import Any, Optional, TYPE_CHECKING
from collections.abc import Generator
if TYPE_CHECKING:
from typing import Hashable
Expand All @@ -1012,6 +1010,38 @@ def foo(x: Optional[Hashable]) -> Optional[Hashable]:
).strip()
)

def test_union(self) -> None:
# Regression test for #397
node = parse(
dedent(
"""
from typing import Any, Iterable, Union, TYPE_CHECKING
if TYPE_CHECKING:
from typing import Hashable
def foo(x: Union[Iterable[Hashable], str]) -> None:
pass
"""
)
)
TypeguardTransformer().visit(node)
assert (
unparse(node)
== dedent(
"""
from typeguard import TypeCheckMemo
from typeguard._functions import check_argument_types
from typing import Any, Iterable, Union, TYPE_CHECKING
if TYPE_CHECKING:
from typing import Hashable
def foo(x: Union[Iterable[Hashable], str]) -> None:
memo = TypeCheckMemo(globals(), locals())
check_argument_types('foo', {'x': (x, Union[Iterable, str])}, memo)
"""
).strip()
)


class TestAssign:
def test_annotated_assign(self) -> None:
Expand Down

0 comments on commit 4c04b12

Please sign in to comment.