Skip to content

Commit 060315c

Browse files
committed
Change resolver function signature (fix #18) and remove camelCase -> snake_case conversion
1 parent c6d2021 commit 060315c

File tree

3 files changed

+91
-53
lines changed

3 files changed

+91
-53
lines changed

graphql/core/execution/__init__.py

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -239,64 +239,96 @@ def get_field_entry_key(node):
239239
return node.name.value
240240

241241

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+
242271
def resolve_field(ctx, parent_type, source, field_asts):
243272
"""A wrapper function for resolving the field, that catches the error
244273
and adds it to the context's global if the error is not rethrowable."""
245274
field_ast = field_asts[0]
275+
field_name = field_ast.name.value
246276

247-
field_def = get_field_def(ctx.schema, parent_type, field_ast)
277+
field_def = get_field_def(ctx.schema, parent_type, field_name)
248278
if not field_def:
249279
return Undefined
250280

251-
field_type = field_def.type
281+
return_type = field_def.type
252282
resolve_fn = field_def.resolver or default_resolve_fn
253283

254284
# Build a dict of arguments from the field.arguments AST, using the variables scope to fulfill any variable references.
255285
# 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+
)
262299

263300
# If an error occurs while calling the field `resolve` function, ensure that it is wrapped as a GraphQLError with locations.
264301
# Log this error and return null if allowed, otherwise throw the error so the parent field can handle it.
265302
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)
272304
except Exception as e:
273305
reported_error = GraphQLError(str(e), [field_ast], e)
274-
if isinstance(field_type, GraphQLNonNull):
306+
if isinstance(return_type, GraphQLNonNull):
275307
raise reported_error
276308
ctx.errors.append(reported_error)
277309
return None
278310

279311
return complete_value_catching_error(
280-
ctx, field_type, field_asts, result
312+
ctx, return_type, field_asts, info, result
281313
)
282314

283315

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):
285317
# If the field type is non-nullable, then it is resolved without any
286318
# 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)
289321

290322
# Otherwise, error protection is applied, logging the error and
291323
# resolving a null value for this field if one is encountered.
292324
try:
293-
return complete_value(ctx, field_type, field_asts, result)
325+
return complete_value(ctx, return_type, field_asts, info, result)
294326
except Exception as e:
295327
ctx.errors.append(e)
296328
return None
297329

298330

299-
def complete_value(ctx, field_type, field_asts, result):
331+
def complete_value(ctx, return_type, field_asts, info, result):
300332
"""Implements the instructions for completeValue as defined in the
301333
"Field entries" section of the spec.
302334
@@ -310,9 +342,9 @@ def complete_value(ctx, field_type, field_asts, result):
310342
311343
Otherwise, the field type expects a sub-selection set, and will complete the value by evaluating all sub-selections."""
312344
# 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):
314346
completed = complete_value(
315-
ctx, field_type.of_type, field_asts, result
347+
ctx, return_type.of_type, field_asts, info, result
316348
)
317349
if completed is None:
318350
raise GraphQLError(
@@ -326,27 +358,27 @@ def complete_value(ctx, field_type, field_asts, result):
326358
return None
327359

328360
# 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):
330362
assert isinstance(result, collections.Iterable), \
331363
'User Error: expected iterable, but did not find one.'
332364

333-
item_type = field_type.of_type
365+
item_type = return_type.of_type
334366
return [complete_value_catching_error(
335-
ctx, item_type, field_asts, item
367+
ctx, item_type, field_asts, info, item
336368
) for item in result]
337369

338370
# 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)
341373
if is_nullish(coerced_result):
342374
return None
343375
return coerced_result
344376

345377
# 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)
350382
else:
351383
object_type = None
352384

@@ -373,31 +405,28 @@ def camel_to_snake_case(name):
373405
return CAMEL_CASE_PATTERN.sub(lambda m: m.group(1) + '_' + m.group(2).lower(), name)
374406

375407

376-
def default_resolve_fn(source, args, root, field_ast, *_):
408+
def default_resolve_fn(source, args, info):
377409
"""If a resolve function is not given, then a default resolve behavior is used which takes the property of the source object
378410
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
380412
property = getattr(source, name, None)
381-
if property is None:
382-
property = getattr(source, camel_to_snake_case(name), None)
383413
if callable(property):
384414
return property()
385415
return property
386416

387417

388-
def get_field_def(schema, parent_type, field_ast):
418+
def get_field_def(schema, parent_type, field_name):
389419
"""This method looks up the field on the given type defintion.
390420
It has special casing for the two introspection fields, __schema
391421
and __typename. __typename is special because it can always be
392422
queried as a field, even in situations where no other fields
393423
are allowed, like on a Union. __schema could get automatically
394424
added to the query type, but that would require mutating type
395425
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:
398427
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:
400429
return TypeMetaFieldDef
401-
elif name == TypeNameMetaFieldDef.name:
430+
elif field_name == TypeNameMetaFieldDef.name:
402431
return TypeNameMetaFieldDef
403-
return parent_type.get_fields().get(name)
432+
return parent_type.get_fields().get(field_name)

graphql/core/type/introspection.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,18 @@
5151
type=GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))),
5252
resolver=lambda directive, *args: directive.args or [],
5353
),
54-
'onOperation': GraphQLField(GraphQLBoolean),
55-
'onFragment': GraphQLField(GraphQLBoolean),
56-
'onField': GraphQLField(GraphQLBoolean),
54+
'onOperation': GraphQLField(
55+
type=GraphQLBoolean,
56+
resolver=lambda directive, *args: directive.on_operation,
57+
),
58+
'onFragment': GraphQLField(
59+
type=GraphQLBoolean,
60+
resolver=lambda directive, *args: directive.on_fragment,
61+
),
62+
'onField': GraphQLField(
63+
type=GraphQLBoolean,
64+
resolver=lambda directive, *args: directive.on_field,
65+
),
5766
})
5867

5968

@@ -188,7 +197,10 @@ def input_fields(type, *_):
188197
type=GraphQLNonNull(GraphQLBoolean),
189198
resolver=lambda field, *_: bool(field.deprecation_reason)
190199
),
191-
'deprecationReason': GraphQLField(GraphQLString)
200+
'deprecationReason': GraphQLField(
201+
type=GraphQLString,
202+
resolver=lambda enum_value, *_: enum_value.deprecation_reason,
203+
)
192204
})
193205

194206

@@ -252,7 +264,7 @@ class TypeKind(object):
252264
SchemaMetaFieldDef = GraphQLField(
253265
type=GraphQLNonNull(__Schema),
254266
description='Access the current type schema of this server.',
255-
resolver=lambda source, args, root, field_ast, field_type, parent_type, schema: schema
267+
resolver=lambda source, args, info: info.schema
256268
)
257269
SchemaMetaFieldDef.name = '__schema'
258270

@@ -262,15 +274,12 @@ class TypeKind(object):
262274
args={
263275
'name': GraphQLArgument(GraphQLNonNull(GraphQLString))
264276
},
265-
resolver=lambda source, args, root, field_ast, field_type, parent_type, schema: (
266-
schema.get_type(args['name'])
267-
)
277+
resolver=lambda source, args, info: info.schema.get_type(args['name'])
268278
)
269279
TypeMetaFieldDef.name = '__type'
270280

271281
TypeNameMetaFieldDef = GraphQLField(
272282
GraphQLNonNull(GraphQLString),
273-
resolver=lambda source, args, root, field_ast, field_type, parent_type, *_:
274-
parent_type.name
283+
resolver=lambda source, args, info: info.parent_type.name
275284
)
276285
TypeNameMetaFieldDef.name = '__typename'

tests/core_type/test_introspection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ def test_introspects_on_input_object():
525525
'field': GraphQLField(
526526
type=GraphQLString,
527527
args={'complex': GraphQLArgument(TestInputObject)},
528-
resolver=lambda obj, args, *_: json.dumps(args.get('complex'))
528+
resolver=lambda obj, args, info: json.dumps(args.get('complex'))
529529
)
530530
})
531531
schema = GraphQLSchema(TestType)

0 commit comments

Comments
 (0)