strawberry-graphql 0.221.0__py3-none-any.whl → 0.221.0.dev1710955937__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.
strawberry/__init__.py CHANGED
@@ -14,7 +14,6 @@ from .private import Private
14
14
  from .scalars import ID
15
15
  from .schema import Schema
16
16
  from .schema_directive import schema_directive
17
- from .types.info import Info
18
17
  from .union import union
19
18
  from .unset import UNSET
20
19
 
@@ -22,7 +21,6 @@ __all__ = [
22
21
  "BasePermission",
23
22
  "experimental",
24
23
  "ID",
25
- "Info",
26
24
  "UNSET",
27
25
  "lazy",
28
26
  "LazyType",
@@ -65,7 +65,7 @@ class InputMutationExtension(FieldExtension):
65
65
  self,
66
66
  next_: SyncExtensionResolver,
67
67
  source: Any,
68
- info: Info,
68
+ info: Info[Any, Any],
69
69
  **kwargs: Any,
70
70
  ) -> Any:
71
71
  input_args = kwargs.pop("input")
@@ -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, Tuple
7
+ from typing_extensions import Protocol
5
8
 
6
9
  import libcst as cst
7
10
  from graphql import (
@@ -19,6 +22,7 @@ from graphql import (
19
22
  OperationType,
20
23
  ScalarTypeDefinitionNode,
21
24
  SchemaDefinitionNode,
25
+ SchemaExtensionNode,
22
26
  StringValueNode,
23
27
  TypeNode,
24
28
  UnionTypeDefinitionNode,
@@ -27,6 +31,14 @@ from graphql import (
27
31
 
28
32
  from strawberry.utils.str_converters import to_snake_case
29
33
 
34
+ if TYPE_CHECKING:
35
+ from graphql.language.ast import ConstDirectiveNode
36
+
37
+
38
+ class HasDirectives(Protocol):
39
+ directives: Tuple[ConstDirectiveNode]
40
+
41
+
30
42
  _SCALAR_MAP = {
31
43
  "Int": cst.Name("int"),
32
44
  "Float": cst.Name("float"),
@@ -48,6 +60,19 @@ _SCALAR_MAP = {
48
60
  }
49
61
 
50
62
 
63
+ def _is_federation_link_directive(directive: ConstDirectiveNode) -> bool:
64
+ if directive.name.value != "link":
65
+ return False
66
+
67
+ for argument in directive.arguments:
68
+ if argument.name.value == "url":
69
+ return argument.value.value.startswith(
70
+ "https://specs.apollo.dev/federation"
71
+ )
72
+
73
+ return False
74
+
75
+
51
76
  def _get_field_type(
52
77
  field_type: TypeNode, was_non_nullable: bool = False
53
78
  ) -> cst.BaseExpression:
@@ -85,7 +110,10 @@ def _get_field_type(
85
110
  )
86
111
 
87
112
 
88
- def _get_argument(name: str, value: str) -> cst.Arg:
113
+ def _sanitize_argument(value: str | bool) -> cst.SimpleString | cst.Name:
114
+ if isinstance(value, bool):
115
+ return cst.Name(value=str(value))
116
+
89
117
  if "\n" in value:
90
118
  argument_value = cst.SimpleString(f'"""\n{value}\n"""')
91
119
  elif '"' in value:
@@ -93,6 +121,12 @@ def _get_argument(name: str, value: str) -> cst.Arg:
93
121
  else:
94
122
  argument_value = cst.SimpleString(f'"{value}"')
95
123
 
124
+ return argument_value
125
+
126
+
127
+ def _get_argument(name: str, value: str | bool) -> cst.Arg:
128
+ argument_value = _sanitize_argument(value)
129
+
96
130
  return cst.Arg(
97
131
  value=argument_value,
98
132
  keyword=cst.Name(name),
@@ -100,7 +134,25 @@ def _get_argument(name: str, value: str) -> cst.Arg:
100
134
  )
101
135
 
102
136
 
103
- def _get_field_value(description: str | None, alias: str | None) -> cst.Call | None:
137
+ def _get_argument_list(name: str, values: list[str]) -> cst.Arg:
138
+ value = cst.List(
139
+ elements=[cst.Element(value=_sanitize_argument(value)) for value in values],
140
+ )
141
+
142
+ return cst.Arg(
143
+ value=value,
144
+ keyword=cst.Name(name),
145
+ equal=cst.AssignEqual(cst.SimpleWhitespace(""), cst.SimpleWhitespace("")),
146
+ )
147
+
148
+
149
+ def _get_field_value(
150
+ field: FieldDefinitionNode | InputValueDefinitionNode,
151
+ alias: str | None,
152
+ is_apollo_federation: bool,
153
+ ) -> cst.Call | None:
154
+ description = field.description.value if field.description else None
155
+
104
156
  args = list(
105
157
  filter(
106
158
  None,
@@ -111,6 +163,24 @@ def _get_field_value(description: str | None, alias: str | None) -> cst.Call | N
111
163
  )
112
164
  )
113
165
 
166
+ directives = _get_directives(field)
167
+
168
+ apollo_federation_args = _get_federation_arguments(directives)
169
+
170
+ if is_apollo_federation and apollo_federation_args:
171
+ args.extend(apollo_federation_args)
172
+
173
+ return cst.Call(
174
+ func=cst.Attribute(
175
+ value=cst.Attribute(
176
+ value=cst.Name("strawberry"),
177
+ attr=cst.Name("federation"),
178
+ ),
179
+ attr=cst.Name("field"),
180
+ ),
181
+ args=args,
182
+ )
183
+
114
184
  if args:
115
185
  return cst.Call(
116
186
  func=cst.Attribute(
@@ -125,6 +195,7 @@ def _get_field_value(description: str | None, alias: str | None) -> cst.Call | N
125
195
 
126
196
  def _get_field(
127
197
  field: FieldDefinitionNode | InputValueDefinitionNode,
198
+ is_apollo_federation: bool,
128
199
  ) -> cst.SimpleStatementLine:
129
200
  name = to_snake_case(field.name.value)
130
201
  alias: str | None = None
@@ -141,19 +212,67 @@ def _get_field(
141
212
  _get_field_type(field.type),
142
213
  ),
143
214
  value=_get_field_value(
144
- description=field.description.value if field.description else None,
145
- alias=alias if alias != name else None,
215
+ field, alias=alias, is_apollo_federation=is_apollo_federation
146
216
  ),
147
217
  )
148
218
  ]
149
219
  )
150
220
 
151
221
 
222
+ def _get_directives(definition: HasDirectives) -> dict[str, list[dict[str, str]]]:
223
+ directives = defaultdict(list)
224
+
225
+ for directive in definition.directives:
226
+ directive_name = directive.name.value
227
+
228
+ directives[directive_name].append(
229
+ {
230
+ argument.name.value: argument.value.value
231
+ for argument in directive.arguments
232
+ }
233
+ )
234
+
235
+ return directives
236
+
237
+
238
+ def _get_federation_arguments(
239
+ directives: dict[str, list[dict[str, str]]],
240
+ ) -> list[cst.Arg]:
241
+ def append_arg_from_directive(
242
+ directive: str, argument_name: str, keyword_name: str | None = None
243
+ ):
244
+ keyword_name = keyword_name or directive
245
+
246
+ if directive in directives:
247
+ arguments.append(
248
+ _get_argument_list(
249
+ keyword_name,
250
+ [item[argument_name] for item in directives[directive]],
251
+ )
252
+ )
253
+
254
+ arguments: list[cst.Arg] = []
255
+
256
+ append_arg_from_directive("key", "fields", "keys")
257
+ append_arg_from_directive("requires", "fields")
258
+ append_arg_from_directive("provides", "fields")
259
+ append_arg_from_directive("tag", "name", "tags")
260
+
261
+ boolean_keys = ("shareable", "inaccessible", "external", "override")
262
+
263
+ arguments.extend(
264
+ _get_argument(key, True) for key in boolean_keys if directives.get(key, False)
265
+ )
266
+
267
+ return arguments
268
+
269
+
152
270
  def _get_strawberry_decorator(
153
271
  definition: ObjectTypeDefinitionNode
154
272
  | ObjectTypeExtensionNode
155
273
  | InterfaceTypeDefinitionNode
156
274
  | InputObjectTypeDefinitionNode,
275
+ is_apollo_federation: bool,
157
276
  ) -> cst.Decorator:
158
277
  type_ = {
159
278
  ObjectTypeDefinitionNode: "type",
@@ -168,15 +287,36 @@ def _get_strawberry_decorator(
168
287
  else None
169
288
  )
170
289
 
290
+ directives = _get_directives(definition)
291
+
171
292
  decorator: cst.BaseExpression = cst.Attribute(
172
293
  value=cst.Name("strawberry"),
173
294
  attr=cst.Name(type_),
174
295
  )
175
296
 
297
+ arguments: list[cst.Arg] = []
298
+
176
299
  if description is not None:
300
+ arguments.append(_get_argument("description", description.value))
301
+
302
+ federation_arguments = _get_federation_arguments(directives)
303
+
304
+ # and has any directive that is a federation directive
305
+ if is_apollo_federation and federation_arguments:
306
+ decorator = cst.Attribute(
307
+ value=cst.Attribute(
308
+ value=cst.Name("strawberry"),
309
+ attr=cst.Name("federation"),
310
+ ),
311
+ attr=cst.Name(type_),
312
+ )
313
+
314
+ arguments.extend(federation_arguments)
315
+
316
+ if arguments:
177
317
  decorator = cst.Call(
178
318
  func=decorator,
179
- args=[_get_argument("description", description.value)],
319
+ args=arguments,
180
320
  )
181
321
 
182
322
  return cst.Decorator(
@@ -189,8 +329,9 @@ def _get_class_definition(
189
329
  | ObjectTypeExtensionNode
190
330
  | InterfaceTypeDefinitionNode
191
331
  | InputObjectTypeDefinitionNode,
332
+ is_apollo_federation: bool,
192
333
  ) -> cst.ClassDef:
193
- decorator = _get_strawberry_decorator(definition)
334
+ decorator = _get_strawberry_decorator(definition, is_apollo_federation)
194
335
 
195
336
  bases = (
196
337
  [cst.Arg(cst.Name(interface.name.value)) for interface in definition.interfaces]
@@ -204,7 +345,11 @@ def _get_class_definition(
204
345
  return cst.ClassDef(
205
346
  name=cst.Name(definition.name.value),
206
347
  bases=bases,
207
- body=cst.IndentedBlock(body=[_get_field(field) for field in definition.fields]),
348
+ body=cst.IndentedBlock(
349
+ body=[
350
+ _get_field(field, is_apollo_federation) for field in definition.fields
351
+ ]
352
+ ),
208
353
  decorators=[decorator],
209
354
  )
210
355
 
@@ -243,6 +388,7 @@ def _get_schema_definition(
243
388
  root_query_name: str | None,
244
389
  root_mutation_name: str | None,
245
390
  root_subscription_name: str | None,
391
+ is_apollo_federation: bool,
246
392
  ) -> cst.SimpleStatementLine | None:
247
393
  if not any([root_query_name, root_mutation_name, root_subscription_name]):
248
394
  return None
@@ -265,17 +411,40 @@ def _get_schema_definition(
265
411
  if root_subscription_name:
266
412
  args.append(_get_arg("subscription", root_subscription_name))
267
413
 
414
+ schema_call = cst.Call(
415
+ func=cst.Attribute(
416
+ value=cst.Name("strawberry"),
417
+ attr=cst.Name("Schema"),
418
+ ),
419
+ args=args,
420
+ )
421
+
422
+ if is_apollo_federation:
423
+ args.append(
424
+ cst.Arg(
425
+ keyword=cst.Name("enable_federation_2"),
426
+ value=cst.Name("True"),
427
+ equal=cst.AssignEqual(
428
+ cst.SimpleWhitespace(""), cst.SimpleWhitespace("")
429
+ ),
430
+ )
431
+ )
432
+ schema_call = cst.Call(
433
+ func=cst.Attribute(
434
+ value=cst.Attribute(
435
+ value=cst.Name(value="strawberry"),
436
+ attr=cst.Name(value="federation"),
437
+ ),
438
+ attr=cst.Name(value="Schema"),
439
+ ),
440
+ args=args,
441
+ )
442
+
268
443
  return cst.SimpleStatementLine(
269
444
  body=[
270
445
  cst.Assign(
271
446
  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
- ),
447
+ value=schema_call,
279
448
  )
280
449
  ]
281
450
  )
@@ -430,6 +599,12 @@ def codegen(schema: str) -> str:
430
599
 
431
600
  object_types: dict[str, cst.ClassDef] = {}
432
601
 
602
+ # when we encounter a extend schema @link ..., we check if is an apollo federation schema
603
+ # and we use this variable to keep track of it, but at the moment the assumption is that
604
+ # the schema extension is always done at the top, this might not be the case all the
605
+ # time
606
+ is_apollo_federation = False
607
+
433
608
  for definition in document.definitions:
434
609
  if isinstance(
435
610
  definition,
@@ -440,7 +615,7 @@ def codegen(schema: str) -> str:
440
615
  ObjectTypeExtensionNode,
441
616
  ),
442
617
  ):
443
- class_definition = _get_class_definition(definition)
618
+ class_definition = _get_class_definition(definition, is_apollo_federation)
444
619
 
445
620
  object_types[definition.name.value] = class_definition
446
621
 
@@ -478,6 +653,11 @@ def codegen(schema: str) -> str:
478
653
  definitions.append(cst.EmptyLine())
479
654
  definitions.append(scalar_definition)
480
655
  definitions.append(cst.EmptyLine())
656
+ elif isinstance(definition, SchemaExtensionNode):
657
+ is_apollo_federation = any(
658
+ _is_federation_link_directive(directive)
659
+ for directive in definition.directives
660
+ )
481
661
  else:
482
662
  raise NotImplementedError(f"Unknown definition {definition}")
483
663
 
@@ -496,6 +676,7 @@ def codegen(schema: str) -> str:
496
676
  root_query_name=root_query_name,
497
677
  root_mutation_name=root_mutation_name,
498
678
  root_subscription_name=root_subscription_name,
679
+ is_apollo_federation=is_apollo_federation,
499
680
  )
500
681
 
501
682
  if schema_definition:
@@ -155,7 +155,7 @@ class ReservedType(NamedTuple):
155
155
  # Handle annotated arguments such as Private[str] and DirectiveValue[str]
156
156
  return type_has_annotation(other, self.type)
157
157
  else:
158
- # Handle both concrete and generic types (i.e Info, and Info)
158
+ # Handle both concrete and generic types (i.e Info, and Info[Any, Any])
159
159
  return (
160
160
  issubclass(origin, self.type)
161
161
  if isinstance(origin, type)
strawberry/types/info.py CHANGED
@@ -11,9 +11,9 @@ from typing import (
11
11
  List,
12
12
  Optional,
13
13
  Type,
14
+ TypeVar,
14
15
  Union,
15
16
  )
16
- from typing_extensions import TypeVar
17
17
 
18
18
  from .nodes import convert_selections
19
19
 
@@ -29,8 +29,8 @@ if TYPE_CHECKING:
29
29
 
30
30
  from .nodes import Selection
31
31
 
32
- ContextType = TypeVar("ContextType", default=Any)
33
- RootValueType = TypeVar("RootValueType", default=Any)
32
+ ContextType = TypeVar("ContextType")
33
+ RootValueType = TypeVar("RootValueType")
34
34
 
35
35
 
36
36
  @dataclasses.dataclass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strawberry-graphql
3
- Version: 0.221.0
3
+ Version: 0.221.0.dev1710955937
4
4
  Summary: A library for creating GraphQL APIs
5
5
  Home-page: https://strawberry.rocks/
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- strawberry/__init__.py,sha256=NP_R8Benm6W-v3Qqj3MSPm_CMickSCJIMaW2sZZ5oGs,1104
1
+ strawberry/__init__.py,sha256=rq7MRtYXzCOd792dYdgZqS6Ejv7UiX7U2hqFfQhLkGk,1063
2
2
  strawberry/__main__.py,sha256=3U77Eu21mJ-LY27RG-JEnpbh6Z63wGOom4i-EoLtUcY,59
3
3
  strawberry/aiohttp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  strawberry/aiohttp/handlers/__init__.py,sha256=7EeGIIwrgJYHAS9XJnZLfRGL_GHrligKm39rrVt7qDA,241
@@ -139,7 +139,7 @@ strawberry/federation/types.py,sha256=mM70g1aLgjplOc3SdtuJjy7NAKFLv35Z4BDC4s_j5u
139
139
  strawberry/federation/union.py,sha256=QXeh-nhApqFtXa3To9MX_IwvtwErZBhWYnssUK7JB2E,1005
140
140
  strawberry/field.py,sha256=2LvlnoQyRQHYcZV2Yy1E-2DYKXpOROUa8jyWgxG2QsI,18883
141
141
  strawberry/field_extensions/__init__.py,sha256=0z6RG9jEO7jpAuyEaQhRI5A_30rdcvsBM0qMhLs8y2s,96
142
- strawberry/field_extensions/input_mutation.py,sha256=a2zK97Fc5biqBt2ZlUPEPTQgMTRx6sJCSd3hSsjLSxI,2650
142
+ strawberry/field_extensions/input_mutation.py,sha256=G1nWnIdPS877MZXnDzGU4VvlRHT56KDpK-YBDOn6rDI,2660
143
143
  strawberry/file_uploads/__init__.py,sha256=v2-6FGBqnTnMPSUTFOiXpIutDMl-ga0PFtw5tKlcagk,50
144
144
  strawberry/file_uploads/scalars.py,sha256=jccJh-h7t4naRiDzrszGoYsp95zdF30c7y5Ht0Zrk_s,131
145
145
  strawberry/file_uploads/utils.py,sha256=U8gk1aHjWOBa_Opyvc47h6OpYToGq1QiSnEkI2kphdo,1112
@@ -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=bPWBUwYPGYRtLwLu1QtDWa0Knt0IgF0FcZnePvDkveI,20837
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
@@ -220,9 +220,9 @@ strawberry/type.py,sha256=KDztUFxVju4CEPPRzZYcyHtDNRrkFYxK-mI99GcqCgU,6511
220
220
  strawberry/types/__init__.py,sha256=APb1Cjy6bxqFxIfDfempP6eb9NE3LYDwQ3gX7r07lXI,139
221
221
  strawberry/types/execution.py,sha256=qCqDMJgdAFwdc3uPzVTAKsZlu6fgxPLrFwkoX_PFBq8,2775
222
222
  strawberry/types/fields/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
223
- strawberry/types/fields/resolver.py,sha256=Lwy2XVKnTbDyibk9pPsWsunlm22akXjy-gKCg22Tp1A,14130
223
+ strawberry/types/fields/resolver.py,sha256=7-fzFfQdf0uWuYyWiSIsGbt2qvpLpGPy6w1T_57cYL8,14140
224
224
  strawberry/types/graphql.py,sha256=3SWZEsa0Zy1eVW6vy75BnB7t9_lJVi6TBV3_1j3RNBs,687
225
- strawberry/types/info.py,sha256=kCsXvsTxrHubS936eOOxp2PNLWNqLRCPDckpVDspEk4,2827
225
+ strawberry/types/info.py,sha256=AwYUVQgCiTGFU9x97ntF3v6W6qnknOBvuYnERdfIe4w,2776
226
226
  strawberry/types/nodes.py,sha256=2ZVa1HOFgHZ96QTfz3QEr1fufi6Sz9hAzcZxsN7KtGE,5026
227
227
  strawberry/types/type_resolver.py,sha256=F0z_geS4VEun8EhD571LaTgI8ypjCeLfp910gF0Q3MY,6280
228
228
  strawberry/types/types.py,sha256=t5MOV4xiutPL2Ka02u3Z2V3eqy2R2JPYQxsqCnY2Aqk,7139
@@ -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.221.0.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
245
- strawberry_graphql-0.221.0.dist-info/METADATA,sha256=0KsXiYNdNh45hcDBa7AElQz56ODfRQiMeP8FNjXBaF4,7740
246
- strawberry_graphql-0.221.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
247
- strawberry_graphql-0.221.0.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
248
- strawberry_graphql-0.221.0.dist-info/RECORD,,
244
+ strawberry_graphql-0.221.0.dev1710955937.dist-info/LICENSE,sha256=m-XnIVUKqlG_AWnfi9NReh9JfKhYOB-gJfKE45WM1W8,1072
245
+ strawberry_graphql-0.221.0.dev1710955937.dist-info/METADATA,sha256=l7sfrGtQwOGjYGDh5RVMKFg7bL4dvPYKnTzz6xo-eFU,7754
246
+ strawberry_graphql-0.221.0.dev1710955937.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
247
+ strawberry_graphql-0.221.0.dev1710955937.dist-info/entry_points.txt,sha256=Nk7-aT3_uEwCgyqtHESV9H6Mc31cK-VAvhnQNTzTb4k,49
248
+ strawberry_graphql-0.221.0.dev1710955937.dist-info/RECORD,,