Skip to content

Commit

Permalink
Fixed literal quotes being stripped from Union[Literal[...],]
Browse files Browse the repository at this point in the history
Fixes #372.
  • Loading branch information
agronholm committed Jul 29, 2023
1 parent 2a5ef72 commit 6a5b8e2
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 1 deletion.
6 changes: 6 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ Version history

This library adheres to `Semantic Versioning 2.0 <https://semver.org/#semantic-versioning-200>`_.

**UNRELEASED**

- Fixed regression where ``Literal`` inside a ``Union`` had quotes stripped from its
contents, thus typically causing ``NameError`` to be raised when run
(`#372 <https://github.com/agronholm/typeguard/issues/372>`_)

**4.0.1** (2023-07-27)

- Fixed handling of ``typing_extensions.Literal`` on Python 3.8 and 3.9 when
Expand Down
8 changes: 7 additions & 1 deletion src/typeguard/_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ def visit(self, node: AST) -> Any:

return new_node

def generic_visit(self, node: AST) -> AST:
if isinstance(node, expr) and self._memo.name_matches(node, *literal_names):
return node

return super().generic_visit(node)

def visit_BinOp(self, node: BinOp) -> Any:
self.generic_visit(node)

Expand Down Expand Up @@ -395,7 +401,7 @@ def visit_Subscript(self, node: Subscript) -> Any:

# The subscript of typing(_extensions).Literal can be any arbitrary string, so
# don't try to evaluate it as code
if not self._memo.name_matches(node.value, *literal_names) and node.slice:
if node.slice:
if isinstance(node.slice, Index):
# Python 3.7 and 3.8
slice_value = node.slice.value # type: ignore[attr-defined]
Expand Down
6 changes: 6 additions & 0 deletions tests/dummymodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,12 @@ def literal(x: Literal["foo"]) -> Literal["foo"]:
return y


@typechecked
def literal_in_union(x: Union[Literal["foo"],]) -> Literal["foo"]:
y: Literal["foo"] = x
return y


@typechecked
def typevar_forwardref(x: Type[T]) -> T:
return x()
Expand Down
5 changes: 5 additions & 0 deletions tests/test_instrumentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ def test_literal(dummymodule):
assert dummymodule.literal("foo") == "foo"


def test_literal_in_union(dummymodule):
"""Regression test for #372."""
assert dummymodule.literal_in_union("foo") == "foo"


def test_typevar_forwardref(dummymodule):
instance = dummymodule.typevar_forwardref(dummymodule.DummyClass)
assert isinstance(instance, dummymodule.DummyClass)

0 comments on commit 6a5b8e2

Please sign in to comment.