@@ -239,64 +239,96 @@ def get_field_entry_key(node):
239
239
return node .name .value
240
240
241
241
242
+ class ResolveInfo (object ):
243
+ def __init__ (self , field_name , field_asts , return_type , parent_type , context ):
244
+ self .field_name = field_name
245
+ self .field_ast = field_asts
246
+ self .return_type = return_type
247
+ self .parent_type = parent_type
248
+ self .context = context
249
+
250
+ @property
251
+ def schema (self ):
252
+ return self .context .schema
253
+
254
+ @property
255
+ def fragments (self ):
256
+ return self .context .fragments
257
+
258
+ @property
259
+ def root_value (self ):
260
+ return self .context .root_value
261
+
262
+ @property
263
+ def operation (self ):
264
+ return self .context .operation
265
+
266
+ @property
267
+ def variable_values (self ):
268
+ return self .context .variables
269
+
270
+
242
271
def resolve_field (ctx , parent_type , source , field_asts ):
243
272
"""A wrapper function for resolving the field, that catches the error
244
273
and adds it to the context's global if the error is not rethrowable."""
245
274
field_ast = field_asts [0 ]
275
+ field_name = field_ast .name .value
246
276
247
- field_def = get_field_def (ctx .schema , parent_type , field_ast )
277
+ field_def = get_field_def (ctx .schema , parent_type , field_name )
248
278
if not field_def :
249
279
return Undefined
250
280
251
- field_type = field_def .type
281
+ return_type = field_def .type
252
282
resolve_fn = field_def .resolver or default_resolve_fn
253
283
254
284
# Build a dict of arguments from the field.arguments AST, using the variables scope to fulfill any variable references.
255
285
# TODO: find a way to memoize, in case this field is within a list type.
256
- if field_def .args is not None :
257
- args = get_argument_values (
258
- field_def .args , field_ast .arguments , ctx .variables
259
- )
260
- else :
261
- args = None
286
+ args = get_argument_values (
287
+ field_def .args , field_ast .arguments , ctx .variables
288
+ )
289
+
290
+ # The resolve function's optional third argument is a collection of
291
+ # information about the current execution state.
292
+ info = ResolveInfo (
293
+ field_name ,
294
+ field_asts ,
295
+ return_type ,
296
+ parent_type ,
297
+ ctx
298
+ )
262
299
263
300
# If an error occurs while calling the field `resolve` function, ensure that it is wrapped as a GraphQLError with locations.
264
301
# Log this error and return null if allowed, otherwise throw the error so the parent field can handle it.
265
302
try :
266
- result = resolve_fn (
267
- source , args , ctx .root ,
268
- # TODO: provide all fieldASTs, not just the first field
269
- field_ast ,
270
- field_type , parent_type , ctx .schema
271
- )
303
+ result = resolve_fn (source , args , info )
272
304
except Exception as e :
273
305
reported_error = GraphQLError (str (e ), [field_ast ], e )
274
- if isinstance (field_type , GraphQLNonNull ):
306
+ if isinstance (return_type , GraphQLNonNull ):
275
307
raise reported_error
276
308
ctx .errors .append (reported_error )
277
309
return None
278
310
279
311
return complete_value_catching_error (
280
- ctx , field_type , field_asts , result
312
+ ctx , return_type , field_asts , info , result
281
313
)
282
314
283
315
284
- def complete_value_catching_error (ctx , field_type , field_asts , result ):
316
+ def complete_value_catching_error (ctx , return_type , field_asts , info , result ):
285
317
# If the field type is non-nullable, then it is resolved without any
286
318
# protection from errors.
287
- if isinstance (field_type , GraphQLNonNull ):
288
- return complete_value (ctx , field_type , field_asts , result )
319
+ if isinstance (return_type , GraphQLNonNull ):
320
+ return complete_value (ctx , return_type , field_asts , info , result )
289
321
290
322
# Otherwise, error protection is applied, logging the error and
291
323
# resolving a null value for this field if one is encountered.
292
324
try :
293
- return complete_value (ctx , field_type , field_asts , result )
325
+ return complete_value (ctx , return_type , field_asts , info , result )
294
326
except Exception as e :
295
327
ctx .errors .append (e )
296
328
return None
297
329
298
330
299
- def complete_value (ctx , field_type , field_asts , result ):
331
+ def complete_value (ctx , return_type , field_asts , info , result ):
300
332
"""Implements the instructions for completeValue as defined in the
301
333
"Field entries" section of the spec.
302
334
@@ -310,9 +342,9 @@ def complete_value(ctx, field_type, field_asts, result):
310
342
311
343
Otherwise, the field type expects a sub-selection set, and will complete the value by evaluating all sub-selections."""
312
344
# If field type is NonNull, complete for inner type, and throw field error if result is null.
313
- if isinstance (field_type , GraphQLNonNull ):
345
+ if isinstance (return_type , GraphQLNonNull ):
314
346
completed = complete_value (
315
- ctx , field_type .of_type , field_asts , result
347
+ ctx , return_type .of_type , field_asts , info , result
316
348
)
317
349
if completed is None :
318
350
raise GraphQLError (
@@ -326,27 +358,27 @@ def complete_value(ctx, field_type, field_asts, result):
326
358
return None
327
359
328
360
# If field type is List, complete each item in the list with the inner type
329
- if isinstance (field_type , GraphQLList ):
361
+ if isinstance (return_type , GraphQLList ):
330
362
assert isinstance (result , collections .Iterable ), \
331
363
'User Error: expected iterable, but did not find one.'
332
364
333
- item_type = field_type .of_type
365
+ item_type = return_type .of_type
334
366
return [complete_value_catching_error (
335
- ctx , item_type , field_asts , item
367
+ ctx , item_type , field_asts , info , item
336
368
) for item in result ]
337
369
338
370
# If field type is Scalar or Enum, coerce to a valid value, returning null if coercion is not possible.
339
- if isinstance (field_type , (GraphQLScalarType , GraphQLEnumType )):
340
- coerced_result = field_type .coerce (result )
371
+ if isinstance (return_type , (GraphQLScalarType , GraphQLEnumType )):
372
+ coerced_result = return_type .coerce (result )
341
373
if is_nullish (coerced_result ):
342
374
return None
343
375
return coerced_result
344
376
345
377
# Field type must be Object, Interface or Union and expect sub-selections.
346
- if isinstance (field_type , GraphQLObjectType ):
347
- object_type = field_type
348
- elif isinstance (field_type , (GraphQLInterfaceType , GraphQLUnionType )):
349
- object_type = field_type .resolve_type (result )
378
+ if isinstance (return_type , GraphQLObjectType ):
379
+ object_type = return_type
380
+ elif isinstance (return_type , (GraphQLInterfaceType , GraphQLUnionType )):
381
+ object_type = return_type .resolve_type (result )
350
382
else :
351
383
object_type = None
352
384
@@ -373,31 +405,28 @@ def camel_to_snake_case(name):
373
405
return CAMEL_CASE_PATTERN .sub (lambda m : m .group (1 ) + '_' + m .group (2 ).lower (), name )
374
406
375
407
376
- def default_resolve_fn (source , args , root , field_ast , * _ ):
408
+ def default_resolve_fn (source , args , info ):
377
409
"""If a resolve function is not given, then a default resolve behavior is used which takes the property of the source object
378
410
of the same name as the field and returns it as the result, or if it's a function, returns the result of calling that function."""
379
- name = field_ast . name . value
411
+ name = info . field_name
380
412
property = getattr (source , name , None )
381
- if property is None :
382
- property = getattr (source , camel_to_snake_case (name ), None )
383
413
if callable (property ):
384
414
return property ()
385
415
return property
386
416
387
417
388
- def get_field_def (schema , parent_type , field_ast ):
418
+ def get_field_def (schema , parent_type , field_name ):
389
419
"""This method looks up the field on the given type defintion.
390
420
It has special casing for the two introspection fields, __schema
391
421
and __typename. __typename is special because it can always be
392
422
queried as a field, even in situations where no other fields
393
423
are allowed, like on a Union. __schema could get automatically
394
424
added to the query type, but that would require mutating type
395
425
definitions, which would cause issues."""
396
- name = field_ast .name .value
397
- if name == SchemaMetaFieldDef .name and schema .get_query_type () == parent_type :
426
+ if field_name == SchemaMetaFieldDef .name and schema .get_query_type () == parent_type :
398
427
return SchemaMetaFieldDef
399
- elif name == TypeMetaFieldDef .name and schema .get_query_type () == parent_type :
428
+ elif field_name == TypeMetaFieldDef .name and schema .get_query_type () == parent_type :
400
429
return TypeMetaFieldDef
401
- elif name == TypeNameMetaFieldDef .name :
430
+ elif field_name == TypeNameMetaFieldDef .name :
402
431
return TypeNameMetaFieldDef
403
- return parent_type .get_fields ().get (name )
432
+ return parent_type .get_fields ().get (field_name )
0 commit comments