mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-02 12:44:15 +03:00
Improved classtypes core support
This commit is contained in:
parent
398f7da24c
commit
8abcaff02b
|
@ -8,11 +8,15 @@ from graphene.core.schema import (
|
||||||
Schema
|
Schema
|
||||||
)
|
)
|
||||||
|
|
||||||
from graphene.core.types import (
|
from graphene.core.classtypes import (
|
||||||
ObjectType,
|
ObjectType,
|
||||||
InputObjectType,
|
InputObjectType,
|
||||||
Interface,
|
Interface,
|
||||||
Mutation,
|
Mutation,
|
||||||
|
Scalar
|
||||||
|
)
|
||||||
|
|
||||||
|
from graphene.core.types import (
|
||||||
BaseType,
|
BaseType,
|
||||||
LazyType,
|
LazyType,
|
||||||
Argument,
|
Argument,
|
||||||
|
@ -59,6 +63,7 @@ __all__ = [
|
||||||
'InputObjectType',
|
'InputObjectType',
|
||||||
'Interface',
|
'Interface',
|
||||||
'Mutation',
|
'Mutation',
|
||||||
|
'Scalar',
|
||||||
'Field',
|
'Field',
|
||||||
'InputField',
|
'InputField',
|
||||||
'StringField',
|
'StringField',
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
from .inputobjecttype import InputObjectType
|
||||||
|
from .interface import Interface
|
||||||
|
from .mutation import Mutation
|
||||||
|
from .objecttype import ObjectType
|
||||||
|
from .options import Options
|
||||||
|
from .scalar import Scalar
|
||||||
|
from .uniontype import UnionType
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'InputObjectType',
|
||||||
|
'Interface',
|
||||||
|
'Mutation',
|
||||||
|
'ObjectType',
|
||||||
|
'Options',
|
||||||
|
'Scalar',
|
||||||
|
'UnionType']
|
|
@ -1,72 +0,0 @@
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from ..utils import cached_property
|
|
||||||
|
|
||||||
DEFAULT_NAMES = ('description', 'name', 'is_interface', 'is_mutation',
|
|
||||||
'type_name', 'interfaces', 'abstract')
|
|
||||||
|
|
||||||
|
|
||||||
class Options(object):
|
|
||||||
|
|
||||||
def __init__(self, meta=None):
|
|
||||||
self.meta = meta
|
|
||||||
self.local_fields = []
|
|
||||||
self.is_interface = False
|
|
||||||
self.is_mutation = False
|
|
||||||
self.is_union = False
|
|
||||||
self.abstract = False
|
|
||||||
self.interfaces = []
|
|
||||||
self.parents = []
|
|
||||||
self.types = []
|
|
||||||
self.valid_attrs = DEFAULT_NAMES
|
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
|
||||||
cls._meta = self
|
|
||||||
self.parent = cls
|
|
||||||
# First, construct the default values for these options.
|
|
||||||
self.object_name = cls.__name__
|
|
||||||
self.type_name = self.object_name
|
|
||||||
|
|
||||||
self.description = cls.__doc__
|
|
||||||
# Store the original user-defined values for each option,
|
|
||||||
# for use when serializing the model definition
|
|
||||||
self.original_attrs = {}
|
|
||||||
|
|
||||||
# Next, apply any overridden values from 'class Meta'.
|
|
||||||
if self.meta:
|
|
||||||
meta_attrs = self.meta.__dict__.copy()
|
|
||||||
for name in self.meta.__dict__:
|
|
||||||
# Ignore any private attributes that Django doesn't care about.
|
|
||||||
# NOTE: We can't modify a dictionary's contents while looping
|
|
||||||
# over it, so we loop over the *original* dictionary instead.
|
|
||||||
if name.startswith('_'):
|
|
||||||
del meta_attrs[name]
|
|
||||||
for attr_name in self.valid_attrs:
|
|
||||||
if attr_name in meta_attrs:
|
|
||||||
setattr(self, attr_name, meta_attrs.pop(attr_name))
|
|
||||||
self.original_attrs[attr_name] = getattr(self, attr_name)
|
|
||||||
elif hasattr(self.meta, attr_name):
|
|
||||||
setattr(self, attr_name, getattr(self.meta, attr_name))
|
|
||||||
self.original_attrs[attr_name] = getattr(self, attr_name)
|
|
||||||
|
|
||||||
del self.valid_attrs
|
|
||||||
|
|
||||||
# Any leftover attributes must be invalid.
|
|
||||||
if meta_attrs != {}:
|
|
||||||
raise TypeError(
|
|
||||||
"'class Meta' got invalid attribute(s): %s" %
|
|
||||||
','.join(
|
|
||||||
meta_attrs.keys()))
|
|
||||||
|
|
||||||
del self.meta
|
|
||||||
|
|
||||||
def add_field(self, field):
|
|
||||||
self.local_fields.append(field)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def fields(self):
|
|
||||||
return sorted(self.local_fields)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def fields_map(self):
|
|
||||||
return OrderedDict([(f.attname, f) for f in self.fields])
|
|
|
@ -11,7 +11,6 @@ from graphql.core.utils.schema_printer import print_schema
|
||||||
from graphene import signals
|
from graphene import signals
|
||||||
|
|
||||||
from .types.base import BaseType
|
from .types.base import BaseType
|
||||||
from .types.objecttype import BaseObjectType
|
|
||||||
from .classtypes.base import ClassType
|
from .classtypes.base import ClassType
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,7 +48,7 @@ class Schema(object):
|
||||||
internal_type = object_type.internal_type(self)
|
internal_type = object_type.internal_type(self)
|
||||||
self._types[object_type] = internal_type
|
self._types[object_type] = internal_type
|
||||||
is_objecttype = inspect.isclass(
|
is_objecttype = inspect.isclass(
|
||||||
object_type) and issubclass(object_type, BaseObjectType)
|
object_type) and issubclass(object_type, ClassType)
|
||||||
if is_objecttype:
|
if is_objecttype:
|
||||||
self.register(object_type)
|
self.register(object_type)
|
||||||
return self._types[object_type]
|
return self._types[object_type]
|
||||||
|
@ -91,7 +90,7 @@ class Schema(object):
|
||||||
if name:
|
if name:
|
||||||
objecttype = self._types_names.get(name, None)
|
objecttype = self._types_names.get(name, None)
|
||||||
if objecttype and inspect.isclass(
|
if objecttype and inspect.isclass(
|
||||||
objecttype) and issubclass(objecttype, BaseObjectType):
|
objecttype) and issubclass(objecttype, ClassType):
|
||||||
return objecttype
|
return objecttype
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from .base import BaseType, LazyType, OrderedType
|
from .base import BaseType, LazyType, OrderedType
|
||||||
from .argument import Argument, ArgumentsGroup, to_arguments
|
from .argument import Argument, ArgumentsGroup, to_arguments
|
||||||
from .definitions import List, NonNull
|
from .definitions import List, NonNull
|
||||||
from .objecttype import ObjectTypeMeta, BaseObjectType, Interface, ObjectType, Mutation, InputObjectType
|
# Compatibility import
|
||||||
|
from .objecttype import Interface, ObjectType, Mutation, InputObjectType
|
||||||
|
|
||||||
from .scalars import String, ID, Boolean, Int, Float, Scalar
|
from .scalars import String, ID, Boolean, Int, Float, Scalar
|
||||||
from .field import Field, InputField
|
from .field import Field, InputField
|
||||||
|
|
||||||
|
@ -17,8 +19,6 @@ __all__ = [
|
||||||
'Field',
|
'Field',
|
||||||
'InputField',
|
'InputField',
|
||||||
'Interface',
|
'Interface',
|
||||||
'BaseObjectType',
|
|
||||||
'ObjectTypeMeta',
|
|
||||||
'ObjectType',
|
'ObjectType',
|
||||||
'Mutation',
|
'Mutation',
|
||||||
'InputObjectType',
|
'InputObjectType',
|
||||||
|
|
|
@ -2,9 +2,6 @@ from functools import total_ordering
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ..classtypes.base import FieldsClassType
|
|
||||||
from ..classtypes.inputobjecttype import InputObjectType as NewInputObjectType
|
|
||||||
|
|
||||||
|
|
||||||
class BaseType(object):
|
class BaseType(object):
|
||||||
|
|
||||||
|
@ -107,11 +104,12 @@ class ArgumentType(MirroredType):
|
||||||
class FieldType(MirroredType):
|
class FieldType(MirroredType):
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
from ..types import BaseObjectType, InputObjectType
|
from ..classtypes.base import FieldsClassType
|
||||||
if issubclass(cls, (InputObjectType, NewInputObjectType)):
|
from ..classtypes.inputobjecttype import InputObjectType
|
||||||
|
if issubclass(cls, (InputObjectType)):
|
||||||
inputfield = self.as_inputfield()
|
inputfield = self.as_inputfield()
|
||||||
return inputfield.contribute_to_class(cls, name)
|
return inputfield.contribute_to_class(cls, name)
|
||||||
elif issubclass(cls, (BaseObjectType, FieldsClassType)):
|
elif issubclass(cls, (FieldsClassType)):
|
||||||
field = self.as_field()
|
field = self.as_field()
|
||||||
return field.contribute_to_class(cls, name)
|
return field.contribute_to_class(cls, name)
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ from graphql.core.type import GraphQLField, GraphQLInputObjectField
|
||||||
|
|
||||||
from ...utils import to_camel_case
|
from ...utils import to_camel_case
|
||||||
from ..classtypes.base import FieldsClassType
|
from ..classtypes.base import FieldsClassType
|
||||||
from ..classtypes.inputobjecttype import InputObjectType as NewInputObjectType
|
from ..classtypes.mutation import Mutation
|
||||||
from ..types import BaseObjectType, InputObjectType
|
from ..classtypes.inputobjecttype import InputObjectType
|
||||||
from .argument import ArgumentsGroup, snake_case_args
|
from .argument import ArgumentsGroup, snake_case_args
|
||||||
from .base import LazyType, MountType, OrderedType
|
from .base import LazyType, MountType, OrderedType
|
||||||
from .definitions import NonNull
|
from .definitions import NonNull
|
||||||
|
@ -34,7 +34,7 @@ class Field(OrderedType):
|
||||||
|
|
||||||
def contribute_to_class(self, cls, attname):
|
def contribute_to_class(self, cls, attname):
|
||||||
assert issubclass(
|
assert issubclass(
|
||||||
cls, (BaseObjectType, FieldsClassType)), 'Field {} cannot be mounted in {}'.format(
|
cls, (FieldsClassType)), 'Field {} cannot be mounted in {}'.format(
|
||||||
self, cls)
|
self, cls)
|
||||||
if not self.name:
|
if not self.name:
|
||||||
self.name = to_camel_case(attname)
|
self.name = to_camel_case(attname)
|
||||||
|
@ -71,7 +71,7 @@ class Field(OrderedType):
|
||||||
description = resolver.__doc__
|
description = resolver.__doc__
|
||||||
type = schema.T(self.get_type(schema))
|
type = schema.T(self.get_type(schema))
|
||||||
type_objecttype = schema.objecttype(type)
|
type_objecttype = schema.objecttype(type)
|
||||||
if type_objecttype and type_objecttype._meta.is_mutation:
|
if type_objecttype and issubclass(type_objecttype, Mutation):
|
||||||
assert len(arguments) == 0
|
assert len(arguments) == 0
|
||||||
arguments = type_objecttype.get_arguments()
|
arguments = type_objecttype.get_arguments()
|
||||||
resolver = getattr(type_objecttype, 'mutate')
|
resolver = getattr(type_objecttype, 'mutate')
|
||||||
|
@ -128,7 +128,7 @@ class InputField(OrderedType):
|
||||||
|
|
||||||
def contribute_to_class(self, cls, attname):
|
def contribute_to_class(self, cls, attname):
|
||||||
assert issubclass(
|
assert issubclass(
|
||||||
cls, (InputObjectType, NewInputObjectType)), 'InputField {} cannot be mounted in {}'.format(
|
cls, (InputObjectType)), 'InputField {} cannot be mounted in {}'.format(
|
||||||
self, cls)
|
self, cls)
|
||||||
if not self.name:
|
if not self.name:
|
||||||
self.name = to_camel_case(attname)
|
self.name = to_camel_case(attname)
|
||||||
|
|
|
@ -1,282 +1,3 @@
|
||||||
import copy
|
from ..classtypes import ObjectType, Interface, Mutation, InputObjectType
|
||||||
import inspect
|
|
||||||
from collections import OrderedDict
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
import six
|
__all__ = ['ObjectType', 'Interface', 'Mutation', 'InputObjectType']
|
||||||
from graphql.core.type import (GraphQLInputObjectType, GraphQLInterfaceType,
|
|
||||||
GraphQLObjectType, GraphQLUnionType)
|
|
||||||
|
|
||||||
from graphene import signals
|
|
||||||
|
|
||||||
from ..exceptions import SkipField
|
|
||||||
from ..options import Options
|
|
||||||
from .argument import ArgumentsGroup
|
|
||||||
from .base import BaseType
|
|
||||||
from .definitions import List, NonNull
|
|
||||||
|
|
||||||
|
|
||||||
def is_objecttype(cls):
|
|
||||||
if not issubclass(cls, BaseObjectType):
|
|
||||||
return False
|
|
||||||
_meta = getattr(cls, '_meta', None)
|
|
||||||
return not(_meta and (_meta.abstract or _meta.is_interface))
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectTypeMeta(type):
|
|
||||||
options_cls = Options
|
|
||||||
|
|
||||||
def is_interface(cls, parents):
|
|
||||||
return Interface in parents
|
|
||||||
|
|
||||||
def is_mutation(cls, parents):
|
|
||||||
return issubclass(cls, Mutation)
|
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
|
||||||
super_new = super(ObjectTypeMeta, cls).__new__
|
|
||||||
parents = [b for b in bases if isinstance(b, cls)]
|
|
||||||
if not parents:
|
|
||||||
# If this isn't a subclass of Model, don't do anything special.
|
|
||||||
return super_new(cls, name, bases, attrs)
|
|
||||||
|
|
||||||
module = attrs.pop('__module__', None)
|
|
||||||
doc = attrs.pop('__doc__', None)
|
|
||||||
new_class = super_new(cls, name, bases, {
|
|
||||||
'__module__': module,
|
|
||||||
'__doc__': doc
|
|
||||||
})
|
|
||||||
attr_meta = attrs.pop('Meta', None)
|
|
||||||
abstract = getattr(attr_meta, 'abstract', False)
|
|
||||||
if not attr_meta:
|
|
||||||
meta = getattr(new_class, 'Meta', None)
|
|
||||||
else:
|
|
||||||
meta = attr_meta
|
|
||||||
|
|
||||||
base_meta = getattr(new_class, '_meta', None)
|
|
||||||
|
|
||||||
new_class.add_to_class('_meta', new_class.options_cls(meta))
|
|
||||||
|
|
||||||
new_class._meta.is_interface = new_class.is_interface(parents)
|
|
||||||
new_class._meta.is_mutation = new_class.is_mutation(parents) or (base_meta and base_meta.is_mutation)
|
|
||||||
union_types = list(filter(is_objecttype, parents))
|
|
||||||
|
|
||||||
new_class._meta.is_union = len(union_types) > 1
|
|
||||||
new_class._meta.types = union_types
|
|
||||||
|
|
||||||
assert not (
|
|
||||||
new_class._meta.is_interface and new_class._meta.is_mutation)
|
|
||||||
|
|
||||||
assert not (
|
|
||||||
new_class._meta.is_interface and new_class._meta.is_union)
|
|
||||||
|
|
||||||
# Add all attributes to the class.
|
|
||||||
for obj_name, obj in attrs.items():
|
|
||||||
new_class.add_to_class(obj_name, obj)
|
|
||||||
|
|
||||||
if abstract:
|
|
||||||
new_class._prepare()
|
|
||||||
return new_class
|
|
||||||
|
|
||||||
if new_class._meta.is_mutation:
|
|
||||||
assert hasattr(
|
|
||||||
new_class, 'mutate'), "All mutations must implement mutate method"
|
|
||||||
|
|
||||||
new_class.add_extra_fields()
|
|
||||||
|
|
||||||
new_fields = new_class._meta.local_fields
|
|
||||||
assert not(new_class._meta.is_union and new_fields), 'An union cannot have extra fields'
|
|
||||||
|
|
||||||
field_names = {f.name: f for f in new_fields}
|
|
||||||
|
|
||||||
for base in parents:
|
|
||||||
if not hasattr(base, '_meta'):
|
|
||||||
# Things without _meta aren't functional models, so they're
|
|
||||||
# uninteresting parents.
|
|
||||||
continue
|
|
||||||
# if base._meta.schema != new_class._meta.schema:
|
|
||||||
# raise Exception('The parent schema is not the same')
|
|
||||||
|
|
||||||
parent_fields = base._meta.local_fields
|
|
||||||
# Check for clashes between locally declared fields and those
|
|
||||||
# on the base classes (we cannot handle shadowed fields at the
|
|
||||||
# moment).
|
|
||||||
for field in parent_fields:
|
|
||||||
if field.name in field_names and field.type.__class__ != field_names[
|
|
||||||
field.name].type.__class__:
|
|
||||||
raise Exception(
|
|
||||||
'Local field %r in class %r (%r) clashes '
|
|
||||||
'with field with similar name from '
|
|
||||||
'Interface %s (%r)' % (
|
|
||||||
field.name,
|
|
||||||
new_class.__name__,
|
|
||||||
field.__class__,
|
|
||||||
base.__name__,
|
|
||||||
field_names[field.name].__class__)
|
|
||||||
)
|
|
||||||
new_field = copy.copy(field)
|
|
||||||
new_class.add_to_class(field.attname, new_field)
|
|
||||||
|
|
||||||
new_class._meta.parents.append(base)
|
|
||||||
if base._meta.is_interface:
|
|
||||||
new_class._meta.interfaces.append(base)
|
|
||||||
# new_class._meta.parents.extend(base._meta.parents)
|
|
||||||
|
|
||||||
setattr(new_class, 'NonNull', NonNull(new_class))
|
|
||||||
setattr(new_class, 'List', List(new_class))
|
|
||||||
|
|
||||||
new_class._prepare()
|
|
||||||
return new_class
|
|
||||||
|
|
||||||
def add_extra_fields(cls):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _prepare(cls):
|
|
||||||
if hasattr(cls, '_prepare_class'):
|
|
||||||
cls._prepare_class()
|
|
||||||
signals.class_prepared.send(cls)
|
|
||||||
|
|
||||||
def add_to_class(cls, name, value):
|
|
||||||
# We should call the contribute_to_class method only if it's bound
|
|
||||||
if not inspect.isclass(value) and hasattr(
|
|
||||||
value, 'contribute_to_class'):
|
|
||||||
value.contribute_to_class(cls, name)
|
|
||||||
else:
|
|
||||||
setattr(cls, name, value)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseObjectType(BaseType):
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
|
||||||
if cls._meta.is_interface:
|
|
||||||
raise Exception("An interface cannot be initialized")
|
|
||||||
elif cls._meta.is_union:
|
|
||||||
raise Exception("An union cannot be initialized")
|
|
||||||
elif cls._meta.abstract:
|
|
||||||
raise Exception("An abstract ObjectType cannot be initialized")
|
|
||||||
return super(BaseObjectType, cls).__new__(cls)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
signals.pre_init.send(self.__class__, args=args, kwargs=kwargs)
|
|
||||||
self._root = kwargs.pop('_root', None)
|
|
||||||
args_len = len(args)
|
|
||||||
fields = self._meta.fields
|
|
||||||
if args_len > len(fields):
|
|
||||||
# Daft, but matches old exception sans the err msg.
|
|
||||||
raise IndexError("Number of args exceeds number of fields")
|
|
||||||
fields_iter = iter(fields)
|
|
||||||
|
|
||||||
if not kwargs:
|
|
||||||
for val, field in zip(args, fields_iter):
|
|
||||||
setattr(self, field.attname, val)
|
|
||||||
else:
|
|
||||||
for val, field in zip(args, fields_iter):
|
|
||||||
setattr(self, field.attname, val)
|
|
||||||
kwargs.pop(field.attname, None)
|
|
||||||
|
|
||||||
for field in fields_iter:
|
|
||||||
try:
|
|
||||||
val = kwargs.pop(field.attname)
|
|
||||||
setattr(self, field.attname, val)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
for prop in list(kwargs):
|
|
||||||
try:
|
|
||||||
if isinstance(getattr(self.__class__, prop), property):
|
|
||||||
setattr(self, prop, kwargs.pop(prop))
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
if kwargs:
|
|
||||||
raise TypeError(
|
|
||||||
"'%s' is an invalid keyword argument for this function" %
|
|
||||||
list(kwargs)[0])
|
|
||||||
|
|
||||||
signals.post_init.send(self.__class__, instance=self)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _resolve_type(cls, schema, instance, *args):
|
|
||||||
return schema.T(instance.__class__)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def internal_type(cls, schema):
|
|
||||||
if cls._meta.abstract:
|
|
||||||
raise Exception("Abstract ObjectTypes don't have a specific type.")
|
|
||||||
|
|
||||||
if cls._meta.is_interface:
|
|
||||||
return GraphQLInterfaceType(
|
|
||||||
cls._meta.type_name,
|
|
||||||
description=cls._meta.description,
|
|
||||||
resolve_type=partial(cls._resolve_type, schema),
|
|
||||||
fields=partial(cls.get_fields, schema)
|
|
||||||
)
|
|
||||||
elif cls._meta.is_union:
|
|
||||||
return GraphQLUnionType(
|
|
||||||
cls._meta.type_name,
|
|
||||||
types=cls._meta.types,
|
|
||||||
description=cls._meta.description,
|
|
||||||
)
|
|
||||||
return GraphQLObjectType(
|
|
||||||
cls._meta.type_name,
|
|
||||||
description=cls._meta.description,
|
|
||||||
interfaces=[schema.T(i) for i in cls._meta.interfaces],
|
|
||||||
fields=partial(cls.get_fields, schema),
|
|
||||||
is_type_of=getattr(cls, 'is_type_of', None)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fields(cls, schema):
|
|
||||||
fields = []
|
|
||||||
for field in cls._meta.fields:
|
|
||||||
try:
|
|
||||||
fields.append((field.name, schema.T(field)))
|
|
||||||
except SkipField:
|
|
||||||
continue
|
|
||||||
|
|
||||||
return OrderedDict(fields)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def wrap(cls, instance, args, info):
|
|
||||||
return cls(_root=instance)
|
|
||||||
|
|
||||||
|
|
||||||
class Interface(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectType(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Mutation(six.with_metaclass(ObjectTypeMeta, BaseObjectType)):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _construct_arguments(cls, items):
|
|
||||||
return ArgumentsGroup(**items)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _prepare_class(cls):
|
|
||||||
input_class = getattr(cls, 'Input', None)
|
|
||||||
if input_class:
|
|
||||||
items = dict(vars(input_class))
|
|
||||||
items.pop('__dict__', None)
|
|
||||||
items.pop('__doc__', None)
|
|
||||||
items.pop('__module__', None)
|
|
||||||
items.pop('__weakref__', None)
|
|
||||||
cls.add_to_class('arguments', cls._construct_arguments(items))
|
|
||||||
delattr(cls, 'Input')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_arguments(cls):
|
|
||||||
return cls.arguments
|
|
||||||
|
|
||||||
|
|
||||||
class InputObjectType(ObjectType):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def internal_type(cls, schema):
|
|
||||||
return GraphQLInputObjectType(
|
|
||||||
cls._meta.type_name,
|
|
||||||
description=cls._meta.description,
|
|
||||||
fields=partial(cls.get_fields, schema),
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,194 +0,0 @@
|
||||||
from graphql.core.execution.middlewares.utils import (resolver_has_tag,
|
|
||||||
tag_resolver)
|
|
||||||
from graphql.core.type import (GraphQLInterfaceType, GraphQLObjectType,
|
|
||||||
GraphQLUnionType)
|
|
||||||
from py.test import raises
|
|
||||||
|
|
||||||
from graphene.core.schema import Schema
|
|
||||||
from graphene.core.types import Int, Interface, ObjectType, String
|
|
||||||
|
|
||||||
|
|
||||||
class Character(Interface):
|
|
||||||
'''Character description'''
|
|
||||||
name = String()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
type_name = 'core_Character'
|
|
||||||
|
|
||||||
|
|
||||||
class Human(Character):
|
|
||||||
'''Human description'''
|
|
||||||
friends = String()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
type_name = 'core_Human'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def readonly_prop(self):
|
|
||||||
return 'readonly'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def write_prop(self):
|
|
||||||
return self._write_prop
|
|
||||||
|
|
||||||
@write_prop.setter
|
|
||||||
def write_prop(self, value):
|
|
||||||
self._write_prop = value
|
|
||||||
|
|
||||||
|
|
||||||
class Droid(Character):
|
|
||||||
'''Droid description'''
|
|
||||||
|
|
||||||
|
|
||||||
class CharacterType(Droid, Human):
|
|
||||||
'''Union Type'''
|
|
||||||
|
|
||||||
schema = Schema()
|
|
||||||
|
|
||||||
|
|
||||||
def test_interface():
|
|
||||||
object_type = schema.T(Character)
|
|
||||||
assert Character._meta.is_interface is True
|
|
||||||
assert isinstance(object_type, GraphQLInterfaceType)
|
|
||||||
assert Character._meta.type_name == 'core_Character'
|
|
||||||
assert object_type.description == 'Character description'
|
|
||||||
assert list(object_type.get_fields().keys()) == ['name']
|
|
||||||
|
|
||||||
|
|
||||||
def test_interface_cannot_initialize():
|
|
||||||
with raises(Exception) as excinfo:
|
|
||||||
Character()
|
|
||||||
assert 'An interface cannot be initialized' == str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_union():
|
|
||||||
object_type = schema.T(CharacterType)
|
|
||||||
assert CharacterType._meta.is_union is True
|
|
||||||
assert isinstance(object_type, GraphQLUnionType)
|
|
||||||
assert object_type.description == 'Union Type'
|
|
||||||
|
|
||||||
|
|
||||||
def test_union_cannot_initialize():
|
|
||||||
with raises(Exception) as excinfo:
|
|
||||||
CharacterType()
|
|
||||||
assert 'An union cannot be initialized' == str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_interface_resolve_type():
|
|
||||||
resolve_type = Character._resolve_type(schema, Human(object()))
|
|
||||||
assert isinstance(resolve_type, GraphQLObjectType)
|
|
||||||
|
|
||||||
|
|
||||||
def test_object_type():
|
|
||||||
object_type = schema.T(Human)
|
|
||||||
assert Human._meta.is_interface is False
|
|
||||||
assert Human._meta.type_name == 'core_Human'
|
|
||||||
assert isinstance(object_type, GraphQLObjectType)
|
|
||||||
assert object_type.description == 'Human description'
|
|
||||||
assert list(object_type.get_fields().keys()) == ['name', 'friends']
|
|
||||||
assert object_type.get_interfaces() == [schema.T(Character)]
|
|
||||||
assert Human._meta.fields_map['name'].object_type == Human
|
|
||||||
|
|
||||||
|
|
||||||
def test_object_type_container():
|
|
||||||
h = Human(name='My name')
|
|
||||||
assert h.name == 'My name'
|
|
||||||
|
|
||||||
|
|
||||||
def test_object_type_set_properties():
|
|
||||||
h = Human(readonly_prop='custom', write_prop='custom')
|
|
||||||
assert h.readonly_prop == 'readonly'
|
|
||||||
assert h.write_prop == 'custom'
|
|
||||||
|
|
||||||
|
|
||||||
def test_object_type_container_invalid_kwarg():
|
|
||||||
with raises(TypeError):
|
|
||||||
Human(invalid='My name')
|
|
||||||
|
|
||||||
|
|
||||||
def test_object_type_container_too_many_args():
|
|
||||||
with raises(IndexError):
|
|
||||||
Human('Peter', 'No friends :(', None)
|
|
||||||
|
|
||||||
|
|
||||||
def test_field_clashes():
|
|
||||||
with raises(Exception) as excinfo:
|
|
||||||
class Droid(Character):
|
|
||||||
name = Int()
|
|
||||||
|
|
||||||
assert 'clashes' in str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_fields_inherited_should_be_different():
|
|
||||||
assert Character._meta.fields_map['name'] != Human._meta.fields_map['name']
|
|
||||||
|
|
||||||
|
|
||||||
def test_field_mantain_resolver_tags():
|
|
||||||
class Droid(Character):
|
|
||||||
name = String()
|
|
||||||
|
|
||||||
def resolve_name(self, *args):
|
|
||||||
return 'My Droid'
|
|
||||||
|
|
||||||
tag_resolver(resolve_name, 'test')
|
|
||||||
|
|
||||||
field = schema.T(Droid._meta.fields_map['name'])
|
|
||||||
assert resolver_has_tag(field.resolver, 'test')
|
|
||||||
|
|
||||||
|
|
||||||
def test_type_has_nonnull():
|
|
||||||
class Droid(Character):
|
|
||||||
name = String()
|
|
||||||
|
|
||||||
assert Droid.NonNull.of_type == Droid
|
|
||||||
|
|
||||||
|
|
||||||
def test_type_has_list():
|
|
||||||
class Droid(Character):
|
|
||||||
name = String()
|
|
||||||
|
|
||||||
assert Droid.List.of_type == Droid
|
|
||||||
|
|
||||||
|
|
||||||
def test_abstracttype():
|
|
||||||
class MyObject1(ObjectType):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
name1 = String()
|
|
||||||
|
|
||||||
class MyObject2(ObjectType):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
name2 = String()
|
|
||||||
|
|
||||||
class MyObject(MyObject1, MyObject2):
|
|
||||||
pass
|
|
||||||
|
|
||||||
object_type = schema.T(MyObject)
|
|
||||||
|
|
||||||
assert list(MyObject._meta.fields_map.keys()) == ['name1', 'name2']
|
|
||||||
assert MyObject._meta.fields_map['name1'].object_type == MyObject
|
|
||||||
assert MyObject._meta.fields_map['name2'].object_type == MyObject
|
|
||||||
assert isinstance(object_type, GraphQLObjectType)
|
|
||||||
|
|
||||||
|
|
||||||
def test_abstracttype_initialize():
|
|
||||||
class MyAbstractObjectType(ObjectType):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
with raises(Exception) as excinfo:
|
|
||||||
MyAbstractObjectType()
|
|
||||||
|
|
||||||
assert 'An abstract ObjectType cannot be initialized' == str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_abstracttype_type():
|
|
||||||
class MyAbstractObjectType(ObjectType):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
with raises(Exception) as excinfo:
|
|
||||||
schema.T(MyAbstractObjectType)
|
|
||||||
|
|
||||||
assert 'Abstract ObjectTypes don\'t have a specific type.' == str(excinfo.value)
|
|
Loading…
Reference in New Issue
Block a user