Skip to content

Commit

Permalink
fix: Parse complex, stringified annotations
Browse files Browse the repository at this point in the history
Issue #159: #159
  • Loading branch information
pawamoy committed May 17, 2023
1 parent 8d6fc70 commit f743616
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 4 deletions.
10 changes: 6 additions & 4 deletions src/griffe/agents/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import enum
import inspect
import sys
from ast import AST
from ast import AST, PyCF_ONLY_AST
from ast import Add as NodeAdd
from ast import And as NodeAnd
from ast import AnnAssign as NodeAnnAssign
Expand Down Expand Up @@ -636,10 +636,12 @@ def _get_call_annotation(node: NodeCall, parent: Module | Class) -> Expression:
return Expression(_get_annotation(node.func, parent), "(", args, ")")


def _get_constant_annotation(node: NodeConstant, parent: Module | Class) -> str | Name:
def _get_constant_annotation(node: NodeConstant, parent: Module | Class) -> str | Name | Expression:
if isinstance(node.value, str):
node.id = node.value # type: ignore[attr-defined] # fake node as Name
return _get_name_annotation(node, parent) # type: ignore[arg-type]
# a string in an annotation is a stringified annotation: we parse it again
# literal strings must be wrapped in Literal[...] to be picked up as such
parsed = compile(node.value, mode="eval", filename="<string-annotation>", flags=PyCF_ONLY_AST, optimize=1)
return _get_annotation(parsed.body, parent=parent) # type: ignore[attr-defined]
return _get_literal_annotation(node, parent)


Expand Down
25 changes: 25 additions & 0 deletions tests/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,28 @@ def test_building_value_from_nodes(expression: str) -> None:
expression = expression.replace(", ", ",")

assert value == expression


# https://github.com/mkdocstrings/griffe/issues/159
def test_parsing_complex_string_annotations() -> None:
"""Test parsing of complex, stringified annotations."""
with temporary_visited_module(
"""
class ArgsKwargs:
def __init__(self, args: 'tuple[Any, ...]', kwargs: 'dict[str, Any] | None' = None) -> None:
...
@property
def args(self) -> 'tuple[Any, ...]':
...
@property
def kwargs(self) -> 'dict[str, Any] | None':
...
""",
) as module:
init_args_annotation = module["ArgsKwargs.__init__"].parameters["args"].annotation
assert isinstance(init_args_annotation, Expression)
assert init_args_annotation.is_tuple
kwargs_return_annotation = module["ArgsKwargs.kwargs"].annotation
assert isinstance(kwargs_return_annotation, Expression)

0 comments on commit f743616

Please sign in to comment.