strawberry-graphql 0.222.0__py3-none-any.whl → 0.224.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
 
3
3
  A consumer to provide a graphql endpoint, and optionally graphiql.
4
4
  """
5
+
5
6
  from __future__ import annotations
6
7
 
7
8
  import dataclasses
@@ -1,6 +1,6 @@
1
1
  try:
2
2
  from . import pydantic
3
- except ImportError:
3
+ except ModuleNotFoundError:
4
4
  pass
5
5
  else:
6
6
  __all__ = ["pydantic"]
@@ -1,10 +1,16 @@
1
1
  import dataclasses
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Type
3
+ from decimal import Decimal
4
+ from functools import cached_property
5
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Type
6
+ from uuid import UUID
4
7
 
8
+ import pydantic
5
9
  from pydantic import BaseModel
6
10
  from pydantic.version import VERSION as PYDANTIC_VERSION
7
11
 
12
+ from strawberry.experimental.pydantic.exceptions import UnsupportedTypeError
13
+
8
14
  if TYPE_CHECKING:
9
15
  from pydantic.fields import FieldInfo
10
16
 
@@ -24,21 +30,101 @@ class CompatModelField:
24
30
  allow_none: bool
25
31
  has_alias: bool
26
32
  description: Optional[str]
33
+ _missing_type: Any
34
+ is_v1: bool
27
35
 
36
+ @property
37
+ def has_default_factory(self) -> bool:
38
+ return self.default_factory is not self._missing_type
28
39
 
29
- if IS_PYDANTIC_V2:
30
- from typing_extensions import get_args, get_origin
40
+ @property
41
+ def has_default(self) -> bool:
42
+ return self.default is not self._missing_type
31
43
 
32
- from pydantic._internal._typing_extra import is_new_type
33
- from pydantic._internal._utils import lenient_issubclass, smart_deepcopy
34
- from pydantic_core import PydanticUndefined
35
44
 
36
- PYDANTIC_MISSING_TYPE = PydanticUndefined
45
+ ATTR_TO_TYPE_MAP = {
46
+ "NoneStr": Optional[str],
47
+ "NoneBytes": Optional[bytes],
48
+ "StrBytes": None,
49
+ "NoneStrBytes": None,
50
+ "StrictStr": str,
51
+ "ConstrainedBytes": bytes,
52
+ "conbytes": bytes,
53
+ "ConstrainedStr": str,
54
+ "constr": str,
55
+ "EmailStr": str,
56
+ "PyObject": None,
57
+ "ConstrainedInt": int,
58
+ "conint": int,
59
+ "PositiveInt": int,
60
+ "NegativeInt": int,
61
+ "ConstrainedFloat": float,
62
+ "confloat": float,
63
+ "PositiveFloat": float,
64
+ "NegativeFloat": float,
65
+ "ConstrainedDecimal": Decimal,
66
+ "condecimal": Decimal,
67
+ "UUID1": UUID,
68
+ "UUID3": UUID,
69
+ "UUID4": UUID,
70
+ "UUID5": UUID,
71
+ "FilePath": None,
72
+ "DirectoryPath": None,
73
+ "Json": None,
74
+ "JsonWrapper": None,
75
+ "SecretStr": str,
76
+ "SecretBytes": bytes,
77
+ "StrictBool": bool,
78
+ "StrictInt": int,
79
+ "StrictFloat": float,
80
+ "PaymentCardNumber": None,
81
+ "ByteSize": None,
82
+ "AnyUrl": str,
83
+ "AnyHttpUrl": str,
84
+ "HttpUrl": str,
85
+ "PostgresDsn": str,
86
+ "RedisDsn": str,
87
+ }
88
+
89
+ ATTR_TO_TYPE_MAP_Pydantic_V2 = {
90
+ "EmailStr": str,
91
+ "SecretStr": str,
92
+ "SecretBytes": bytes,
93
+ "AnyUrl": str,
94
+ }
95
+
96
+ ATTR_TO_TYPE_MAP_Pydantic_Core_V2 = {
97
+ "MultiHostUrl": str,
98
+ }
37
99
 
38
- def new_type_supertype(type_: Any) -> Any:
39
- return type_.__supertype__
40
100
 
41
- def get_model_fields(model: Type[BaseModel]) -> Dict[str, CompatModelField]:
101
+ def get_fields_map_for_v2() -> Dict[Any, Any]:
102
+ import pydantic_core
103
+
104
+ fields_map = {
105
+ getattr(pydantic, field_name): type
106
+ for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_V2.items()
107
+ if hasattr(pydantic, field_name)
108
+ }
109
+ fields_map.update(
110
+ {
111
+ getattr(pydantic_core, field_name): type
112
+ for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_Core_V2.items()
113
+ if hasattr(pydantic_core, field_name)
114
+ }
115
+ )
116
+
117
+ return fields_map
118
+
119
+
120
+ class PydanticV2Compat:
121
+ @property
122
+ def PYDANTIC_MISSING_TYPE(self) -> Any:
123
+ from pydantic_core import PydanticUndefined
124
+
125
+ return PydanticUndefined
126
+
127
+ def get_model_fields(self, model: Type[BaseModel]) -> Dict[str, CompatModelField]:
42
128
  field_info: dict[str, FieldInfo] = model.model_fields
43
129
  new_fields = {}
44
130
  # Convert it into CompatModelField
@@ -55,24 +141,34 @@ if IS_PYDANTIC_V2:
55
141
  allow_none=False,
56
142
  has_alias=field is not None,
57
143
  description=field.description,
144
+ _missing_type=self.PYDANTIC_MISSING_TYPE,
145
+ is_v1=False,
58
146
  )
59
147
  return new_fields
60
148
 
61
- else:
62
- from pydantic.typing import ( # type: ignore[no-redef]
63
- get_args,
64
- get_origin,
65
- is_new_type,
66
- new_type_supertype,
67
- )
68
- from pydantic.utils import ( # type: ignore[no-redef]
69
- lenient_issubclass,
70
- smart_deepcopy,
71
- )
149
+ @cached_property
150
+ def fields_map(self) -> Dict[Any, Any]:
151
+ return get_fields_map_for_v2()
72
152
 
73
- PYDANTIC_MISSING_TYPE = dataclasses.MISSING # type: ignore[assignment]
153
+ def get_basic_type(self, type_: Any) -> Type[Any]:
154
+ if type_ in self.fields_map:
155
+ type_ = self.fields_map[type_]
74
156
 
75
- def get_model_fields(model: Type[BaseModel]) -> Dict[str, CompatModelField]:
157
+ if type_ is None:
158
+ raise UnsupportedTypeError()
159
+
160
+ if is_new_type(type_):
161
+ return new_type_supertype(type_)
162
+
163
+ return type_
164
+
165
+
166
+ class PydanticV1Compat:
167
+ @property
168
+ def PYDANTIC_MISSING_TYPE(self) -> Any:
169
+ return dataclasses.MISSING
170
+
171
+ def get_model_fields(self, model: Type[BaseModel]) -> Dict[str, CompatModelField]:
76
172
  new_fields = {}
77
173
  # Convert it into CompatModelField
78
174
  for name, field in model.__fields__.items(): # type: ignore[attr-defined]
@@ -87,17 +183,104 @@ else:
87
183
  allow_none=field.allow_none,
88
184
  has_alias=field.has_alias,
89
185
  description=field.field_info.description,
186
+ _missing_type=self.PYDANTIC_MISSING_TYPE,
187
+ is_v1=True,
90
188
  )
91
189
  return new_fields
92
190
 
191
+ @cached_property
192
+ def fields_map(self) -> Dict[Any, Any]:
193
+ if IS_PYDANTIC_V2:
194
+ return {
195
+ getattr(pydantic.v1, field_name): type
196
+ for field_name, type in ATTR_TO_TYPE_MAP.items()
197
+ if hasattr(pydantic.v1, field_name)
198
+ }
199
+
200
+ return {
201
+ getattr(pydantic, field_name): type
202
+ for field_name, type in ATTR_TO_TYPE_MAP.items()
203
+ if hasattr(pydantic, field_name)
204
+ }
205
+
206
+ def get_basic_type(self, type_: Any) -> Type[Any]:
207
+ if IS_PYDANTIC_V1:
208
+ ConstrainedInt = pydantic.ConstrainedInt
209
+ ConstrainedFloat = pydantic.ConstrainedFloat
210
+ ConstrainedStr = pydantic.ConstrainedStr
211
+ ConstrainedList = pydantic.ConstrainedList
212
+ else:
213
+ ConstrainedInt = pydantic.v1.ConstrainedInt
214
+ ConstrainedFloat = pydantic.v1.ConstrainedFloat
215
+ ConstrainedStr = pydantic.v1.ConstrainedStr
216
+ ConstrainedList = pydantic.v1.ConstrainedList
217
+
218
+ if lenient_issubclass(type_, ConstrainedInt):
219
+ return int
220
+ if lenient_issubclass(type_, ConstrainedFloat):
221
+ return float
222
+ if lenient_issubclass(type_, ConstrainedStr):
223
+ return str
224
+ if lenient_issubclass(type_, ConstrainedList):
225
+ return List[self.get_basic_type(type_.item_type)] # type: ignore
226
+
227
+ if type_ in self.fields_map:
228
+ type_ = self.fields_map[type_]
229
+
230
+ if type_ is None:
231
+ raise UnsupportedTypeError()
232
+
233
+ if is_new_type(type_):
234
+ return new_type_supertype(type_)
235
+
236
+ return type_
237
+
238
+
239
+ class PydanticCompat:
240
+ def __init__(self, is_v2: bool):
241
+ if is_v2:
242
+ self._compat = PydanticV2Compat()
243
+ else:
244
+ self._compat = PydanticV1Compat() # type: ignore[assignment]
245
+
246
+ @classmethod
247
+ def from_model(cls, model: Type[BaseModel]) -> "PydanticCompat":
248
+ if hasattr(model, "model_fields"):
249
+ return cls(is_v2=True)
250
+
251
+ return cls(is_v2=False)
252
+
253
+ def __getattr__(self, name: str) -> Any:
254
+ return getattr(self._compat, name)
255
+
256
+
257
+ if IS_PYDANTIC_V2:
258
+ from typing_extensions import get_args, get_origin
259
+
260
+ from pydantic._internal._typing_extra import is_new_type
261
+ from pydantic._internal._utils import lenient_issubclass, smart_deepcopy
262
+
263
+ def new_type_supertype(type_: Any) -> Any:
264
+ return type_.__supertype__
265
+ else:
266
+ from pydantic.typing import ( # type: ignore[no-redef]
267
+ get_args,
268
+ get_origin,
269
+ is_new_type,
270
+ new_type_supertype,
271
+ )
272
+ from pydantic.utils import ( # type: ignore[no-redef]
273
+ lenient_issubclass,
274
+ smart_deepcopy,
275
+ )
276
+
93
277
 
94
278
  __all__ = [
95
- "smart_deepcopy",
279
+ "PydanticCompat",
280
+ "is_new_type",
96
281
  "lenient_issubclass",
97
- "get_args",
98
282
  "get_origin",
99
- "is_new_type",
283
+ "get_args",
100
284
  "new_type_supertype",
101
- "get_model_fields",
102
- "PYDANTIC_MISSING_TYPE",
285
+ "smart_deepcopy",
103
286
  ]
@@ -19,7 +19,7 @@ from pydantic import BaseModel
19
19
  from strawberry.auto import StrawberryAuto
20
20
  from strawberry.experimental.pydantic._compat import (
21
21
  CompatModelField,
22
- get_model_fields,
22
+ PydanticCompat,
23
23
  lenient_issubclass,
24
24
  )
25
25
  from strawberry.experimental.pydantic.utils import (
@@ -72,7 +72,8 @@ def error_type(
72
72
  all_fields: bool = False,
73
73
  ) -> Callable[..., Type]:
74
74
  def wrap(cls: Type) -> Type:
75
- model_fields = get_model_fields(model)
75
+ compat = PydanticCompat.from_model(model)
76
+ model_fields = compat.get_model_fields(model)
76
77
  fields_set = set(fields) if fields else set()
77
78
 
78
79
  if fields:
@@ -1,23 +1,17 @@
1
1
  import builtins
2
- from decimal import Decimal
3
- from typing import Any, Dict, List, Optional, Type, Union
2
+ from typing import Any, Union
4
3
  from typing_extensions import Annotated
5
- from uuid import UUID
6
4
 
7
- import pydantic
8
5
  from pydantic import BaseModel
9
6
 
10
7
  from strawberry.experimental.pydantic._compat import (
11
- IS_PYDANTIC_V1,
8
+ PydanticCompat,
12
9
  get_args,
13
10
  get_origin,
14
- is_new_type,
15
11
  lenient_issubclass,
16
- new_type_supertype,
17
12
  )
18
13
  from strawberry.experimental.pydantic.exceptions import (
19
14
  UnregisteredTypeException,
20
- UnsupportedTypeError,
21
15
  )
22
16
  from strawberry.types.types import StrawberryObjectDefinition
23
17
 
@@ -44,115 +38,6 @@ except ImportError:
44
38
  raise
45
39
 
46
40
 
47
- ATTR_TO_TYPE_MAP = {
48
- "NoneStr": Optional[str],
49
- "NoneBytes": Optional[bytes],
50
- "StrBytes": None,
51
- "NoneStrBytes": None,
52
- "StrictStr": str,
53
- "ConstrainedBytes": bytes,
54
- "conbytes": bytes,
55
- "ConstrainedStr": str,
56
- "constr": str,
57
- "EmailStr": str,
58
- "PyObject": None,
59
- "ConstrainedInt": int,
60
- "conint": int,
61
- "PositiveInt": int,
62
- "NegativeInt": int,
63
- "ConstrainedFloat": float,
64
- "confloat": float,
65
- "PositiveFloat": float,
66
- "NegativeFloat": float,
67
- "ConstrainedDecimal": Decimal,
68
- "condecimal": Decimal,
69
- "UUID1": UUID,
70
- "UUID3": UUID,
71
- "UUID4": UUID,
72
- "UUID5": UUID,
73
- "FilePath": None,
74
- "DirectoryPath": None,
75
- "Json": None,
76
- "JsonWrapper": None,
77
- "SecretStr": str,
78
- "SecretBytes": bytes,
79
- "StrictBool": bool,
80
- "StrictInt": int,
81
- "StrictFloat": float,
82
- "PaymentCardNumber": None,
83
- "ByteSize": None,
84
- "AnyUrl": str,
85
- "AnyHttpUrl": str,
86
- "HttpUrl": str,
87
- "PostgresDsn": str,
88
- "RedisDsn": str,
89
- }
90
-
91
- ATTR_TO_TYPE_MAP_Pydantic_V2 = {
92
- "EmailStr": str,
93
- "SecretStr": str,
94
- "SecretBytes": bytes,
95
- "AnyUrl": str,
96
- }
97
-
98
- ATTR_TO_TYPE_MAP_Pydantic_Core_V2 = {
99
- "MultiHostUrl": str,
100
- }
101
-
102
-
103
- def get_fields_map_for_v2() -> Dict[Any, Any]:
104
- import pydantic_core
105
-
106
- fields_map = {
107
- getattr(pydantic, field_name): type
108
- for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_V2.items()
109
- if hasattr(pydantic, field_name)
110
- }
111
- fields_map.update(
112
- {
113
- getattr(pydantic_core, field_name): type
114
- for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_Core_V2.items()
115
- if hasattr(pydantic_core, field_name)
116
- }
117
- )
118
-
119
- return fields_map
120
-
121
-
122
- FIELDS_MAP = (
123
- {
124
- getattr(pydantic, field_name): type
125
- for field_name, type in ATTR_TO_TYPE_MAP.items()
126
- if hasattr(pydantic, field_name)
127
- }
128
- if IS_PYDANTIC_V1
129
- else get_fields_map_for_v2()
130
- )
131
-
132
-
133
- def get_basic_type(type_: Any) -> Type[Any]:
134
- if IS_PYDANTIC_V1:
135
- # only pydantic v1 has these
136
- if lenient_issubclass(type_, pydantic.ConstrainedInt):
137
- return int
138
- if lenient_issubclass(type_, pydantic.ConstrainedFloat):
139
- return float
140
- if lenient_issubclass(type_, pydantic.ConstrainedStr):
141
- return str
142
- if lenient_issubclass(type_, pydantic.ConstrainedList):
143
- return List[get_basic_type(type_.item_type)] # type: ignore
144
-
145
- if type_ in FIELDS_MAP:
146
- type_ = FIELDS_MAP.get(type_)
147
- if type_ is None:
148
- raise UnsupportedTypeError()
149
-
150
- if is_new_type(type_):
151
- return new_type_supertype(type_)
152
-
153
- return type_
154
-
155
-
156
41
  def replace_pydantic_types(type_: Any, is_input: bool) -> Any:
157
42
  if lenient_issubclass(type_, BaseModel):
158
43
  attr = "_strawberry_input_type" if is_input else "_strawberry_type"
@@ -163,17 +48,21 @@ def replace_pydantic_types(type_: Any, is_input: bool) -> Any:
163
48
  return type_
164
49
 
165
50
 
166
- def replace_types_recursively(type_: Any, is_input: bool) -> Any:
51
+ def replace_types_recursively(
52
+ type_: Any, is_input: bool, compat: PydanticCompat
53
+ ) -> Any:
167
54
  """Runs the conversions recursively into the arguments of generic types if any"""
168
- basic_type = get_basic_type(type_)
55
+ basic_type = compat.get_basic_type(type_)
169
56
  replaced_type = replace_pydantic_types(basic_type, is_input)
170
57
 
171
58
  origin = get_origin(type_)
59
+
172
60
  if not origin or not hasattr(type_, "__args__"):
173
61
  return replaced_type
174
62
 
175
63
  converted = tuple(
176
- replace_types_recursively(t, is_input=is_input) for t in get_args(replaced_type)
64
+ replace_types_recursively(t, is_input=is_input, compat=compat)
65
+ for t in get_args(replaced_type)
177
66
  )
178
67
 
179
68
  if isinstance(replaced_type, TypingGenericAlias):
@@ -19,9 +19,8 @@ from typing import (
19
19
  from strawberry.annotation import StrawberryAnnotation
20
20
  from strawberry.auto import StrawberryAuto
21
21
  from strawberry.experimental.pydantic._compat import (
22
- IS_PYDANTIC_V1,
23
22
  CompatModelField,
24
- get_model_fields,
23
+ PydanticCompat,
25
24
  )
26
25
  from strawberry.experimental.pydantic.conversion import (
27
26
  convert_pydantic_model_to_strawberry_class,
@@ -44,10 +43,12 @@ if TYPE_CHECKING:
44
43
  from graphql import GraphQLResolveInfo
45
44
 
46
45
 
47
- def get_type_for_field(field: CompatModelField, is_input: bool): # noqa: ANN201
46
+ def get_type_for_field(field: CompatModelField, is_input: bool, compat: PydanticCompat): # noqa: ANN201
48
47
  outer_type = field.outer_type_
49
- replaced_type = replace_types_recursively(outer_type, is_input)
50
- if IS_PYDANTIC_V1:
48
+
49
+ replaced_type = replace_types_recursively(outer_type, is_input, compat=compat)
50
+
51
+ if field.is_v1:
51
52
  # only pydantic v1 has this Optional logic
52
53
  should_add_optional: bool = field.allow_none
53
54
  if should_add_optional:
@@ -62,9 +63,10 @@ def _build_dataclass_creation_fields(
62
63
  existing_fields: Dict[str, StrawberryField],
63
64
  auto_fields_set: Set[str],
64
65
  use_pydantic_alias: bool,
66
+ compat: PydanticCompat,
65
67
  ) -> DataclassCreationFields:
66
68
  field_type = (
67
- get_type_for_field(field, is_input)
69
+ get_type_for_field(field, is_input, compat=compat)
68
70
  if field.name in auto_fields_set
69
71
  else existing_fields[field.name].type
70
72
  )
@@ -129,7 +131,8 @@ def type(
129
131
  use_pydantic_alias: bool = True,
130
132
  ) -> Callable[..., Type[StrawberryTypeFromPydantic[PydanticModel]]]:
131
133
  def wrap(cls: Any) -> Type[StrawberryTypeFromPydantic[PydanticModel]]:
132
- model_fields = get_model_fields(model)
134
+ compat = PydanticCompat.from_model(model)
135
+ model_fields = compat.get_model_fields(model)
133
136
  original_fields_set = set(fields) if fields else set()
134
137
 
135
138
  if fields:
@@ -182,7 +185,12 @@ def type(
182
185
 
183
186
  all_model_fields: List[DataclassCreationFields] = [
184
187
  _build_dataclass_creation_fields(
185
- field, is_input, extra_fields_dict, auto_fields_set, use_pydantic_alias
188
+ field,
189
+ is_input,
190
+ extra_fields_dict,
191
+ auto_fields_set,
192
+ use_pydantic_alias,
193
+ compat=compat,
186
194
  )
187
195
  for field_name, field in model_fields.items()
188
196
  if field_name in fields_set
@@ -14,9 +14,8 @@ from typing import (
14
14
  )
15
15
 
16
16
  from strawberry.experimental.pydantic._compat import (
17
- PYDANTIC_MISSING_TYPE,
18
17
  CompatModelField,
19
- get_model_fields,
18
+ PydanticCompat,
20
19
  smart_deepcopy,
21
20
  )
22
21
  from strawberry.experimental.pydantic.exceptions import (
@@ -83,12 +82,8 @@ def get_default_factory_for_field(
83
82
  Returns optionally a NoArgAnyCallable representing a default_factory parameter
84
83
  """
85
84
  # replace dataclasses.MISSING with our own UNSET to make comparisons easier
86
- default_factory = (
87
- field.default_factory
88
- if field.default_factory is not PYDANTIC_MISSING_TYPE
89
- else UNSET
90
- )
91
- default = field.default if field.default is not PYDANTIC_MISSING_TYPE else UNSET
85
+ default_factory = field.default_factory if field.has_default_factory else UNSET
86
+ default = field.default if field.has_default else UNSET
92
87
 
93
88
  has_factory = default_factory is not None and default_factory is not UNSET
94
89
  has_default = default is not None and default is not UNSET
@@ -126,8 +121,9 @@ def get_default_factory_for_field(
126
121
  def ensure_all_auto_fields_in_pydantic(
127
122
  model: Type[BaseModel], auto_fields: Set[str], cls_name: str
128
123
  ) -> None:
124
+ compat = PydanticCompat.from_model(model)
129
125
  # Raise error if user defined a strawberry.auto field not present in the model
130
- non_existing_fields = list(auto_fields - get_model_fields(model).keys())
126
+ non_existing_fields = list(auto_fields - compat.get_model_fields(model).keys())
131
127
 
132
128
  if non_existing_fields:
133
129
  raise AutoFieldsNotInBaseModelError(
@@ -74,7 +74,9 @@ def _serialize_dataclasses(value: Dict[_T, object]) -> Dict[_T, object]:
74
74
 
75
75
 
76
76
  @overload
77
- def _serialize_dataclasses(value: Union[List[object], Tuple[object]]) -> List[object]:
77
+ def _serialize_dataclasses(
78
+ value: Union[List[object], Tuple[object]],
79
+ ) -> List[object]:
78
80
  ...
79
81
 
80
82
 
@@ -2,6 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  import dataclasses
4
4
  import keyword
5
+ from collections import defaultdict
6
+ from typing import TYPE_CHECKING, List, Tuple, Union
7
+ from typing_extensions import Protocol, TypeAlias
5
8
 
6
9
  import libcst as cst
7
10
  from graphql import (
@@ -19,14 +22,28 @@ from graphql import (
19
22
  OperationType,
20
23
  ScalarTypeDefinitionNode,
21
24
  SchemaDefinitionNode,
25
+ SchemaExtensionNode,
22
26
  StringValueNode,
23
27
  TypeNode,
24
28
  UnionTypeDefinitionNode,
25
29
  parse,
26
30
  )
31
+ from graphql.language.ast import (
32
+ BooleanValueNode,
33
+ ConstValueNode,
34
+ ListValueNode,
35
+ )
27
36
 
28
37
  from strawberry.utils.str_converters import to_snake_case
29
38
 
39
+ if TYPE_CHECKING:
40
+ from graphql.language.ast import ConstDirectiveNode
41
+
42
+
43
+ class HasDirectives(Protocol):
44
+ directives: Tuple[ConstDirectiveNode, ...]
45
+
46
+
30
47
  _SCALAR_MAP = {
31
48
  "Int": cst.Name("int"),
32
49
  "Float": cst.Name("float"),
@@ -48,6 +65,48 @@ _SCALAR_MAP = {
48
65
  }
49
66
 
50
67
 
68
+ @dataclasses.dataclass(frozen=True)
69
+ class Import:
70
+ module: str | None
71
+ imports: tuple[str]
72
+
73
+ def module_path_to_cst(self, module_path: str) -> cst.Name | cst.Attribute:
74
+ parts = module_path.split(".")
75
+
76
+ module_name: cst.Name | cst.Attribute = cst.Name(parts[0])
77
+
78
+ for part in parts[1:]:
79
+ module_name = cst.Attribute(value=module_name, attr=cst.Name(part))
80
+
81
+ return module_name
82
+
83
+ def to_cst(self) -> cst.Import | cst.ImportFrom:
84
+ if self.module is None:
85
+ return cst.Import(
86
+ names=[cst.ImportAlias(name=cst.Name(name)) for name in self.imports]
87
+ )
88
+
89
+ return cst.ImportFrom(
90
+ module=self.module_path_to_cst(self.module),
91
+ names=[cst.ImportAlias(name=cst.Name(name)) for name in self.imports],
92
+ )
93
+
94
+
95
+ def _is_federation_link_directive(directive: ConstDirectiveNode) -> bool:
96
+ if directive.name.value != "link":
97
+ return False
98
+
99
+ return next(
100
+ (
101
+ argument.value.value
102
+ for argument in directive.arguments
103
+ if argument.name.value == "url"
104
+ if isinstance(argument.value, StringValueNode)
105
+ ),
106
+ "",
107
+ ).startswith("https://specs.apollo.dev/federation")
108
+
109
+
51
110
  def _get_field_type(
52
111
  field_type: TypeNode, was_non_nullable: bool = False
53
112
  ) -> cst.BaseExpression:
@@ -85,7 +144,19 @@ def _get_field_type(
85
144
  )
86
145
 
87
146
 
88
- def _get_argument(name: str, value: str) -> cst.Arg:
147
+ def _sanitize_argument(value: ArgumentValue) -> cst.SimpleString | cst.Name | cst.List:
148
+ if isinstance(value, bool):
149
+ return cst.Name(value=str(value))
150
+
151
+ if isinstance(value, list):
152
+ return cst.List(
153
+ elements=[
154
+ cst.Element(value=_sanitize_argument(item))
155
+ for item in value
156
+ if item is not None
157
+ ],
158
+ )
159
+
89
160
  if "\n" in value:
90
161
  argument_value = cst.SimpleString(f'"""\n{value}\n"""')
91
162
  elif '"' in value:
@@ -93,6 +164,12 @@ def _get_argument(name: str, value: str) -> cst.Arg:
93
164
  else:
94
165
  argument_value = cst.SimpleString(f'"{value}"')
95
166
 
167
+ return argument_value
168
+
169
+
170
+ def _get_argument(name: str, value: ArgumentValue) -> cst.Arg:
171
+ argument_value = _sanitize_argument(value)
172
+
96
173
  return cst.Arg(
97
174
  value=argument_value,
98
175
  keyword=cst.Name(name),
@@ -100,7 +177,27 @@ def _get_argument(name: str, value: str) -> cst.Arg:
100
177
  )
101
178
 
102
179
 
103
- def _get_field_value(description: str | None, alias: str | None) -> cst.Call | None:
180
+ # TODO: this might be removed now
181
+ def _get_argument_list(name: str, values: list[ArgumentValue]) -> cst.Arg:
182
+ value = cst.List(
183
+ elements=[cst.Element(value=_sanitize_argument(value)) for value in values],
184
+ )
185
+
186
+ return cst.Arg(
187
+ value=value,
188
+ keyword=cst.Name(name),
189
+ equal=cst.AssignEqual(cst.SimpleWhitespace(""), cst.SimpleWhitespace("")),
190
+ )
191
+
192
+
193
+ def _get_field_value(
194
+ field: FieldDefinitionNode | InputValueDefinitionNode,
195
+ alias: str | None,
196
+ is_apollo_federation: bool,
197
+ imports: set[Import],
198
+ ) -> cst.Call | None:
199
+ description = field.description.value if field.description else None
200
+
104
201
  args = list(
105
202
  filter(
106
203
  None,
@@ -111,6 +208,24 @@ def _get_field_value(description: str | None, alias: str | None) -> cst.Call | N
111
208
  )
112
209
  )
113
210
 
211
+ directives = _get_directives(field)
212
+
213
+ apollo_federation_args = _get_federation_arguments(directives, imports)
214
+
215
+ if is_apollo_federation and apollo_federation_args:
216
+ args.extend(apollo_federation_args)
217
+
218
+ return cst.Call(
219
+ func=cst.Attribute(
220
+ value=cst.Attribute(
221
+ value=cst.Name("strawberry"),
222
+ attr=cst.Name("federation"),
223
+ ),
224
+ attr=cst.Name("field"),
225
+ ),
226
+ args=args,
227
+ )
228
+
114
229
  if args:
115
230
  return cst.Call(
116
231
  func=cst.Attribute(
@@ -125,6 +240,8 @@ def _get_field_value(description: str | None, alias: str | None) -> cst.Call | N
125
240
 
126
241
  def _get_field(
127
242
  field: FieldDefinitionNode | InputValueDefinitionNode,
243
+ is_apollo_federation: bool,
244
+ imports: set[Import],
128
245
  ) -> cst.SimpleStatementLine:
129
246
  name = to_snake_case(field.name.value)
130
247
  alias: str | None = None
@@ -141,19 +258,131 @@ def _get_field(
141
258
  _get_field_type(field.type),
142
259
  ),
143
260
  value=_get_field_value(
144
- description=field.description.value if field.description else None,
145
- alias=alias if alias != name else None,
261
+ field,
262
+ alias=alias,
263
+ is_apollo_federation=is_apollo_federation,
264
+ imports=imports,
146
265
  ),
147
266
  )
148
267
  ]
149
268
  )
150
269
 
151
270
 
271
+ ArgumentValue: TypeAlias = Union[str, bool, List["ArgumentValue"]]
272
+
273
+
274
+ def _get_argument_value(argument_value: ConstValueNode) -> ArgumentValue:
275
+ if isinstance(argument_value, StringValueNode):
276
+ return argument_value.value
277
+ elif isinstance(argument_value, EnumValueDefinitionNode):
278
+ return argument_value.name.value
279
+ elif isinstance(argument_value, ListValueNode):
280
+ return [_get_argument_value(arg) for arg in argument_value.values]
281
+ elif isinstance(argument_value, BooleanValueNode):
282
+ return argument_value.value
283
+ else:
284
+ raise NotImplementedError(f"Unknown argument value {argument_value}")
285
+
286
+
287
+ def _get_directives(
288
+ definition: HasDirectives,
289
+ ) -> dict[str, list[dict[str, ArgumentValue]]]:
290
+ directives: dict[str, list[dict[str, ArgumentValue]]] = defaultdict(list)
291
+
292
+ for directive in definition.directives:
293
+ directive_name = directive.name.value
294
+
295
+ directives[directive_name].append(
296
+ {
297
+ argument.name.value: _get_argument_value(argument.value)
298
+ for argument in directive.arguments
299
+ }
300
+ )
301
+
302
+ return directives
303
+
304
+
305
+ def _get_federation_arguments(
306
+ directives: dict[str, list[dict[str, ArgumentValue]]],
307
+ imports: set[Import],
308
+ ) -> list[cst.Arg]:
309
+ def append_arg_from_directive(
310
+ directive: str,
311
+ argument_name: str,
312
+ keyword_name: str | None = None,
313
+ flatten: bool = True,
314
+ ):
315
+ keyword_name = keyword_name or directive
316
+
317
+ if directive in directives:
318
+ values = [item[argument_name] for item in directives[directive]]
319
+
320
+ if flatten:
321
+ arguments.append(_get_argument(keyword_name, values))
322
+ else:
323
+ arguments.extend(_get_argument(keyword_name, value) for value in values)
324
+
325
+ arguments: list[cst.Arg] = []
326
+
327
+ append_arg_from_directive("key", "fields", "keys")
328
+ append_arg_from_directive("requires", "fields")
329
+ append_arg_from_directive("provides", "fields")
330
+ append_arg_from_directive(
331
+ "requiresScopes", "scopes", "requires_scopes", flatten=False
332
+ )
333
+ append_arg_from_directive("policy", "policies", "policy", flatten=False)
334
+ append_arg_from_directive("tag", "name", "tags")
335
+
336
+ boolean_keys = (
337
+ "shareable",
338
+ "inaccessible",
339
+ "external",
340
+ "authenticated",
341
+ )
342
+
343
+ arguments.extend(
344
+ _get_argument(key, True) for key in boolean_keys if directives.get(key, False)
345
+ )
346
+
347
+ if overrides := directives.get("override"):
348
+ override = overrides[0]
349
+
350
+ if "label" not in override:
351
+ arguments.append(_get_argument("override", override["from"]))
352
+ else:
353
+ imports.add(
354
+ Import(
355
+ module="strawberry.federation.schema_directives",
356
+ imports=("Override",),
357
+ )
358
+ )
359
+
360
+ arguments.append(
361
+ cst.Arg(
362
+ keyword=cst.Name("override"),
363
+ value=cst.Call(
364
+ func=cst.Name("Override"),
365
+ args=[
366
+ _get_argument("override_from", override["from"]),
367
+ _get_argument("label", override["label"]),
368
+ ],
369
+ ),
370
+ equal=cst.AssignEqual(
371
+ cst.SimpleWhitespace(""), cst.SimpleWhitespace("")
372
+ ),
373
+ )
374
+ )
375
+
376
+ return arguments
377
+
378
+
152
379
  def _get_strawberry_decorator(
153
380
  definition: ObjectTypeDefinitionNode
154
381
  | ObjectTypeExtensionNode
155
382
  | InterfaceTypeDefinitionNode
156
383
  | InputObjectTypeDefinitionNode,
384
+ is_apollo_federation: bool,
385
+ imports: set[Import],
157
386
  ) -> cst.Decorator:
158
387
  type_ = {
159
388
  ObjectTypeDefinitionNode: "type",
@@ -168,15 +397,36 @@ def _get_strawberry_decorator(
168
397
  else None
169
398
  )
170
399
 
400
+ directives = _get_directives(definition)
401
+
171
402
  decorator: cst.BaseExpression = cst.Attribute(
172
403
  value=cst.Name("strawberry"),
173
404
  attr=cst.Name(type_),
174
405
  )
175
406
 
407
+ arguments: list[cst.Arg] = []
408
+
176
409
  if description is not None:
410
+ arguments.append(_get_argument("description", description.value))
411
+
412
+ federation_arguments = _get_federation_arguments(directives, imports)
413
+
414
+ # and has any directive that is a federation directive
415
+ if is_apollo_federation and federation_arguments:
416
+ decorator = cst.Attribute(
417
+ value=cst.Attribute(
418
+ value=cst.Name("strawberry"),
419
+ attr=cst.Name("federation"),
420
+ ),
421
+ attr=cst.Name(type_),
422
+ )
423
+
424
+ arguments.extend(federation_arguments)
425
+
426
+ if arguments:
177
427
  decorator = cst.Call(
178
428
  func=decorator,
179
- args=[_get_argument("description", description.value)],
429
+ args=arguments,
180
430
  )
181
431
 
182
432
  return cst.Decorator(
@@ -189,8 +439,10 @@ def _get_class_definition(
189
439
  | ObjectTypeExtensionNode
190
440
  | InterfaceTypeDefinitionNode
191
441
  | InputObjectTypeDefinitionNode,
442
+ is_apollo_federation: bool,
443
+ imports: set[Import],
192
444
  ) -> cst.ClassDef:
193
- decorator = _get_strawberry_decorator(definition)
445
+ decorator = _get_strawberry_decorator(definition, is_apollo_federation, imports)
194
446
 
195
447
  bases = (
196
448
  [cst.Arg(cst.Name(interface.name.value)) for interface in definition.interfaces]
@@ -204,7 +456,12 @@ def _get_class_definition(
204
456
  return cst.ClassDef(
205
457
  name=cst.Name(definition.name.value),
206
458
  bases=bases,
207
- body=cst.IndentedBlock(body=[_get_field(field) for field in definition.fields]),
459
+ body=cst.IndentedBlock(
460
+ body=[
461
+ _get_field(field, is_apollo_federation, imports)
462
+ for field in definition.fields
463
+ ]
464
+ ),
208
465
  decorators=[decorator],
209
466
  )
210
467
 
@@ -243,6 +500,7 @@ def _get_schema_definition(
243
500
  root_query_name: str | None,
244
501
  root_mutation_name: str | None,
245
502
  root_subscription_name: str | None,
503
+ is_apollo_federation: bool,
246
504
  ) -> cst.SimpleStatementLine | None:
247
505
  if not any([root_query_name, root_mutation_name, root_subscription_name]):
248
506
  return None
@@ -265,39 +523,45 @@ def _get_schema_definition(
265
523
  if root_subscription_name:
266
524
  args.append(_get_arg("subscription", root_subscription_name))
267
525
 
526
+ schema_call = cst.Call(
527
+ func=cst.Attribute(
528
+ value=cst.Name("strawberry"),
529
+ attr=cst.Name("Schema"),
530
+ ),
531
+ args=args,
532
+ )
533
+
534
+ if is_apollo_federation:
535
+ args.append(
536
+ cst.Arg(
537
+ keyword=cst.Name("enable_federation_2"),
538
+ value=cst.Name("True"),
539
+ equal=cst.AssignEqual(
540
+ cst.SimpleWhitespace(""), cst.SimpleWhitespace("")
541
+ ),
542
+ )
543
+ )
544
+ schema_call = cst.Call(
545
+ func=cst.Attribute(
546
+ value=cst.Attribute(
547
+ value=cst.Name(value="strawberry"),
548
+ attr=cst.Name(value="federation"),
549
+ ),
550
+ attr=cst.Name(value="Schema"),
551
+ ),
552
+ args=args,
553
+ )
554
+
268
555
  return cst.SimpleStatementLine(
269
556
  body=[
270
557
  cst.Assign(
271
558
  targets=[cst.AssignTarget(cst.Name("schema"))],
272
- value=cst.Call(
273
- func=cst.Attribute(
274
- value=cst.Name("strawberry"),
275
- attr=cst.Name("Schema"),
276
- ),
277
- args=args,
278
- ),
559
+ value=schema_call,
279
560
  )
280
561
  ]
281
562
  )
282
563
 
283
564
 
284
- @dataclasses.dataclass(frozen=True)
285
- class Import:
286
- module: str | None
287
- imports: tuple[str]
288
-
289
- def to_cst(self) -> cst.Import | cst.ImportFrom:
290
- if self.module is None:
291
- return cst.Import(
292
- names=[cst.ImportAlias(name=cst.Name(name)) for name in self.imports]
293
- )
294
-
295
- return cst.ImportFrom(
296
- module=cst.Name(self.module),
297
- names=[cst.ImportAlias(name=cst.Name(name)) for name in self.imports],
298
- )
299
-
300
-
301
565
  def _get_union_definition(definition: UnionTypeDefinitionNode) -> cst.Assign:
302
566
  name = definition.name.value
303
567
 
@@ -430,6 +694,12 @@ def codegen(schema: str) -> str:
430
694
 
431
695
  object_types: dict[str, cst.ClassDef] = {}
432
696
 
697
+ # when we encounter a extend schema @link ..., we check if is an apollo federation schema
698
+ # and we use this variable to keep track of it, but at the moment the assumption is that
699
+ # the schema extension is always done at the top, this might not be the case all the
700
+ # time
701
+ is_apollo_federation = False
702
+
433
703
  for definition in document.definitions:
434
704
  if isinstance(
435
705
  definition,
@@ -440,7 +710,9 @@ def codegen(schema: str) -> str:
440
710
  ObjectTypeExtensionNode,
441
711
  ),
442
712
  ):
443
- class_definition = _get_class_definition(definition)
713
+ class_definition = _get_class_definition(
714
+ definition, is_apollo_federation, imports
715
+ )
444
716
 
445
717
  object_types[definition.name.value] = class_definition
446
718
 
@@ -478,6 +750,11 @@ def codegen(schema: str) -> str:
478
750
  definitions.append(cst.EmptyLine())
479
751
  definitions.append(scalar_definition)
480
752
  definitions.append(cst.EmptyLine())
753
+ elif isinstance(definition, SchemaExtensionNode):
754
+ is_apollo_federation = any(
755
+ _is_federation_link_directive(directive)
756
+ for directive in definition.directives
757
+ )
481
758
  else:
482
759
  raise NotImplementedError(f"Unknown definition {definition}")
483
760
 
@@ -496,6 +773,7 @@ def codegen(schema: str) -> str:
496
773
  root_query_name=root_query_name,
497
774
  root_mutation_name=root_mutation_name,
498
775
  root_subscription_name=root_subscription_name,
776
+ is_apollo_federation=is_apollo_federation,
499
777
  )
500
778
 
501
779
  if schema_definition:
strawberry/types/nodes.py CHANGED
@@ -9,6 +9,7 @@ If a node has only one useful value, it's value is inlined.
9
9
  If a list of nodes have unique names, it's transformed into a mapping.
10
10
  Note Python dicts maintain ordering (for all supported versions).
11
11
  """
12
+
12
13
  from __future__ import annotations
13
14
 
14
15
  import dataclasses
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strawberry-graphql
3
- Version: 0.222.0
3
+ Version: 0.224.0
4
4
  Summary: A library for creating GraphQL APIs
5
5
  Home-page: https://strawberry.rocks/
6
6
  License: MIT
@@ -22,7 +22,7 @@ strawberry/channels/__init__.py,sha256=9oRdAT7uIYETF-23gZ5NteOXSjwkUtwRmwu3YCFv1
22
22
  strawberry/channels/handlers/base.py,sha256=tgXLjKCHrP-8yOJV-qo9iOMJUgDe2wW6rGs_nDZFi80,7869
23
23
  strawberry/channels/handlers/graphql_transport_ws_handler.py,sha256=UKfxRoo1pEv1CBHhkhSTkbztJlfU-OoksFwyLktj8xc,1945
24
24
  strawberry/channels/handlers/graphql_ws_handler.py,sha256=PHRkwnXt3tY4E0XBVHh4hpTyFBt9jQfTreBUmEo9vhI,2497
25
- strawberry/channels/handlers/http_handler.py,sha256=9pW978XaeF-aFWM9WMaSHCOWmcWoIJCNkW8X3lKJcws,9560
25
+ strawberry/channels/handlers/http_handler.py,sha256=c1Jpf7L4b7LvSaOolP2e9TWcEM5IqHXhScK0kZrhSg8,9561
26
26
  strawberry/channels/handlers/ws_handler.py,sha256=sHL44eay4tNoKzkrRn3WewSYH-3ZSJzxJpmBJ-aTkeM,4650
27
27
  strawberry/channels/router.py,sha256=dyOBbSF8nFiygP0zz6MM14mhkvFQAEbbLBXzcpubSHM,1927
28
28
  strawberry/channels/testing.py,sha256=IWj1CuIS3vOo2f2fw0W-0GCz-YSs7QSAAscC6suqtiI,5668
@@ -81,16 +81,16 @@ strawberry/exceptions/syntax.py,sha256=OCz2Ai1Yn0jonRZXCDI2h30tPNsa8L5wMU8vcqIO4
81
81
  strawberry/exceptions/unresolved_field_type.py,sha256=voYLO7kj_8DW0r3jvwn82HOqFIWk_3jvoPCciXFv5JA,1857
82
82
  strawberry/exceptions/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
83
  strawberry/exceptions/utils/source_finder.py,sha256=AcuevLgWwOSOR5zBDJbVyXMGRUxsTzrr07XUB0UqNiI,20044
84
- strawberry/experimental/__init__.py,sha256=n6Ytr6wBgXt47jLY8rEXQs7G4gZEkm9BCLL0Q8opWfw,94
84
+ strawberry/experimental/__init__.py,sha256=2HP5XtxL8ZKsPp4EDRAbMCqiP7p2V4Cca278JUGxnt0,102
85
85
  strawberry/experimental/pydantic/__init__.py,sha256=jlsYH1j_9W4ieRpUKgt5zQPERDL7nc1ZBNQ3dAhJs8w,241
86
- strawberry/experimental/pydantic/_compat.py,sha256=ofbkbF6wSlBedCKj3qprAgcCfWlXdGL5iUZo355uAvA,3137
86
+ strawberry/experimental/pydantic/_compat.py,sha256=sBWil9Ggf-OqfK35XB-0-rx9MS66QFVEqptPkF3sjC0,8139
87
87
  strawberry/experimental/pydantic/conversion.py,sha256=_YMz4cYCwLUazkT2oCQ4B1gusksnXDHdFm1DldIrU7Q,4213
88
88
  strawberry/experimental/pydantic/conversion_types.py,sha256=psVRmKE5fVcBAUT0Z2vCOLAXHddat2A0jJ-0SB1XDn8,963
89
- strawberry/experimental/pydantic/error_type.py,sha256=Fy7m2E03a86XsF76UoAUeroiJLnvVxBwKKEt6RcMfKs,4311
89
+ strawberry/experimental/pydantic/error_type.py,sha256=s8v0weIVM-9c0e3zqySaaa3pGZan7RvKTGuTtXLGPmE,4366
90
90
  strawberry/experimental/pydantic/exceptions.py,sha256=GJt22DTolA24A0ZHcHsy-fcRkBnavgUZYOo0MBIVcjs,1427
91
- strawberry/experimental/pydantic/fields.py,sha256=m7LJEQ39wko4NRIjYPxhUbwNyZcubrbZmyBumG34CJM,5356
92
- strawberry/experimental/pydantic/object_type.py,sha256=kb1gwHw7vQCshk84a7DMRBp_9zDntrAK3bbI58vjqf0,12160
93
- strawberry/experimental/pydantic/utils.py,sha256=vh9Mz33b5QhomuCMVygZ2mp2jrBOxVejIRK0XdfpygI,3828
91
+ strawberry/experimental/pydantic/fields.py,sha256=QP5vYb8vKf7vOC69cjUPGOdlO_j4kZRdxalWgeqJ5Hk,2588
92
+ strawberry/experimental/pydantic/object_type.py,sha256=gZ_-SJoH4ZG6CIVa2BBInzW9TIXeTw_0-q74a-x1EuI,12372
93
+ strawberry/experimental/pydantic/utils.py,sha256=9m_oKyz0K1rTmh1ZrYtbFfyUNsiV11RiEb7bLrZuOXs,3770
94
94
  strawberry/ext/LICENSE,sha256=_oY0TZg0b_sW0--0T44aMTpy2e2zF1Kiyn8E1qDiivo,1249
95
95
  strawberry/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
96
  strawberry/ext/dataclasses/LICENSE,sha256=WZgm35K_3NJwLqxpEHJJi7CWxVrwTumEz5D3Dtd7WnA,13925
@@ -165,7 +165,7 @@ strawberry/parent.py,sha256=mCnJcLBQKwtokXcGxkm5HqGg7Wkst97rn61ViuaotrM,829
165
165
  strawberry/permission.py,sha256=Rm_dft2AGZCSgBRQuxWEfncgWj88nmSdL-5mJ6TvHeA,5917
166
166
  strawberry/printer/__init__.py,sha256=DmepjmgtkdF5RxK_7yC6qUyRWn56U-9qeZMbkztYB9w,62
167
167
  strawberry/printer/ast_from_value.py,sha256=MFIX2V51d9ocRvD0Njemjk8YIzKh2BB1g2iUcX6a3d8,4946
168
- strawberry/printer/printer.py,sha256=x6qGNchRAb75yBxOJV8jzHYSE42uaJns_PC9ulKVsYI,17454
168
+ strawberry/printer/printer.py,sha256=PXuYfAJxMU92aRETeSixhJjk-XUDu2ZFAFwkogzTu_Y,17461
169
169
  strawberry/private.py,sha256=fS5IbVUajzr-6NcR41yt4ukXDPAMzwe1WPNdRJP5wWk,534
170
170
  strawberry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
171
  strawberry/quart/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -194,7 +194,7 @@ strawberry/schema/types/__init__.py,sha256=oHO3COWhL3L1KLYCJNY1XFf5xt2GGtHiMC-Ua
194
194
  strawberry/schema/types/base_scalars.py,sha256=Z_BmgwLicNexLipGyw6MmZ7OBnkGJU3ySgaY9SwBWrw,1837
195
195
  strawberry/schema/types/concrete_type.py,sha256=HB30G1hMUuuvjAvfSe6ADS35iI_T_wKO-EprVOWTMSs,746
196
196
  strawberry/schema/types/scalar.py,sha256=SVJ8HiKncCvOw2xwABI5xYaHcC7KkGHG-tx2WDtSoCA,2802
197
- strawberry/schema_codegen/__init__.py,sha256=U-ABa02BAfCN6zEGlc2LJEju18ErtGjbf0S8BhJXc6w,15592
197
+ strawberry/schema_codegen/__init__.py,sha256=2TO50tEZXQkH9yLUEBCZXEd0tUuzZ_XcY5dcKaf26ps,24040
198
198
  strawberry/schema_directive.py,sha256=GxiOedFB-RJAflpQNUZv00C5Z6gavR-AYdsvoCA_0jc,1963
199
199
  strawberry/starlite/__init__.py,sha256=v209swT8H9MljVL-npvANhEO1zz3__PSfxb_Ix-NoeE,134
200
200
  strawberry/starlite/controller.py,sha256=x6Mm3r36cRfzo6hz9B4AYWbVh2QlYtndYcXFOr_3THM,11860
@@ -223,7 +223,7 @@ strawberry/types/fields/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
223
223
  strawberry/types/fields/resolver.py,sha256=Lwy2XVKnTbDyibk9pPsWsunlm22akXjy-gKCg22Tp1A,14130
224
224
  strawberry/types/graphql.py,sha256=3SWZEsa0Zy1eVW6vy75BnB7t9_lJVi6TBV3_1j3RNBs,687
225
225
  strawberry/types/info.py,sha256=b1ZWW_wUop6XrGNcGHKBQeUYjlX-y8u3s2Wm_XhKPYI,3412
226
- strawberry/types/nodes.py,sha256=2ZVa1HOFgHZ96QTfz3QEr1fufi6Sz9hAzcZxsN7KtGE,5026
226
+ strawberry/types/nodes.py,sha256=5tTYmxGpVDshbydicHTTBWEiUe8A7p7mdiaSV8Ry80Y,5027
227
227
  strawberry/types/type_resolver.py,sha256=F0z_geS4VEun8EhD571LaTgI8ypjCeLfp910gF0Q3MY,6280
228
228
  strawberry/types/types.py,sha256=t5MOV4xiutPL2Ka02u3Z2V3eqy2R2JPYQxsqCnY2Aqk,7139
229
229
  strawberry/union.py,sha256=5DOzsmsrI4tU4aXO7BBEN-570ABQ9_VkRDvUJUrIofY,9926
@@ -241,8 +241,8 @@ strawberry/utils/logging.py,sha256=flS7hV0JiIOEdXcrIjda4WyIWix86cpHHFNJL8gl1y4,7
241
241
  strawberry/utils/operation.py,sha256=Um-tBCPl3_bVFN2Ph7o1mnrxfxBes4HFCj6T0x4kZxE,1135
242
242
  strawberry/utils/str_converters.py,sha256=avIgPVLg98vZH9mA2lhzVdyyjqzLsK2NdBw9mJQ02Xk,813
243
243
  strawberry/utils/typing.py,sha256=Qxz1LwyVsNGV7LQW1dFsaUbsswj5LHBOdKLMom5eyEA,13491
244
- strawberry_graphql-0.222.0.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
245
- strawberry_graphql-0.222.0.dist-info/METADATA,sha256=yc6q0kAtPFBIqo0Whk1t6bxehAmcOKFfnd4-4lcAIcs,7740
246
- strawberry_graphql-0.222.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
247
- strawberry_graphql-0.222.0.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
248
- strawberry_graphql-0.222.0.dist-info/RECORD,,
244
+ strawberry_graphql-0.224.0.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
245
+ strawberry_graphql-0.224.0.dist-info/METADATA,sha256=lahprMtPJhhknOqREgJb0Yc-JujPzgHJXA5loc8tOi8,7740
246
+ strawberry_graphql-0.224.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
247
+ strawberry_graphql-0.224.0.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
248
+ strawberry_graphql-0.224.0.dist-info/RECORD,,