diff --git a/graphene/types/argument.py b/graphene/types/argument.py index a3febbb2..072b83a9 100644 --- a/graphene/types/argument.py +++ b/graphene/types/argument.py @@ -2,7 +2,7 @@ import inspect from collections import OrderedDict from itertools import chain -from graphql import GraphQLArgument +from graphql.type.definition import GraphQLArgument, GraphQLArgumentDefinition from graphql.utils.assert_valid_name import assert_valid_name from ..utils.orderedtype import OrderedType @@ -40,11 +40,15 @@ class Argument(GraphQLArgument, OrderedType): @classmethod def copy_from(cls, argument): + if isinstance (argument, (GraphQLArgumentDefinition, Argument)): + name = argument.name + else: + name = None return cls( type=argument.type, default_value=argument.default_value, description=argument.description, - name=argument.name, + name=name, _creation_counter=argument.creation_counter if isinstance(argument, Argument) else None, ) diff --git a/graphene/types/field.py b/graphene/types/field.py index b9aa285a..f5316896 100644 --- a/graphene/types/field.py +++ b/graphene/types/field.py @@ -1,6 +1,7 @@ +from collections import OrderedDict import inspect -from graphql.type import GraphQLField, GraphQLInputObjectField +from graphql.type import GraphQLField, GraphQLInputObjectField, GraphQLFieldDefinition from graphql.utils.assert_valid_name import assert_valid_name from ..utils.orderedtype import OrderedType @@ -126,18 +127,23 @@ class Field(AbstractField, GraphQLField, OrderedType): _creation_counter = field.creation_counter if _creation_counter is False else None attname = attname or field.attname parent = parent or field.parent + args = to_arguments(args, field.args) else: # If is a GraphQLField type = type or field.type resolver = resolver or field.resolver - name = field.name + field_args = field.args + if isinstance(field, GraphQLFieldDefinition): + name = name or field.name + field_args = OrderedDict((a.name, a) for a in field_args) + args = to_arguments(args, field_args) _creation_counter = None attname = attname or name parent = parent new_field = cls( type=type, - args=to_arguments(args, field.args), + args=args, resolver=resolver, source=source, deprecation_reason=field.deprecation_reason, diff --git a/graphene/types/inputobjecttype.py b/graphene/types/inputobjecttype.py index 465b6db0..f1326f3f 100644 --- a/graphene/types/inputobjecttype.py +++ b/graphene/types/inputobjecttype.py @@ -7,7 +7,7 @@ from ..utils.get_fields import get_fields from ..utils.is_base_type import is_base_type from .definitions import GrapheneGraphQLType from .field import InputField -from .interface import attrs_without_fields +from .objecttype import attrs_without_fields from .options import Options from .unmountedtype import UnmountedType diff --git a/graphene/types/interface.py b/graphene/types/interface.py index 37218d8c..d7b4127b 100644 --- a/graphene/types/interface.py +++ b/graphene/types/interface.py @@ -1,81 +1 @@ -import six - -from graphql import GraphQLInterfaceType - -from ..utils.copy_fields import copy_fields -from ..utils.get_fields import get_fields -from ..utils.is_base_type import is_base_type -from .definitions import GrapheneGraphQLType -from .field import Field -from .options import Options - - -class GrapheneInterfaceType(GrapheneGraphQLType, GraphQLInterfaceType): - pass - - -class InterfaceTypeMeta(type): - - @staticmethod - def _get_interface_options(meta): - return Options( - meta, - name=None, - description=None, - graphql_type=None, - abstract=False - ) - - def __new__(cls, name, bases, attrs): - super_new = super(InterfaceTypeMeta, cls).__new__ - - # Also ensure initialization is only performed for subclasses of Model - # (excluding Model class itself). - if not is_base_type(bases, InterfaceTypeMeta): - return super_new(cls, name, bases, attrs) - - options = cls._get_interface_options(attrs.pop('Meta', None)) - - fields = get_fields(Interface, attrs, bases) - attrs = attrs_without_fields(attrs, fields) - cls = super_new(cls, name, bases, dict(attrs, _meta=options)) - - if not options.graphql_type: - fields = copy_fields(Field, fields, parent=cls) - options.graphql_type = GrapheneInterfaceType( - graphene_type=cls, - name=options.name or cls.__name__, - resolve_type=cls.resolve_type, - description=options.description or cls.__doc__, - fields=fields, - ) - else: - assert not fields, "Can't mount Fields in an Interface with a defined graphql_type" - fields = copy_fields(options.graphql_type.get_fields(), parent=cls) - - for name, field in fields.items(): - setattr(cls, field.attname or name, field) - - return cls - - -def attrs_without_fields(attrs, fields): - return {k: v for k, v in attrs.items() if k not in fields} - - -class Interface(six.with_metaclass(InterfaceTypeMeta)): - resolve_type = None - - def __init__(self, *args, **kwargs): - from .objecttype import ObjectType - if not isinstance(self, ObjectType): - raise Exception("An interface cannot be intitialized") - super(Interface, self).__init__(*args, **kwargs) - - @classmethod - def implements(cls, object_type): - ''' - We use this function for customizing when a ObjectType have this class as Interface - For example, if we want to check that the ObjectType have some required things - in it like Node.get_node - ''' +from .objecttype import Interface, GrapheneInterfaceType diff --git a/graphene/types/objecttype.py b/graphene/types/objecttype.py index 6892936b..a5fdf169 100644 --- a/graphene/types/objecttype.py +++ b/graphene/types/objecttype.py @@ -1,18 +1,20 @@ import six -from graphql import GraphQLObjectType +from graphql import GraphQLObjectType, GraphQLInterfaceType from ..utils.copy_fields import copy_fields from ..utils.get_fields import get_fields from ..utils.is_base_type import is_base_type from .definitions import GrapheneGraphQLType from .field import Field -from .interface import (GrapheneInterfaceType, Interface, InterfaceTypeMeta, - attrs_without_fields) from .options import Options +class GrapheneInterfaceType(GrapheneGraphQLType, GraphQLInterfaceType): + pass + + class GrapheneObjectType(GrapheneGraphQLType, GraphQLObjectType): def __init__(self, *args, **kwargs): @@ -42,10 +44,13 @@ def is_objecttype(bases): return False +def attrs_without_fields(attrs, fields): + return {k: v for k, v in attrs.items() if k not in fields} + # We inherit from InterfaceTypeMeta instead of type for being able # to have ObjectTypes extending Interfaces using Python syntax, like: # class MyObjectType(ObjectType, MyInterface) -class ObjectTypeMeta(InterfaceTypeMeta): +class ObjectTypeMeta(type): def __new__(cls, name, bases, attrs): super_new = type.__new__ @@ -57,7 +62,7 @@ class ObjectTypeMeta(InterfaceTypeMeta): return super_new(cls, name, bases, attrs) if not is_objecttype(bases): - return super(ObjectTypeMeta, cls).__new__(cls, name, bases, attrs) + return cls._create_interface(cls, name, bases, attrs) options = Options( attrs.pop('Meta', None), @@ -99,6 +104,42 @@ class ObjectTypeMeta(InterfaceTypeMeta): def is_object_type(cls): return issubclass(cls, ObjectType) + @staticmethod + def _get_interface_options(meta): + return Options( + meta, + name=None, + description=None, + graphql_type=None, + abstract=False + ) + + @staticmethod + def _create_interface(cls, name, bases, attrs): + options = cls._get_interface_options(attrs.pop('Meta', None)) + + fields = get_fields(Interface, attrs, bases) + attrs = attrs_without_fields(attrs, fields) + cls = type.__new__(cls, name, bases, dict(attrs, _meta=options)) + + if not options.graphql_type: + fields = copy_fields(Field, fields, parent=cls) + options.graphql_type = GrapheneInterfaceType( + graphene_type=cls, + name=options.name or cls.__name__, + resolve_type=cls.resolve_type, + description=options.description or cls.__doc__, + fields=fields, + ) + else: + assert not fields, "Can't mount Fields in an Interface with a defined graphql_type" + fields = copy_fields(options.graphql_type.get_fields(), parent=cls) + + for name, field in fields.items(): + setattr(cls, field.attname or name, field) + + return cls + class ObjectType(six.with_metaclass(ObjectTypeMeta)): @@ -152,3 +193,21 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)): return graphql_type.name == cls._meta.graphql_type.name except: return False + + +class Interface(six.with_metaclass(ObjectTypeMeta)): + resolve_type = None + + def __init__(self, *args, **kwargs): + from .objecttype import ObjectType + if not isinstance(self, ObjectType): + raise Exception("An interface cannot be intitialized") + super(Interface, self).__init__(*args, **kwargs) + + @classmethod + def implements(cls, object_type): + ''' + We use this function for customizing when a ObjectType have this class as Interface + For example, if we want to check that the ObjectType have some required things + in it like Node.get_node + ''' diff --git a/graphene/types/tests/test_inputobjecttype.py b/graphene/types/tests/test_inputobjecttype.py index 6455b1c0..8036b96a 100644 --- a/graphene/types/tests/test_inputobjecttype.py +++ b/graphene/types/tests/test_inputobjecttype.py @@ -1,5 +1,6 @@ from graphql import GraphQLInputObjectType, GraphQLString +from graphql.type.definition import GraphQLInputFieldDefinition from ..field import InputField from ..inputobjecttype import InputObjectType @@ -43,7 +44,7 @@ def test_generate_objecttype_with_fields(): graphql_type = MyObjectType._meta.graphql_type fields = graphql_type.get_fields() assert 'field' in fields - assert isinstance(fields['field'], InputField) + assert isinstance(fields['field'], GraphQLInputFieldDefinition) def test_generate_objecttype_with_graphene_fields(): @@ -53,4 +54,4 @@ def test_generate_objecttype_with_graphene_fields(): graphql_type = MyObjectType._meta.graphql_type fields = graphql_type.get_fields() assert 'field' in fields - assert isinstance(fields['field'], InputField) + assert isinstance(fields['field'], GraphQLInputFieldDefinition) diff --git a/graphene/types/tests/test_interface.py b/graphene/types/tests/test_interface.py index 9370de98..09136e04 100644 --- a/graphene/types/tests/test_interface.py +++ b/graphene/types/tests/test_interface.py @@ -56,7 +56,7 @@ def test_interface_inheritance(): fields = graphql_type.get_fields() assert 'field' in fields assert 'inherited' in fields - assert fields['field'] > fields['inherited'] + assert MyInterface.field > MyInheritedInterface.inherited def test_interface_instance(): diff --git a/graphene/types/tests/test_objecttype.py b/graphene/types/tests/test_objecttype.py index c3f59470..a3970b52 100644 --- a/graphene/types/tests/test_objecttype.py +++ b/graphene/types/tests/test_objecttype.py @@ -74,11 +74,7 @@ def test_objecttype_inheritance(): graphql_type = MyObjectType._meta.graphql_type fields = graphql_type.get_fields() - assert 'field1' in fields - assert 'field2' in fields - assert 'inherited' in fields - assert fields['field1'] > fields['inherited'] - assert fields['field2'] > fields['field1'] + assert fields.keys() == ['inherited', 'field1', 'field2'] def test_objecttype_as_container_get_fields(): @@ -195,11 +191,7 @@ def test_objecttype_graphene_interface(): graphql_type = GrapheneObjectType._meta.graphql_type assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, ) assert graphql_type.is_type_of(GrapheneObjectType(), None, None) - fields = graphql_type.get_fields() - assert 'field' in fields - assert 'extended' in fields - assert 'name' in fields - assert fields['field'] > fields['extended'] > fields['name'] + fields = graphql_type.get_fields().keys() == ['name', 'extended', 'field'] def test_objecttype_graphene_inherit_interface(): diff --git a/graphene/types/tests/test_scalars.py b/graphene/types/tests/test_scalars.py index 7ff232a2..c79b6910 100644 --- a/graphene/types/tests/test_scalars.py +++ b/graphene/types/tests/test_scalars.py @@ -3,8 +3,9 @@ import datetime import pytest from graphene.utils.get_graphql_type import get_graphql_type -from graphql import (GraphQLBoolean, GraphQLFloat, GraphQLInt, - GraphQLScalarType, GraphQLString, graphql) +from graphql import graphql +from graphql.type import (GraphQLBoolean, GraphQLFloat, GraphQLInt, + GraphQLScalarType, GraphQLString, GraphQLFieldDefinition) from graphql.language import ast from ..field import Field @@ -94,7 +95,7 @@ def test_scalar_in_objecttype(scalar_class, graphql_type): graphql_type = get_graphql_type(MyObjectType) fields = graphql_type.get_fields() assert list(fields.keys()) == ['before', 'field', 'after'] - assert isinstance(fields['field'], Field) + assert isinstance(fields['field'], GraphQLFieldDefinition) def test_custom_scalar_empty(): diff --git a/graphene/types/tests/test_schema.py b/graphene/types/tests/test_schema.py index cb5ed568..5f3a7edc 100644 --- a/graphene/types/tests/test_schema.py +++ b/graphene/types/tests/test_schema.py @@ -11,6 +11,12 @@ class Character(Interface): friends = List(lambda: Character) best_friend = Field(lambda: Character) + def resolve_friends(self, *args): + return [Human(name='Peter')] + + def resolve_best_friend(self, *args): + return Human(name='Best') + class Pet(ObjectType): type = String() @@ -26,12 +32,6 @@ class Human(ObjectType): def resolve_pet(self, *args): return Pet(type='Dog') - def resolve_friends(self, *args): - return [Human(name='Peter')] - - def resolve_best_friend(self, *args): - return Human(name='Best') - class RootQuery(ObjectType): character = Field(Character)