Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unpack list type when looking up namespaces #3388

Merged
merged 8 commits into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Release type: patch

Fixes an issue where lazy annotations raised an error when used together with a List
6 changes: 6 additions & 0 deletions strawberry/utils/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ def _get_namespace_from_ast(

for elt in cast(ast.Tuple, expr_slice).elts:
extra.update(_get_namespace_from_ast(elt, globalns, localns))
elif (
isinstance(expr, ast.Subscript)
and isinstance(expr.value, ast.Name)
and expr.value.id in {"list", "List"}
):
extra.update(_get_namespace_from_ast(expr.slice, globalns, localns))
elif (
isinstance(expr, ast.Subscript)
and isinstance(expr.value, ast.Name)
Expand Down
10 changes: 9 additions & 1 deletion tests/b.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, List, Optional
from typing_extensions import Annotated

import strawberry
Expand All @@ -19,6 +19,14 @@ async def a(self) -> Annotated[A, strawberry.lazy("tests.a"), object()]:

return A(id=self.id)

@strawberry.field
async def a_list(
self,
) -> List[Annotated[A, strawberry.lazy("tests.a")]]: # pragma: no cover
from tests.a import A

return [A(id=self.id)]

@strawberry.field
async def optional_a(
self,
Expand Down
8 changes: 8 additions & 0 deletions tests/c.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import annotations

import strawberry


@strawberry.type
class C:
id: strawberry.ID
22 changes: 22 additions & 0 deletions tests/d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from __future__ import annotations

from typing import TYPE_CHECKING, List
from typing_extensions import Annotated

import strawberry

if TYPE_CHECKING:
from tests.c import C


@strawberry.type
class D:
id: strawberry.ID

@strawberry.field
async def c_list(
self,
) -> List[Annotated[C, strawberry.lazy("tests.c")]]: # pragma: no cover
from tests.c import C

return [C(id=self.id)]
6 changes: 6 additions & 0 deletions tests/schema/test_lazy/test_lazy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ class Query:

type TypeB {
typeA: TypeA!
typeAList: [TypeA!]!
typeCList: [TypeC!]!
}

type TypeC {
name: String!
}
"""

Expand Down
6 changes: 6 additions & 0 deletions tests/schema/test_lazy/test_lazy_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,17 @@ class Query:

type TypeB {
typeA: TypeA!
typeAList: [TypeA!]!
typeCList: [TypeC!]!
}

type TypeBEdge {
node: TypeB!
}

type TypeC {
name: String!
}
"""
).strip()

Expand Down
28 changes: 27 additions & 1 deletion tests/schema/test_lazy/type_b.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, List
from typing_extensions import Annotated

import strawberry

if TYPE_CHECKING:
from .type_a import TypeA
from .type_c import TypeC

ListTypeA = List[TypeA]
ListTypeC = List[TypeC]
else:
TypeA = Annotated["TypeA", strawberry.lazy("tests.schema.test_lazy.type_a")]
ListTypeA = List[
Annotated["TypeA", strawberry.lazy("tests.schema.test_lazy.type_a")]
]
ListTypeC = List[
Annotated["TypeC", strawberry.lazy("tests.schema.test_lazy.type_c")]
]


@strawberry.type
Expand All @@ -18,3 +28,19 @@ def type_a(
from .type_a import TypeA

return TypeA()

@strawberry.field()
def type_a_list(
self,
) -> ListTypeA: # pragma: no cover
from .type_a import TypeA

return [TypeA()]

@strawberry.field()
def type_c_list(
self,
) -> ListTypeC: # pragma: no cover
from .type_c import TypeC

return [TypeC()]
32 changes: 32 additions & 0 deletions tests/test_forward_references.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from strawberry.scalars import JSON
from strawberry.type import StrawberryList, StrawberryOptional
from tests.a import A
from tests.d import D


def test_forward_reference():
Expand Down Expand Up @@ -74,6 +75,7 @@ async def a(self) -> A:
type B {
id: ID!
a: A!
aList: [A!]!
optionalA: A
optionalA2: A
}
Expand All @@ -87,6 +89,36 @@ async def a(self) -> A:
assert print_schema(schema) == textwrap.dedent(expected_representation).strip()


@pytest.mark.skipif(
sys.version_info < (3, 9),
reason="Python 3.8 and previous can't properly resolve this.",
)
def test_lazy_forward_reference_schema_with_a_list_only():
@strawberry.type
class Query:
@strawberry.field
async def d(self) -> D: # pragma: no cover
return D(id=strawberry.ID("1"))

expected_representation = """
type C {
id: ID!
}

type D {
id: ID!
cList: [C!]!
}

type Query {
d: D!
}
"""

schema = strawberry.Schema(query=Query)
assert print_schema(schema) == textwrap.dedent(expected_representation).strip()


def test_with_resolver():
global User

Expand Down