Moved Interface to ObjectType. Improved integration with latest GraphQL-core

This commit is contained in:
Syrus Akbary 2016-06-20 17:47:26 -07:00
parent 7a29502790
commit 9a9d7f8873
10 changed files with 97 additions and 114 deletions

View File

@ -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,
)

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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
'''

View File

@ -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)

View File

@ -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():

View File

@ -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():

View File

@ -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():

View File

@ -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)