-
-
Notifications
You must be signed in to change notification settings - Fork 577
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Describe the Bug
Hello! First of all, thank you for this library!
I have a bug probably related to #3466 - consider creating a boolean algebra of AND
, OR
and NOT
and trying to encode it within GraphQL:
import dataclasses
import typing as t
import strawberry
_T = t.TypeVar("_T")
@strawberry.input(one_of=True)
class BoolOp(t.Generic[_T]):
and_: t.Optional[t.List["BoolOp[_T]"]] = strawberry.UNSET
or_: t.Optional[t.List["BoolOp[_T]"]] = strawberry.UNSET
not_: t.Optional["BoolOp[_T]"] = strawberry.UNSET
val: t.Optional[_T] = strawberry.UNSET
@strawberry.type
class Obj:
a: t.Optional[int] = strawberry.UNSET
b: t.Optional[str] = strawberry.UNSET
@strawberry.type
class Query:
@strawberry.field
def objs(self, where: BoolOp[Obj]) -> list:
return []
schema = strawberry.Schema(query=Query)
I would like to create a query akin to:
query Q {
objs(where: {or_: [{val: {a: 1, b: "abc"}, {and_: [{val: {a: 2, b: ""}}]}] {
...
}
}
however, RecursionError
is raised - here is a snippet of the traceback:
$ ipython dev/strawberry_nested_generics.py
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
File ~/Desktop/alembic/root/projects/aerosani/dev/strawberry_nested_generics.py:24
19 a: t.Optional[int] = strawberry.UNSET
20 b: t.Optional[str] = strawberry.UNSET
23 @strawberry.type
---> 24 class Query:
25 @strawberry.field
26 def objs(self, where: BoolOp[Obj]) -> list:
27 return []
File ~/Desktop/alembic/root/projects/aerosani/dev/strawberry_nested_generics.py:26, in Query()
23 @strawberry.type
24 class Query:
25 @strawberry.field
---> 26 def objs(self, where: BoolOp[Obj]) -> list:
27 return []
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:596, in field(resolver, name, is_subscription, description, permission_classes, deprecation_reason, default, default_factory, metadata, directives, extensions, graphql_type, init)
594 if resolver:
595 assert init is not True, "Can't set init as True when passing a resolver."
--> 596 return field_(resolver)
597 return field_
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:199, in StrawberryField.__call__(self, resolver)
197 if isinstance(argument.type_annotation.annotation, str):
198 continue
--> 199 elif isinstance(argument.type, StrawberryUnion):
200 raise InvalidArgumentTypeError(
201 resolver,
202 argument,
203 )
204 elif has_object_definition(argument.type):
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/arguments.py:131, in StrawberryArgument.type(self)
129 @property
130 def type(self) -> Union[StrawberryType, type]:
--> 131 return self.type_annotation.resolve()
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:133, in StrawberryAnnotation.resolve(self)
131 """Return resolved (transformed) annotation."""
132 if self.__resolve_cache__ is None:
--> 133 self.__resolve_cache__ = self._resolve()
135 return self.__resolve_cache__
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:152, in StrawberryAnnotation._resolve(self)
149 if self._is_list(evaled_type):
150 return self.create_list(evaled_type)
--> 152 if self._is_graphql_generic(evaled_type):
153 if any(is_type_var(type_) for type_ in get_args(evaled_type)):
154 return evaled_type
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:281, in StrawberryAnnotation._is_graphql_generic(cls, annotation)
279 if hasattr(annotation, "__origin__"):
280 if definition := get_object_definition(annotation.__origin__):
--> 281 return definition.is_graphql_generic
283 return is_generic(annotation.__origin__)
285 return False
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/base.py:347, in StrawberryObjectDefinition.is_graphql_generic(self)
342 return False
344 # here we are checking if any exposed field is generic
345 # a Strawberry class can be "generic", but not expose any
346 # generic field to GraphQL
--> 347 return any(field.is_graphql_generic for field in self.fields)
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/base.py:347, in <genexpr>(.0)
342 return False
344 # here we are checking if any exposed field is generic
345 # a Strawberry class can be "generic", but not expose any
346 # generic field to GraphQL
--> 347 return any(field.is_graphql_generic for field in self.fields)
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:256, in StrawberryField.is_graphql_generic(self)
251 @property
252 def is_graphql_generic(self) -> bool:
253 return (
254 self.base_resolver.is_graphql_generic
255 if self.base_resolver
--> 256 else _is_generic(self.type)
257 )
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:305, in StrawberryField.type(self)
297 @property # type: ignore
298 def type(
299 self,
(...)
303 Literal[UNRESOLVED],
304 ]:
--> 305 return self.resolve_type()
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:352, in StrawberryField.resolve_type(self, type_definition)
349 with contextlib.suppress(NameError):
350 # Prioritise the field type over the resolver return type
351 if self.type_annotation is not None:
--> 352 resolved = self.type_annotation.resolve()
353 elif self.base_resolver is not None and self.base_resolver.type is not None:
354 # Handle unannotated functions (such as lambdas)
355 # Generics will raise MissingTypesForGenericError later
356 # on if we let it be returned. So use `type_annotation` instead
357 # which is the same behaviour as having no type information.
358 resolved = self.base_resolver.type
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:133, in StrawberryAnnotation.resolve(self)
131 """Return resolved (transformed) annotation."""
132 if self.__resolve_cache__ is None:
--> 133 self.__resolve_cache__ = self._resolve()
135 return self.__resolve_cache__
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:162, in StrawberryAnnotation._resolve(self)
160 return self.create_enum(evaled_type)
161 elif self._is_optional(evaled_type, args):
--> 162 return self.create_optional(evaled_type)
163 elif self._is_union(evaled_type, args):
164 return self.create_union(evaled_type, args)
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:220, in StrawberryAnnotation.create_optional(self, evaled_type)
210 # Note that passing a single type to `Union` is equivalent to not using `Union`
211 # at all. This allows us to not di any checks for how many types have been
212 # passed as we can safely use `Union` for both optional types
213 # (e.g. `Optional[str]`) and optional unions (e.g.
214 # `Optional[Union[TypeA, TypeB]]`)
215 child_type = Union[non_optional_types] # type: ignore
217 of_type = StrawberryAnnotation(
218 annotation=child_type,
219 namespace=self.namespace,
--> 220 ).resolve()
222 return StrawberryOptional(of_type)
[... skipping similar frames: StrawberryAnnotation.resolve at line 133 (1 times)]
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:150, in StrawberryAnnotation._resolve(self)
148 return evaled_type
149 if self._is_list(evaled_type):
--> 150 return self.create_list(evaled_type)
152 if self._is_graphql_generic(evaled_type):
153 if any(is_type_var(type_) for type_ in get_args(evaled_type)):
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:197, in StrawberryAnnotation.create_list(self, evaled_type)
192 def create_list(self, evaled_type: Any) -> StrawberryList:
193 item_type, *_ = get_args(evaled_type)
194 of_type = StrawberryAnnotation(
195 annotation=item_type,
196 namespace=self.namespace,
--> 197 ).resolve()
199 return StrawberryList(of_type)
[... skipping similar frames: StrawberryAnnotation.resolve at line 133 (1 times)]
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:152, in StrawberryAnnotation._resolve(self)
149 if self._is_list(evaled_type):
150 return self.create_list(evaled_type)
--> 152 if self._is_graphql_generic(evaled_type):
153 if any(is_type_var(type_) for type_ in get_args(evaled_type)):
154 return evaled_type
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:281, in StrawberryAnnotation._is_graphql_generic(cls, annotation)
279 if hasattr(annotation, "__origin__"):
280 if definition := get_object_definition(annotation.__origin__):
--> 281 return definition.is_graphql_generic
283 return is_generic(annotation.__origin__)
285 return False
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/base.py:347, in StrawberryObjectDefinition.is_graphql_generic(self)
342 return False
344 # here we are checking if any exposed field is generic
345 # a Strawberry class can be "generic", but not expose any
346 # generic field to GraphQL
--> 347 return any(field.is_graphql_generic for field in self.fields)
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/base.py:347, in <genexpr>(.0)
342 return False
344 # here we are checking if any exposed field is generic
345 # a Strawberry class can be "generic", but not expose any
346 # generic field to GraphQL
--> 347 return any(field.is_graphql_generic for field in self.fields)
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:256, in StrawberryField.is_graphql_generic(self)
251 @property
252 def is_graphql_generic(self) -> bool:
253 return (
254 self.base_resolver.is_graphql_generic
255 if self.base_resolver
--> 256 else _is_generic(self.type)
257 )
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:305, in StrawberryField.type(self)
297 @property # type: ignore
298 def type(
299 self,
(...)
303 Literal[UNRESOLVED],
304 ]:
--> 305 return self.resolve_type()
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/types/field.py:352, in StrawberryField.resolve_type(self, type_definition)
349 with contextlib.suppress(NameError):
350 # Prioritise the field type over the resolver return type
351 if self.type_annotation is not None:
--> 352 resolved = self.type_annotation.resolve()
353 elif self.base_resolver is not None and self.base_resolver.type is not None:
354 # Handle unannotated functions (such as lambdas)
355 # Generics will raise MissingTypesForGenericError later
356 # on if we let it be returned. So use `type_annotation` instead
357 # which is the same behaviour as having no type information.
358 resolved = self.base_resolver.type
[... skipping similar frames: StrawberryAnnotation.resolve at line 133 (1 times)]
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:162, in StrawberryAnnotation._resolve(self)
160 return self.create_enum(evaled_type)
161 elif self._is_optional(evaled_type, args):
--> 162 return self.create_optional(evaled_type)
163 elif self._is_union(evaled_type, args):
164 return self.create_union(evaled_type, args)
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:220, in StrawberryAnnotation.create_optional(self, evaled_type)
210 # Note that passing a single type to `Union` is equivalent to not using `Union`
211 # at all. This allows us to not di any checks for how many types have been
212 # passed as we can safely use `Union` for both optional types
213 # (e.g. `Optional[str]`) and optional unions (e.g.
214 # `Optional[Union[TypeA, TypeB]]`)
215 child_type = Union[non_optional_types] # type: ignore
217 of_type = StrawberryAnnotation(
218 annotation=child_type,
219 namespace=self.namespace,
--> 220 ).resolve()
222 return StrawberryOptional(of_type)
[... skipping similar frames: StrawberryAnnotation.resolve at line 133 (1 times)]
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:150, in StrawberryAnnotation._resolve(self)
148 return evaled_type
149 if self._is_list(evaled_type):
--> 150 return self.create_list(evaled_type)
152 if self._is_graphql_generic(evaled_type):
153 if any(is_type_var(type_) for type_ in get_args(evaled_type)):
File ~/.virtualenvs/alembic/lib/python3.10/site-packages/strawberry/annotation.py:197, in StrawberryAnnotation.create_list(self, evaled_type)
192 def create_list(self, evaled_type: Any) -> StrawberryList:
193 item_type, *_ = get_args(evaled_type)
194 of_type = StrawberryAnnotation(
195 annotation=item_type,
196 namespace=self.namespace,
--> 197 ).resolve()
199 return StrawberryList(of_type)
[... skipping similar frames: StrawberryAnnotation.resolve at line 133 (584 times), <genexpr> at line 347 (195 times), StrawberryAnnotation._is_graphql_generic at line 281 (195 times), StrawberryAnnotation._resolve at line 152 (195 times), StrawberryObjectDefinition.is_graphql_generic at line 347 (195 times), StrawberryField.is_graphql_generic at line 256 (195 times), StrawberryField.resolve_type at line 352 (195 times), StrawberryField.type at line 305 (195 times), StrawberryAnnotation._resolve at line 162 (194 times), StrawberryAnnotation._resolve at line 150 (194 times), StrawberryAnnotation.create_list at line 197 (194 times), StrawberryAnnotation.create_optional at line 220 (194 times)]
... SNIPPED ...
System Information
- Operating system: macOS
- Strawberry version: 0.237.3 and 0.242
If I wanted to contribute this fix (seems like a major feature though 😄 ), where should I start?
Thank you for your time!
Libor
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working