Merge branch '1.0-django-interfaceobjecttype' of github.com:graphql-python/graphene into 1.0-django-interfaceobjecttype

# Conflicts:
#	graphene/types/objecttype.py
This commit is contained in:
Syrus Akbary 2016-06-21 22:06:45 -07:00
commit f2ed1e58d9
17 changed files with 146 additions and 112 deletions

View File

@ -1,9 +0,0 @@
from ..schema import Droid
def test_query_types():
graphql_type = Droid._meta.graphql_type
fields = graphql_type.get_fields()
assert fields['friends'].parent == Droid
assert fields

View File

@ -54,6 +54,10 @@ class ConnectionMeta(ObjectTypeMeta):
) )
cls.Edge = type(edge.name, (ObjectType, ), {'Meta': type('Meta', (object,), {'graphql_type': edge})}) cls.Edge = type(edge.name, (ObjectType, ), {'Meta': type('Meta', (object,), {'graphql_type': edge})})
cls._meta.graphql_type = connection cls._meta.graphql_type = connection
fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls)
cls._meta.get_fields = lambda: fields
return cls return cls

View File

@ -52,6 +52,8 @@ class ClientIDMutationMeta(MutationMeta):
mutate_and_get_payload=cls.mutate_and_get_payload, mutate_and_get_payload=cls.mutate_and_get_payload,
) )
options.graphql_type = field.type options.graphql_type = field.type
options.get_fields = lambda: output_fields
cls.Field = partial(Field.copy_and_extend, field, type=field.type, _creation_counter=None) cls.Field = partial(Field.copy_and_extend, field, type=field.type, _creation_counter=None)
return cls return cls

View File

@ -10,6 +10,8 @@ from ..types.objecttype import ObjectType, ObjectTypeMeta, is_objecttype
from ..types.options import Options from ..types.options import Options
from .connection import Connection from .connection import Connection
from ..utils.copy_fields import copy_fields
# We inherit from ObjectTypeMeta as we want to allow # We inherit from ObjectTypeMeta as we want to allow
# inheriting from Node, and also ObjectType. # inheriting from Node, and also ObjectType.
@ -23,16 +25,17 @@ class NodeMeta(ObjectTypeMeta):
meta, meta,
) )
def __new__(cls, name, bases, attrs): @staticmethod
def _create_objecttype(cls, name, bases, attrs):
if is_objecttype(bases):
cls = super(NodeMeta, cls).__new__(cls, name, bases, attrs)
# The interface provided by node_definitions is not an instance # The interface provided by node_definitions is not an instance
# of GrapheneInterfaceType, so it will have no graphql_type, # of GrapheneInterfaceType, so it will have no graphql_type,
# so will not trigger Node.implements # so will not trigger Node.implements
cls = super(NodeMeta, cls)._create_objecttype(cls, name, bases, attrs)
cls.implements(cls) cls.implements(cls)
return cls return cls
@staticmethod
def _create_interface(cls, name, bases, attrs):
options = cls._get_interface_options(attrs.pop('Meta', None)) options = cls._get_interface_options(attrs.pop('Meta', None))
cls = type.__new__(cls, name, bases, dict(attrs, _meta=options)) cls = type.__new__(cls, name, bases, dict(attrs, _meta=options))
@ -45,6 +48,10 @@ class NodeMeta(ObjectTypeMeta):
type_resolver=cls.resolve_type, type_resolver=cls.resolve_type,
) )
options.graphql_type = node_interface options.graphql_type = node_interface
fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls)
options.get_fields = lambda: fields
cls.Field = partial( cls.Field = partial(
Field.copy_and_extend, Field.copy_and_extend,
node_field, node_field,

View File

@ -2,7 +2,7 @@ import inspect
from collections import OrderedDict from collections import OrderedDict
from itertools import chain 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 graphql.utils.assert_valid_name import assert_valid_name
from ..utils.orderedtype import OrderedType from ..utils.orderedtype import OrderedType
@ -40,11 +40,15 @@ class Argument(GraphQLArgument, OrderedType):
@classmethod @classmethod
def copy_from(cls, argument): def copy_from(cls, argument):
if isinstance (argument, (GraphQLArgumentDefinition, Argument)):
name = argument.name
else:
name = None
return cls( return cls(
type=argument.type, type=argument.type,
default_value=argument.default_value, default_value=argument.default_value,
description=argument.description, description=argument.description,
name=argument.name, name=name,
_creation_counter=argument.creation_counter if isinstance(argument, Argument) else None, _creation_counter=argument.creation_counter if isinstance(argument, Argument) else None,
) )

View File

@ -22,7 +22,12 @@ class GrapheneEnumType(GrapheneGraphQLType, GraphQLEnumType):
def values_from_enum(enum): def values_from_enum(enum):
_values = OrderedDict() _values = OrderedDict()
for name, value in enum.__members__.items(): for name, value in enum.__members__.items():
_values[name] = GraphQLEnumValue(name=name, value=value.value) _values[name] = GraphQLEnumValue(
name=name,
value=value.value,
description=getattr(value, 'description', None),
deprecation_reason=getattr(value, 'deprecation_reason', None)
)
return _values return _values

View File

@ -1,6 +1,7 @@
from collections import OrderedDict
import inspect 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 graphql.utils.assert_valid_name import assert_valid_name
from ..utils.orderedtype import OrderedType 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 _creation_counter = field.creation_counter if _creation_counter is False else None
attname = attname or field.attname attname = attname or field.attname
parent = parent or field.parent parent = parent or field.parent
args = to_arguments(args, field.args)
else: else:
# If is a GraphQLField # If is a GraphQLField
type = type or field.type type = type or field.type
resolver = resolver or field.resolver 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 _creation_counter = None
attname = attname or name attname = attname or name
parent = parent parent = parent
new_field = cls( new_field = cls(
type=type, type=type,
args=to_arguments(args, field.args), args=args,
resolver=resolver, resolver=resolver,
source=source, source=source,
deprecation_reason=field.deprecation_reason, deprecation_reason=field.deprecation_reason,

View File

@ -11,16 +11,14 @@ from .objecttype import ObjectType, ObjectTypeMeta
class MutationMeta(ObjectTypeMeta): class MutationMeta(ObjectTypeMeta):
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
super_new = super(MutationMeta, cls).__new__ # Also ensure initialization is only performed for subclasses of
# Mutation
# Also ensure initialization is only performed for subclasses of Model
# (excluding Model class itself).
if not is_base_type(bases, MutationMeta): if not is_base_type(bases, MutationMeta):
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
Input = attrs.pop('Input', None) Input = attrs.pop('Input', None)
cls = super_new(cls, name, bases, attrs) cls = cls._create_objecttype(cls, name, bases, attrs)
field_args = props(Input) if Input else {} field_args = props(Input) if Input else {}
resolver = getattr(cls, 'mutate', None) resolver = getattr(cls, 'mutate', None)
assert resolver, 'All mutations must define a mutate method in it' assert resolver, 'All mutations must define a mutate method in it'

View File

@ -55,48 +55,15 @@ class ObjectTypeMeta(type):
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
super_new = type.__new__ super_new = type.__new__
# Also ensure initialization is only performed for subclasses of Model # Also ensure initialization is only performed for subclasses of
# (excluding Model class itself). # ObjectType,or Interfaces
if not is_base_type(bases, ObjectTypeMeta): if not is_base_type(bases, ObjectTypeMeta):
return super_new(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
if not is_objecttype(bases): if not is_objecttype(bases):
return cls._create_interface(cls, name, bases, attrs) return cls._create_interface(cls, name, bases, attrs)
options = Options( return cls._create_objecttype(cls, name, bases, attrs)
attrs.pop('Meta', None),
name=None,
description=None,
graphql_type=None,
interfaces=(),
abstract=False
)
interfaces = tuple(options.interfaces)
fields = get_fields(ObjectType, attrs, bases, interfaces)
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)
base_interfaces = tuple(b for b in bases if issubclass(b, Interface))
options.graphql_type = GrapheneObjectType(
graphene_type=cls,
name=options.name or cls.__name__,
description=options.description or cls.__doc__,
fields=fields,
is_type_of=cls.is_type_of,
interfaces=tuple(get_interfaces(interfaces + base_interfaces))
)
else:
assert not fields, "Can't mount Fields in an ObjectType with a defined graphql_type"
fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls)
for name, field in fields.items():
setattr(cls, field.attname or name, field)
return cls
def get_interfaces(cls, bases): def get_interfaces(cls, bases):
return (b for b in bases if issubclass(b, Interface)) return (b for b in bases if issubclass(b, Interface))
@ -133,7 +100,47 @@ class ObjectTypeMeta(type):
) )
else: else:
assert not fields, "Can't mount Fields in an Interface with a defined graphql_type" 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) fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls)
options.get_fields = lambda: fields
for name, field in fields.items():
setattr(cls, field.attname or name, field)
return cls
@staticmethod
def _create_objecttype(cls, name, bases, attrs):
options = Options(
attrs.pop('Meta', None),
name=None,
description=None,
graphql_type=None,
interfaces=(),
abstract=False
)
interfaces = tuple(options.interfaces)
fields = get_fields(ObjectType, attrs, bases, interfaces)
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)
base_interfaces = tuple(b for b in bases if issubclass(b, Interface))
options.graphql_type = GrapheneObjectType(
graphene_type=cls,
name=options.name or cls.__name__,
description=options.description or cls.__doc__,
fields=fields,
is_type_of=cls.is_type_of,
interfaces=tuple(get_interfaces(interfaces + base_interfaces))
)
else:
assert not fields, "Can't mount Fields in an ObjectType with a defined graphql_type"
fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls)
options.get_fields = lambda: fields
for name, field in fields.items(): for name, field in fields.items():
setattr(cls, field.attname or name, field) setattr(cls, field.attname or name, field)
@ -146,9 +153,9 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# GraphQL ObjectType acting as container # GraphQL ObjectType acting as container
args_len = len(args) args_len = len(args)
fields = self._meta.graphql_type.get_fields().values() fields = self._meta.get_fields().items()
for f in fields: for name, f in fields:
setattr(self, getattr(f, 'attname', f.name), None) setattr(self, getattr(f, 'attname', name), None)
if args_len > len(fields): if args_len > len(fields):
# Daft, but matches old exception sans the err msg. # Daft, but matches old exception sans the err msg.
@ -156,18 +163,18 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
fields_iter = iter(fields) fields_iter = iter(fields)
if not kwargs: if not kwargs:
for val, field in zip(args, fields_iter): for val, (name, field) in zip(args, fields_iter):
attname = getattr(field, 'attname', field.name) attname = getattr(field, 'attname', name)
setattr(self, attname, val) setattr(self, attname, val)
else: else:
for val, field in zip(args, fields_iter): for val, (name, field) in zip(args, fields_iter):
attname = getattr(field, 'attname', field.name) attname = getattr(field, 'attname', name)
setattr(self, attname, val) setattr(self, attname, val)
kwargs.pop(attname, None) kwargs.pop(attname, None)
for field in fields_iter: for name, field in fields_iter:
try: try:
attname = getattr(field, 'attname', field.name) attname = getattr(field, 'attname', name)
val = kwargs.pop(attname) val = kwargs.pop(attname)
setattr(self, attname, val) setattr(self, attname, val)
except KeyError: except KeyError:

View File

@ -11,6 +11,10 @@ def test_enum_construction():
GREEN = 2 GREEN = 2
BLUE = 3 BLUE = 3
@property
def description(self):
return "Description {}".format(self.name)
assert isinstance(RGB._meta.graphql_type, GraphQLEnumType) assert isinstance(RGB._meta.graphql_type, GraphQLEnumType)
values = RGB._meta.graphql_type.get_values() values = RGB._meta.graphql_type.get_values()
assert sorted([v.name for v in values]) == [ assert sorted([v.name for v in values]) == [
@ -18,6 +22,11 @@ def test_enum_construction():
'GREEN', 'GREEN',
'RED' 'RED'
] ]
assert sorted([v.description for v in values]) == [
'Description BLUE',
'Description GREEN',
'Description RED'
]
assert isinstance(RGB(name='field_name').as_field(), Field) assert isinstance(RGB(name='field_name').as_field(), Field)
assert isinstance(RGB(name='field_name').as_argument(), Argument) assert isinstance(RGB(name='field_name').as_argument(), Argument)

View File

@ -1,5 +1,6 @@
from graphql import GraphQLInputObjectType, GraphQLString from graphql import GraphQLInputObjectType, GraphQLString
from graphql.type.definition import GraphQLInputFieldDefinition
from ..field import InputField from ..field import InputField
from ..inputobjecttype import InputObjectType from ..inputobjecttype import InputObjectType
@ -43,7 +44,7 @@ def test_generate_objecttype_with_fields():
graphql_type = MyObjectType._meta.graphql_type graphql_type = MyObjectType._meta.graphql_type
fields = graphql_type.get_fields() fields = graphql_type.get_fields()
assert 'field' in fields assert 'field' in fields
assert isinstance(fields['field'], InputField) assert isinstance(fields['field'], GraphQLInputFieldDefinition)
def test_generate_objecttype_with_graphene_fields(): def test_generate_objecttype_with_graphene_fields():
@ -53,4 +54,4 @@ def test_generate_objecttype_with_graphene_fields():
graphql_type = MyObjectType._meta.graphql_type graphql_type = MyObjectType._meta.graphql_type
fields = graphql_type.get_fields() fields = graphql_type.get_fields()
assert 'field' in 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() fields = graphql_type.get_fields()
assert 'field' in fields assert 'field' in fields
assert 'inherited' in fields assert 'inherited' in fields
assert fields['field'] > fields['inherited'] assert MyInterface.field > MyInheritedInterface.inherited
def test_interface_instance(): def test_interface_instance():

View File

@ -74,11 +74,7 @@ def test_objecttype_inheritance():
graphql_type = MyObjectType._meta.graphql_type graphql_type = MyObjectType._meta.graphql_type
fields = graphql_type.get_fields() fields = graphql_type.get_fields()
assert 'field1' in fields assert fields.keys() == ['inherited', 'field1', 'field2']
assert 'field2' in fields
assert 'inherited' in fields
assert fields['field1'] > fields['inherited']
assert fields['field2'] > fields['field1']
def test_objecttype_as_container_get_fields(): def test_objecttype_as_container_get_fields():
@ -195,11 +191,7 @@ def test_objecttype_graphene_interface():
graphql_type = GrapheneObjectType._meta.graphql_type graphql_type = GrapheneObjectType._meta.graphql_type
assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, ) assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, )
assert graphql_type.is_type_of(GrapheneObjectType(), None, None) assert graphql_type.is_type_of(GrapheneObjectType(), None, None)
fields = graphql_type.get_fields() fields = graphql_type.get_fields().keys() == ['name', 'extended', 'field']
assert 'field' in fields
assert 'extended' in fields
assert 'name' in fields
assert fields['field'] > fields['extended'] > fields['name']
def test_objecttype_graphene_inherit_interface(): def test_objecttype_graphene_inherit_interface():
@ -214,11 +206,8 @@ def test_objecttype_graphene_inherit_interface():
assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, ) assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, )
assert graphql_type.is_type_of(GrapheneObjectType(), None, None) assert graphql_type.is_type_of(GrapheneObjectType(), None, None)
fields = graphql_type.get_fields() fields = graphql_type.get_fields()
assert 'field' in fields fields = graphql_type.get_fields().keys() == ['name', 'extended', 'field']
assert 'extended' in fields
assert 'name' in fields
assert issubclass(GrapheneObjectType, GrapheneInterface) assert issubclass(GrapheneObjectType, GrapheneInterface)
assert fields['field'] > fields['extended'] > fields['name']
# def test_objecttype_graphene_interface_extended(): # def test_objecttype_graphene_interface_extended():

View File

@ -3,8 +3,9 @@ import datetime
import pytest import pytest
from graphene.utils.get_graphql_type import get_graphql_type from graphene.utils.get_graphql_type import get_graphql_type
from graphql import (GraphQLBoolean, GraphQLFloat, GraphQLInt, from graphql import graphql
GraphQLScalarType, GraphQLString, graphql) from graphql.type import (GraphQLBoolean, GraphQLFloat, GraphQLInt,
GraphQLScalarType, GraphQLString, GraphQLFieldDefinition)
from graphql.language import ast from graphql.language import ast
from ..field import Field from ..field import Field
@ -94,7 +95,7 @@ def test_scalar_in_objecttype(scalar_class, graphql_type):
graphql_type = get_graphql_type(MyObjectType) graphql_type = get_graphql_type(MyObjectType)
fields = graphql_type.get_fields() fields = graphql_type.get_fields()
assert list(fields.keys()) == ['before', 'field', 'after'] assert list(fields.keys()) == ['before', 'field', 'after']
assert isinstance(fields['field'], Field) assert isinstance(fields['field'], GraphQLFieldDefinition)
def test_custom_scalar_empty(): def test_custom_scalar_empty():

View File

@ -11,6 +11,12 @@ class Character(Interface):
friends = List(lambda: Character) friends = List(lambda: Character)
best_friend = Field(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): class Pet(ObjectType):
type = String() type = String()
@ -26,12 +32,6 @@ class Human(ObjectType):
def resolve_pet(self, *args): def resolve_pet(self, *args):
return Pet(type='Dog') 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): class RootQuery(ObjectType):
character = Field(Character) character = Field(Character)

View File

@ -16,10 +16,24 @@ def get_fields_from_attrs(in_type, attrs):
yield attname, field yield attname, field
def get_fields_from_types(bases): def get_fields_from_bases_and_types(bases, types):
fields = set() fields = set()
for _class in bases: for _class in bases:
for attname, field in get_graphql_type(_class).get_fields().items(): if not is_graphene_type(_class):
continue
_fields = _class._meta.get_fields()
if callable(_fields):
_fields = _fields()
for default_attname, field in _fields.items():
attname = getattr(field, 'attname', default_attname)
if attname in fields:
continue
fields.add(attname)
yield attname, field
for grapqhl_type in types:
for attname, field in get_graphql_type(grapqhl_type).get_fields().items():
if attname in fields: if attname in fields:
continue continue
fields.add(attname) fields.add(attname)
@ -29,11 +43,7 @@ def get_fields_from_types(bases):
def get_fields(in_type, attrs, bases, graphql_types=()): def get_fields(in_type, attrs, bases, graphql_types=()):
fields = [] fields = []
graphene_bases = tuple( extended_fields = list(get_fields_from_bases_and_types(bases, graphql_types))
base._meta.graphql_type for base in bases if is_graphene_type(base)
) + graphql_types
extended_fields = list(get_fields_from_types(graphene_bases))
local_fields = list(get_fields_from_attrs(in_type, attrs)) local_fields = list(get_fields_from_attrs(in_type, attrs))
# We asume the extended fields are already sorted, so we only # We asume the extended fields are already sorted, so we only
# have to sort the local fields, that are get from attrs # have to sort the local fields, that are get from attrs

View File

@ -4,7 +4,7 @@ from graphql import (GraphQLField, GraphQLFloat, GraphQLInt,
GraphQLInterfaceType, GraphQLString) GraphQLInterfaceType, GraphQLString)
from ...types import Argument, Field, ObjectType, String from ...types import Argument, Field, ObjectType, String
from ..get_fields import get_fields_from_attrs, get_fields_from_types from ..get_fields import get_fields_from_attrs, get_fields_from_bases_and_types
def test_get_fields_from_attrs(): def test_get_fields_from_attrs():
@ -31,8 +31,8 @@ def test_get_fields_from_types():
('extra', GraphQLField(GraphQLFloat)) ('extra', GraphQLField(GraphQLFloat))
])) ]))
bases = (int_base, float_base) _types = (int_base, float_base)
base_fields = OrderedDict(get_fields_from_types(bases)) base_fields = OrderedDict(get_fields_from_bases_and_types((), _types))
assert [f for f in base_fields.keys()] == ['int', 'num', 'extra', 'float'] assert [f for f in base_fields.keys()] == ['int', 'num', 'extra', 'float']
assert [f.type for f in base_fields.values()] == [ assert [f.type for f in base_fields.values()] == [
GraphQLInt, GraphQLInt,