Skip to content

Commit

Permalink
fix: Fix Annotated + strawberry.lazy + deferred annotations (#3507)
Browse files Browse the repository at this point in the history
* fix: Fix Annotated + strawberry.lazy + deferred annotations

Fix #3491

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
bellini666 and pre-commit-ci[bot] committed May 22, 2024
1 parent fc322a8 commit 8b30e07
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 12 deletions.
21 changes: 21 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Release type: patch

This release fixes an issue when using `Annotated` + `strawberry.lazy` +
deferred annotations such as:

```python
from __future__ import annotations
import strawberry
from typing import Annotated


@strawberry.type
class Query:
a: Annotated["datetime", strawberry.lazy("datetime")]


schema = strawberry.Schema(Query)
```

Before this would only work if `datetime` was not inside quotes. Now it should
work as expected!
2 changes: 1 addition & 1 deletion strawberry/utils/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ def _get_namespace_from_ast(
# here to resolve lazy types by execing the annotated args, resolving the
# type directly and then adding it to extra namespace, so that _eval_type
# can properly resolve it later
type_name = args[0].strip()
type_name = args[0].strip(" '\"\n")
for arg in args[1:]:
evaled_arg = eval(arg, globalns, localns) # noqa: PGH001, S307
if isinstance(evaled_arg, StrawberryLazyReference):
Expand Down
7 changes: 4 additions & 3 deletions tests/schema/test_lazy/test_lazy_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class Query:


def test_no_generic_type_duplication_with_lazy():
from tests.schema.test_lazy.type_a import TypeB_abs, TypeB_rel
from tests.schema.test_lazy.type_b import TypeB

@strawberry.type
Expand All @@ -47,8 +46,10 @@ class Edge(Generic[T]):
@strawberry.type
class Query:
users: Edge[TypeB]
relatively_lazy_users: Edge[TypeB_rel]
absolutely_lazy_users: Edge[TypeB_abs]
relatively_lazy_users: Edge[Annotated["TypeB", strawberry.lazy(".type_b")]]
absolutely_lazy_users: Edge[
Annotated["TypeB", strawberry.lazy("tests.schema.test_lazy.type_b")]
]

schema = strawberry.Schema(query=Query)

Expand Down
12 changes: 4 additions & 8 deletions tests/schema/test_lazy/type_a.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,15 @@
if TYPE_CHECKING:
from .type_b import TypeB

TypeB_rel = TypeB
TypeB_abs = TypeB
else:
TypeB_rel = Annotated["TypeB", strawberry.lazy(".type_b")]
TypeB_abs = Annotated["TypeB", strawberry.lazy("tests.schema.test_lazy.type_b")]


@strawberry.type
class TypeA:
list_of_b: Optional[List[TypeB_abs]] = None
list_of_b: Optional[
List[Annotated["TypeB", strawberry.lazy("tests.schema.test_lazy.type_b")]]
] = None

@strawberry.field
def type_b(self) -> TypeB_rel:
def type_b(self) -> Annotated["TypeB", strawberry.lazy(".type_b")]:
from .type_b import TypeB

return TypeB()
76 changes: 76 additions & 0 deletions tests/utils/test_typing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import sys
import typing
from typing import ClassVar, ForwardRef, Optional, Union
from typing_extensions import Annotated

import pytest

import strawberry
from strawberry.lazy_type import LazyType
from strawberry.utils.typing import eval_type, get_optional_annotation, is_classvar
Expand Down Expand Up @@ -62,6 +65,79 @@ class Foo: ...
)
== Annotated[strawberry.auto, "foobar"]
)
assert (
eval_type(
ForwardRef("Annotated[datetime, strawberry.lazy('datetime')]"),
{"strawberry": strawberry, "Annotated": Annotated},
None,
)
== Annotated[
LazyType("datetime", "datetime"),
strawberry.lazy("datetime"),
]
)


@pytest.mark.skipif(
sys.version_info < (3, 9),
reason="python 3.8 resolves Annotated differently",
)
def test_eval_type_with_deferred_annotations():
assert (
eval_type(
ForwardRef(
"Annotated['Fruit', strawberry.lazy('tests.utils.test_typing')]"
),
{"strawberry": strawberry, "Annotated": Annotated},
None,
)
== Annotated[
LazyType("Fruit", "tests.utils.test_typing"),
strawberry.lazy("tests.utils.test_typing"),
]
)
assert (
eval_type(
ForwardRef("Annotated['datetime', strawberry.lazy('datetime')]"),
{"strawberry": strawberry, "Annotated": Annotated},
None,
)
== Annotated[
LazyType("datetime", "datetime"),
strawberry.lazy("datetime"),
]
)


@pytest.mark.skipif(
sys.version_info >= (3, 9),
reason="python 3.8 resolves Annotated differently",
)
def test_eval_type_with_deferred_annotations_3_8():
assert (
eval_type(
ForwardRef(
"Annotated['Fruit', strawberry.lazy('tests.utils.test_typing')]"
),
{"strawberry": strawberry, "Annotated": Annotated},
None,
)
== Annotated[
ForwardRef("Fruit"),
strawberry.lazy("tests.utils.test_typing"),
]
)
assert (
eval_type(
ForwardRef("Annotated['datetime', strawberry.lazy('datetime')]"),
{"strawberry": strawberry, "Annotated": Annotated},
None,
)
== Annotated[
ForwardRef("datetime"),
strawberry.lazy("datetime"),
]
)


def test_is_classvar():
Expand Down

0 comments on commit 8b30e07

Please sign in to comment.