From c738d3db28f0d3bb23f6d2fb7ce7ad5f9782dc6a Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 10 May 2016 00:40:35 -0700 Subject: [PATCH] Added support for graphql 0.5.0 --- .travis.yml | 2 ++ graphene/__init__.py | 3 ++- graphene/contrib/django/debug/plugin.py | 4 ++-- .../contrib/django/debug/tests/test_query.py | 1 + graphene/contrib/django/filter/filterset.py | 6 +++--- graphene/contrib/django/forms.py | 6 +++--- graphene/contrib/django/views.py | 3 --- .../core/classtypes/tests/test_uniontype.py | 2 +- graphene/core/schema.py | 20 +++++++++++++------ graphene/core/tests/test_mutations.py | 2 +- graphene/core/tests/test_old_fields.py | 2 +- graphene/core/tests/test_query.py | 4 ++-- graphene/core/tests/test_schema.py | 12 +++++++---- graphene/core/types/argument.py | 4 ++-- graphene/core/types/field.py | 8 +++++--- graphene/core/types/tests/test_argument.py | 4 ++-- graphene/core/types/tests/test_field.py | 8 ++++---- graphene/relay/fields.py | 12 +++++------ graphene/relay/tests/test_mutations.py | 2 +- graphene/relay/tests/test_query.py | 11 +++++----- graphene/relay/types.py | 1 + graphene/utils/__init__.py | 4 +++- graphene/utils/wrap_resolver_function.py | 17 ++++++++++++++++ 23 files changed, 87 insertions(+), 51 deletions(-) create mode 100644 graphene/utils/wrap_resolver_function.py diff --git a/.travis.yml b/.travis.yml index c935ec0d..6a368387 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,8 @@ install: if [ "$TEST_TYPE" = build ]; then pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter sqlalchemy_utils pip install --download-cache $HOME/.cache/pip psycopg2 > /dev/null 2>&1 + pip install https://github.com/graphql-python/graphql-core/archive/master.zip + pip install https://github.com/graphql-python/graphql-relay-py/archive/master.zip pip install --download-cache $HOME/.cache/pip/ -e .[django] pip install --download-cache $HOME/.cache/pip/ -e .[sqlalchemy] pip install django==$DJANGO_VERSION diff --git a/graphene/__init__.py b/graphene/__init__.py index 5eb2b5dd..f4683c40 100644 --- a/graphene/__init__.py +++ b/graphene/__init__.py @@ -33,7 +33,8 @@ from graphene.core.fields import ( ) from graphene.utils import ( - resolve_only_args + resolve_only_args, + with_context ) __all__ = [ diff --git a/graphene/contrib/django/debug/plugin.py b/graphene/contrib/django/debug/plugin.py index 70cd6741..2d930673 100644 --- a/graphene/contrib/django/debug/plugin.py +++ b/graphene/contrib/django/debug/plugin.py @@ -72,8 +72,8 @@ class DjangoDebugPlugin(Plugin): @contextmanager def context_execution(self, executor): - executor['root'] = WrappedRoot(root=executor['root']) + executor['root_value'] = WrappedRoot(root=executor.get('root_value')) executor['schema'] = self.wrap_schema(executor['schema']) - self.enable_instrumentation(executor['root']) + self.enable_instrumentation(executor['root_value']) yield executor self.disable_instrumentation() diff --git a/graphene/contrib/django/debug/tests/test_query.py b/graphene/contrib/django/debug/tests/test_query.py index c512d6ad..ba7c07c7 100644 --- a/graphene/contrib/django/debug/tests/test_query.py +++ b/graphene/contrib/django/debug/tests/test_query.py @@ -149,6 +149,7 @@ def test_should_query_connection(): schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()]) result = schema.execute(query) assert not result.errors + print result.data['__debug']['sql'] assert result.data['allReporters'] == expected['allReporters'] assert 'COUNT' in result.data['__debug']['sql'][0]['rawSql'] query = str(Reporter.objects.all()[:1].query) diff --git a/graphene/contrib/django/filter/filterset.py b/graphene/contrib/django/filter/filterset.py index 70f776be..482dc8f7 100644 --- a/graphene/contrib/django/filter/filterset.py +++ b/graphene/contrib/django/filter/filterset.py @@ -14,15 +14,15 @@ class GlobalIDFilter(Filter): field_class = GlobalIDFormField def filter(self, qs, value): - gid = from_global_id(value) - return super(GlobalIDFilter, self).filter(qs, gid.id) + _type, _id = from_global_id(value) + return super(GlobalIDFilter, self).filter(qs, _id) class GlobalIDMultipleChoiceFilter(MultipleChoiceFilter): field_class = GlobalIDMultipleChoiceField def filter(self, qs, value): - gids = [from_global_id(v).id for v in value] + gids = [from_global_id(v)[1] for v in value] return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids) diff --git a/graphene/contrib/django/forms.py b/graphene/contrib/django/forms.py index d8062e39..591236cb 100644 --- a/graphene/contrib/django/forms.py +++ b/graphene/contrib/django/forms.py @@ -16,13 +16,13 @@ class GlobalIDFormField(Field): return None try: - gid = from_global_id(value) + _type, _id = from_global_id(value) except (TypeError, ValueError, UnicodeDecodeError, binascii.Error): raise ValidationError(self.error_messages['invalid']) try: - IntegerField().clean(gid.id) - CharField().clean(gid.type) + IntegerField().clean(_id) + CharField().clean(_type) except ValidationError: raise ValidationError(self.error_messages['invalid']) diff --git a/graphene/contrib/django/views.py b/graphene/contrib/django/views.py index 9a4bd96e..81f3ceba 100644 --- a/graphene/contrib/django/views.py +++ b/graphene/contrib/django/views.py @@ -11,6 +11,3 @@ class GraphQLView(BaseGraphQLView): executor=schema.executor, **kwargs ) - - def execute(self, *args, **kwargs): - return self.graphene_schema.execute(*args, **kwargs) diff --git a/graphene/core/classtypes/tests/test_uniontype.py b/graphene/core/classtypes/tests/test_uniontype.py index 0808ee53..0b456776 100644 --- a/graphene/core/classtypes/tests/test_uniontype.py +++ b/graphene/core/classtypes/tests/test_uniontype.py @@ -25,4 +25,4 @@ def test_uniontype(): assert isinstance(object_type, GraphQLUnionType) assert Thing._meta.type_name == 'Thing' assert object_type.description == 'Thing union description' - assert object_type.get_possible_types() == [schema.T(Human), schema.T(Pet)] + assert object_type.get_types() == [schema.T(Human), schema.T(Pet)] diff --git a/graphene/core/schema.py b/graphene/core/schema.py index d2ce801b..966e1fd2 100644 --- a/graphene/core/schema.py +++ b/graphene/core/schema.py @@ -1,7 +1,6 @@ import inspect -from collections import OrderedDict -from graphql.execution.executors.sync import SyncExecutor +from graphql import graphql from graphql.type import GraphQLSchema as _GraphQLSchema from graphql.utils.introspection_query import introspection_query from graphql.utils.schema_printer import print_schema @@ -116,10 +115,19 @@ class Schema(object): def types(self): return self._types_names - def execute(self, request='', root=None, args=None, **kwargs): - kwargs = dict(kwargs, request=request, root=root, args=args, schema=self.schema) + def execute(self, request_string='', root_value=None, variable_values=None, + context_value=None, operation_name=None, executor=None): + kwargs = dict( + schema=self.schema, + request_string=request_string, + root_value=root_value, + context_value=context_value, + variable_values=variable_values, + operation_name=operation_name, + executor=executor or self._executor + ) with self.plugins.context_execution(**kwargs) as execute_kwargs: - return self.executor.execute(**execute_kwargs) + return graphql(**execute_kwargs) def introspect(self): - return self.execute(introspection_query).data + return graphql(self.schema, introspection_query).data diff --git a/graphene/core/tests/test_mutations.py b/graphene/core/tests/test_mutations.py index c5a26daa..428340a6 100644 --- a/graphene/core/tests/test_mutations.py +++ b/graphene/core/tests/test_mutations.py @@ -58,6 +58,6 @@ def test_execute_mutations(): 'result': '5', } } - result = schema.execute(query, root=object()) + result = schema.execute(query, root_value=object()) assert not result.errors assert result.data == expected diff --git a/graphene/core/tests/test_old_fields.py b/graphene/core/tests/test_old_fields.py index 6614e929..86ca805d 100644 --- a/graphene/core/tests/test_old_fields.py +++ b/graphene/core/tests/test_old_fields.py @@ -94,7 +94,7 @@ def test_field_resolve(): f = StringField(required=True, resolve=lambda *args: 'RESOLVED').as_field() f.contribute_to_class(MyOt, 'field_name') field_type = schema.T(f) - assert 'RESOLVED' == field_type.resolver(MyOt, None, None) + assert 'RESOLVED' == field_type.resolver(MyOt, None, None, None) def test_field_resolve_type_custom(): diff --git a/graphene/core/tests/test_query.py b/graphene/core/tests/test_query.py index 23c5b180..57ab91c2 100644 --- a/graphene/core/tests/test_query.py +++ b/graphene/core/tests/test_query.py @@ -40,7 +40,7 @@ Human_type = schema.T(Human) def test_type(): assert Human._meta.fields_map['name'].resolver( - Human(object()), {}, None) == 'Peter' + Human(object()), {}, None, None) == 'Peter' def test_query(): @@ -59,6 +59,6 @@ def test_query(): 'type': 'Dog' } } - result = graphql(schema, query, root=Human(object())) + result = graphql(schema, query, root_value=Human(object())) assert not result.errors assert result.data == expected diff --git a/graphene/core/tests/test_schema.py b/graphene/core/tests/test_schema.py index 7766c3ed..a7e4c2e1 100644 --- a/graphene/core/tests/test_schema.py +++ b/graphene/core/tests/test_schema.py @@ -63,7 +63,7 @@ def test_query_schema_graphql(): 'type': 'Dog' } } - result = graphql(schema.schema, query, root=Human(object())) + result = graphql(schema.schema, query, root_value=Human(object())) assert not result.errors assert result.data == expected @@ -84,7 +84,7 @@ def test_query_schema_execute(): 'type': 'Dog' } } - result = schema.execute(query, root=object()) + result = schema.execute(query, root_value=object()) assert not result.errors assert result.data == expected @@ -93,8 +93,8 @@ def test_schema_get_type_map(): assert_equal_lists( schema.schema.get_type_map().keys(), ['__Field', 'String', 'Pet', 'Character', '__InputValue', - '__Directive', '__TypeKind', '__Schema', '__Type', 'Human', - '__EnumValue', 'Boolean']) + '__Directive', '__DirectiveLocation', '__TypeKind', '__Schema', + '__Type', 'Human', '__EnumValue', 'Boolean']) def test_schema_no_query(): @@ -156,6 +156,10 @@ def test_lazytype(): def test_schema_str(): expected = """ +schema { + query: Human +} + interface Character { name: String } diff --git a/graphene/core/types/argument.py b/graphene/core/types/argument.py index e75c0471..80039ede 100644 --- a/graphene/core/types/argument.py +++ b/graphene/core/types/argument.py @@ -57,7 +57,7 @@ def to_arguments(*args, **kwargs): def snake_case_args(resolver): @wraps(resolver) - def wrapped_resolver(instance, args, info): - return resolver(instance, ProxySnakeDict(args), info) + def wrapped_resolver(instance, args, context, info): + return resolver(instance, ProxySnakeDict(args), context, info) return wrapped_resolver diff --git a/graphene/core/types/field.py b/graphene/core/types/field.py index 9b24e7e3..351182d1 100644 --- a/graphene/core/types/field.py +++ b/graphene/core/types/field.py @@ -5,6 +5,7 @@ import six from graphql.type import GraphQLField, GraphQLInputObjectField from ...utils import maybe_func +from ...utils.wrap_resolver_function import wrap_resolver_function from ..classtypes.base import FieldsClassType from ..classtypes.inputobjecttype import InputObjectType from ..classtypes.mutation import Mutation @@ -105,14 +106,15 @@ class Field(NamedType, OrderedType): assert len(arguments) == 0 arguments = type_objecttype.get_arguments() resolver = getattr(type_objecttype, 'mutate') + resolver = wrap_resolver_function(resolver) else: - my_resolver = resolver + my_resolver = wrap_resolver_function(resolver) @wraps(my_resolver) - def wrapped_func(instance, args, info): + def wrapped_func(instance, args, context, info): if not isinstance(instance, self.object_type): instance = self.object_type(_root=instance) - return my_resolver(instance, args, info) + return my_resolver(instance, args, context, info) resolver = wrapped_func assert type, 'Internal type for field %s is None' % str(self) diff --git a/graphene/core/types/tests/test_argument.py b/graphene/core/types/tests/test_argument.py index d16055d5..4482c11c 100644 --- a/graphene/core/types/tests/test_argument.py +++ b/graphene/core/types/tests/test_argument.py @@ -48,7 +48,7 @@ def test_to_arguments_wrong_type(): def test_snake_case_args(): - def resolver(instance, args, info): + def resolver(instance, args, context, info): return args['my_arg']['inner_arg'] r = snake_case_args(resolver) - assert r(None, {'myArg': {'innerArg': 3}}, None) == 3 + assert r(None, {'myArg': {'innerArg': 3}}, None, None) == 3 diff --git a/graphene/core/types/tests/test_field.py b/graphene/core/types/tests/test_field.py index 6cdeeec9..e2ef15e9 100644 --- a/graphene/core/types/tests/test_field.py +++ b/graphene/core/types/tests/test_field.py @@ -25,7 +25,7 @@ def test_field_internal_type(): assert field.attname == 'my_field' assert isinstance(type, GraphQLField) assert type.description == 'My argument' - assert type.resolver(None, {}, None) == 'RESOLVED' + assert type.resolver(None, {}, None, None) == 'RESOLVED' assert type.type == GraphQLString @@ -44,7 +44,7 @@ def test_field_objectype_resolver(): type = schema.T(field) assert isinstance(type, GraphQLField) assert type.description == 'Custom description' - assert type.resolver(Query(), {}, None) == 'RESOLVED' + assert type.resolver(Query(), {}, None, None) == 'RESOLVED' def test_field_custom_name(): @@ -143,7 +143,7 @@ def test_field_resolve_argument(): schema = Schema(query=Query) type = schema.T(field) - assert type.resolver(None, {'firstName': 'Peter'}, None) == 'Peter' + assert type.resolver(None, {'firstName': 'Peter'}, None, None) == 'Peter' def test_field_resolve_vars(): @@ -160,7 +160,7 @@ def test_field_resolve_vars(): { hello(firstName:$firstName) } - """, args={"firstName": "Serkan"}) + """, variable_values={"firstName": "Serkan"}) expected = { 'hello': 'Hello Serkan' diff --git a/graphene/relay/fields.py b/graphene/relay/fields.py index aac1f7df..e01a41a3 100644 --- a/graphene/relay/fields.py +++ b/graphene/relay/fields.py @@ -4,7 +4,7 @@ from graphql_relay.node.node import from_global_id from ..core.fields import Field from ..core.types.definitions import NonNull from ..core.types.scalars import ID, Int, String - +from ..utils import with_context class ConnectionField(Field): @@ -65,14 +65,13 @@ class NodeField(Field): object_type or Node, id=id, *args, **kwargs) self.field_object_type = object_type - def id_fetcher(self, global_id, info): + def id_fetcher(self, global_id, context, info): from graphene.relay.utils import is_node schema = info.schema.graphene_schema try: - resolved_global_id = from_global_id(global_id) + _type, _id = from_global_id(global_id) except: return None - _type, _id = resolved_global_id.type, resolved_global_id.id object_type = schema.get_type(_type) if isinstance(self.field_object_type, six.string_types): field_object_type = schema.get_type(self.field_object_type) @@ -83,9 +82,10 @@ class NodeField(Field): return object_type.get_node(_id, info) - def resolver(self, instance, args, info): + @with_context + def resolver(self, instance, args, context, info): global_id = args.get('id') - return self.id_fetcher(global_id, info) + return self.id_fetcher(global_id, context, info) class GlobalIDField(Field): diff --git a/graphene/relay/tests/test_mutations.py b/graphene/relay/tests/test_mutations.py index 8176ba6c..d569bd7e 100644 --- a/graphene/relay/tests/test_mutations.py +++ b/graphene/relay/tests/test_mutations.py @@ -76,6 +76,6 @@ def test_execute_mutations(): 'result': '5', } } - result = schema.execute(query, root=object()) + result = schema.execute(query, root_value=object()) assert not result.errors assert result.data == expected diff --git a/graphene/relay/tests/test_query.py b/graphene/relay/tests/test_query.py index 4ec98eeb..ee370b0a 100644 --- a/graphene/relay/tests/test_query.py +++ b/graphene/relay/tests/test_query.py @@ -2,7 +2,7 @@ import pytest from graphql.type import GraphQLID, GraphQLNonNull import graphene -from graphene import relay +from graphene import relay, with_context schema = graphene.Schema() @@ -16,7 +16,7 @@ class MyNode(relay.Node): name = graphene.String() @classmethod - def get_node(cls, id, info): + def get_node(cls, id, context, info): return MyNode(id=id, name='mo') @@ -24,8 +24,9 @@ class SpecialNode(relay.Node): value = graphene.String() @classmethod - def get_node(cls, id, info): - value = "!!!" if info.request_context.get('is_special') else "???" + @with_context + def get_node(cls, id, context, info): + value = "!!!" if context.get('is_special') else "???" return SpecialNode(id=id, value=value) @@ -108,7 +109,7 @@ def test_get_node_info(specialness, value): 'value': value, }, } - result = schema.execute(query, request_context={'is_special': specialness}) + result = schema.execute(query, context_value={'is_special': specialness}) assert not result.errors assert result.data == expected diff --git a/graphene/relay/types.py b/graphene/relay/types.py index d539bce7..77e5aaf2 100644 --- a/graphene/relay/types.py +++ b/graphene/relay/types.py @@ -14,6 +14,7 @@ from ..core.types import Boolean, Field, List, String from ..core.types.argument import ArgumentsGroup from ..core.types.definitions import NonNull from ..utils import memoize +from ..utils.wrap_resolver_function import wrap_resolver_function from .fields import GlobalIDField diff --git a/graphene/utils/__init__.py b/graphene/utils/__init__.py index 34e4ad9a..2ed0f036 100644 --- a/graphene/utils/__init__.py +++ b/graphene/utils/__init__.py @@ -5,8 +5,10 @@ from .maybe_func import maybe_func from .misc import enum_to_graphql_enum from .resolve_only_args import resolve_only_args from .lazylist import LazyList +from .wrap_resolver_function import with_context, wrap_resolver_function __all__ = ['to_camel_case', 'to_snake_case', 'to_const', 'ProxySnakeDict', 'cached_property', 'memoize', 'maybe_func', 'enum_to_graphql_enum', - 'resolve_only_args', 'LazyList'] + 'resolve_only_args', 'LazyList', 'with_context', + 'wrap_resolver_function'] diff --git a/graphene/utils/wrap_resolver_function.py b/graphene/utils/wrap_resolver_function.py new file mode 100644 index 00000000..d57d83e9 --- /dev/null +++ b/graphene/utils/wrap_resolver_function.py @@ -0,0 +1,17 @@ +from functools import wraps + + +def with_context(func): + setattr(func, 'with_context', 'context') + return func + + +def wrap_resolver_function(func): + @wraps(func) + def inner(self, args, context, info): + with_context = getattr(func, 'with_context', None) + if with_context: + return func(self, args, context, info) + # For old compatibility + return func(self, args, info) + return inner