mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-11-04 01:47:45 +03:00 
			
		
		
		
	First working version class types
This commit is contained in:
		
							parent
							
								
									e78936c424
								
							
						
					
					
						commit
						3363f588fe
					
				
							
								
								
									
										0
									
								
								graphene/core/classtypes/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene/core/classtypes/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										109
									
								
								graphene/core/classtypes/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								graphene/core/classtypes/base.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
from collections import OrderedDict
 | 
			
		||||
import inspect
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ..exceptions import SkipField
 | 
			
		||||
from .options import Options
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClassTypeMeta(type):
 | 
			
		||||
    options_class = Options
 | 
			
		||||
 | 
			
		||||
    def __new__(mcs, name, bases, attrs):
 | 
			
		||||
        super_new = super(ClassTypeMeta, mcs).__new__
 | 
			
		||||
 | 
			
		||||
        module = attrs.pop('__module__', None)
 | 
			
		||||
        doc = attrs.pop('__doc__', None)
 | 
			
		||||
        new_class = super_new(mcs, name, bases, {
 | 
			
		||||
            '__module__': module,
 | 
			
		||||
            '__doc__': doc
 | 
			
		||||
        })
 | 
			
		||||
        attr_meta = attrs.pop('Meta', None)
 | 
			
		||||
        if not attr_meta:
 | 
			
		||||
            meta = getattr(new_class, 'Meta', None)
 | 
			
		||||
        else:
 | 
			
		||||
            meta = attr_meta
 | 
			
		||||
 | 
			
		||||
        new_class.add_to_class('_meta', new_class.get_options(meta))
 | 
			
		||||
 | 
			
		||||
        return mcs.construct(new_class, bases, attrs)
 | 
			
		||||
 | 
			
		||||
    def get_options(cls, meta):
 | 
			
		||||
        return cls.options_class(meta)
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
    def construct(cls, bases, attrs):
 | 
			
		||||
        # Add all attributes to the class.
 | 
			
		||||
        for obj_name, obj in attrs.items():
 | 
			
		||||
            cls.add_to_class(obj_name, obj)
 | 
			
		||||
        return cls
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClassType(six.with_metaclass(ClassTypeMeta)):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def internal_type(cls, schema):
 | 
			
		||||
        raise NotImplementedError("Function internal_type not implemented in type {}".format(cls))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FieldsOptions(Options):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(FieldsOptions, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.local_fields = []
 | 
			
		||||
 | 
			
		||||
    def add_field(self, field):
 | 
			
		||||
        self.local_fields.append(field)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def fields(self):
 | 
			
		||||
        return sorted(self.local_fields)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def fields_map(self):
 | 
			
		||||
        return OrderedDict([(f.attname, f) for f in self.fields])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FieldsClassTypeMeta(ClassTypeMeta):
 | 
			
		||||
    options_class = FieldsOptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FieldsClassType(six.with_metaclass(FieldsClassTypeMeta, ClassType)):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def fields_internal_types(cls, schema):
 | 
			
		||||
        fields = []
 | 
			
		||||
        for field in cls._meta.fields:
 | 
			
		||||
            try:
 | 
			
		||||
                fields.append((field.name, schema.T(field)))
 | 
			
		||||
            except SkipField:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
        return OrderedDict(fields)
 | 
			
		||||
 | 
			
		||||
# class NamedClassType(ClassType):
 | 
			
		||||
#     pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# class UnionType(NamedClassType):
 | 
			
		||||
#     class Meta:
 | 
			
		||||
#         abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# class ObjectType(NamedClassType):
 | 
			
		||||
#     class Meta:
 | 
			
		||||
#         abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# class InputObjectType(NamedClassType):
 | 
			
		||||
#     class Meta:
 | 
			
		||||
#         abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# class Mutation(ObjectType):
 | 
			
		||||
#     class Meta:
 | 
			
		||||
#         abstract = True
 | 
			
		||||
							
								
								
									
										24
									
								
								graphene/core/classtypes/inputobjecttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								graphene/core/classtypes/inputobjecttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLInputObjectType
 | 
			
		||||
 | 
			
		||||
from .base import FieldsClassType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InputObjectType(FieldsClassType):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        raise Exception("An InputObjectType cannot be initialized")
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def internal_type(cls, schema):
 | 
			
		||||
        if cls._meta.abstract:
 | 
			
		||||
            raise Exception("Abstract InputObjectTypes don't have a specific type.")
 | 
			
		||||
 | 
			
		||||
        return GraphQLInputObjectType(
 | 
			
		||||
            cls._meta.type_name,
 | 
			
		||||
            description=cls._meta.description,
 | 
			
		||||
            fields=partial(cls.fields_internal_types, schema),
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										54
									
								
								graphene/core/classtypes/interface.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								graphene/core/classtypes/interface.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
import six
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLInterfaceType
 | 
			
		||||
 | 
			
		||||
from .base import FieldsClassTypeMeta
 | 
			
		||||
from .objecttype import ObjectType, ObjectTypeMeta
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InterfaceMeta(ObjectTypeMeta):
 | 
			
		||||
    def construct(cls, bases, attrs):
 | 
			
		||||
        if cls._meta.abstract or Interface in bases:
 | 
			
		||||
            # Return Interface type
 | 
			
		||||
            cls = FieldsClassTypeMeta.construct(cls, bases, attrs)
 | 
			
		||||
            setattr(cls._meta, 'interface', True)
 | 
			
		||||
            return cls
 | 
			
		||||
        else:
 | 
			
		||||
            # Return ObjectType class with all the inherited interfaces
 | 
			
		||||
            cls = super(InterfaceMeta, cls).construct(bases, attrs)
 | 
			
		||||
            for interface in bases:
 | 
			
		||||
                is_interface = issubclass(interface, Interface) and getattr(interface._meta, 'interface', False)
 | 
			
		||||
                if not is_interface:
 | 
			
		||||
                    continue
 | 
			
		||||
                cls._meta.interfaces.append(interface)
 | 
			
		||||
            return cls
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Interface(six.with_metaclass(InterfaceMeta, ObjectType)):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        if self._meta.interface:
 | 
			
		||||
            raise Exception("An interface cannot be initialized")
 | 
			
		||||
        return super(Interface, self).__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @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 Interfaces don't have a specific type.")
 | 
			
		||||
 | 
			
		||||
        if not cls._meta.interface:
 | 
			
		||||
            return super(Interface, cls).internal_type(schema)
 | 
			
		||||
 | 
			
		||||
        return GraphQLInterfaceType(
 | 
			
		||||
            cls._meta.type_name,
 | 
			
		||||
            description=cls._meta.description,
 | 
			
		||||
            resolve_type=partial(cls._resolve_type, schema),
 | 
			
		||||
            fields=partial(cls.fields_internal_types, schema)
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										30
									
								
								graphene/core/classtypes/mutation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								graphene/core/classtypes/mutation.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
import six
 | 
			
		||||
 | 
			
		||||
from ..types.argument import ArgumentsGroup
 | 
			
		||||
from .objecttype import ObjectType, ObjectTypeMeta
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MutationMeta(ObjectTypeMeta):
 | 
			
		||||
    def construct(cls, bases, attrs):
 | 
			
		||||
        input_class = attrs.pop('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))
 | 
			
		||||
        cls = super(MutationMeta, cls).construct(bases, attrs)
 | 
			
		||||
        return cls
 | 
			
		||||
 | 
			
		||||
    def construct_arguments(cls, items):
 | 
			
		||||
        return ArgumentsGroup(**items)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Mutation(six.with_metaclass(MutationMeta, ObjectType)):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_arguments(cls):
 | 
			
		||||
        return cls.arguments
 | 
			
		||||
							
								
								
									
										99
									
								
								graphene/core/classtypes/objecttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								graphene/core/classtypes/objecttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
import six
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLObjectType
 | 
			
		||||
 | 
			
		||||
from graphene import signals
 | 
			
		||||
from .base import FieldsOptions, FieldsClassType, FieldsClassTypeMeta
 | 
			
		||||
from .uniontype import UnionType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_objecttype(cls):
 | 
			
		||||
    if not issubclass(cls, ObjectType):
 | 
			
		||||
        return False
 | 
			
		||||
    return not cls._meta.interface
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectTypeOptions(FieldsOptions):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(ObjectTypeOptions, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.interface = False
 | 
			
		||||
        self.interfaces = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectTypeMeta(FieldsClassTypeMeta):
 | 
			
		||||
    def construct(cls, bases, attrs):
 | 
			
		||||
        cls = super(ObjectTypeMeta, cls).construct(bases, attrs)
 | 
			
		||||
        if not cls._meta.abstract:
 | 
			
		||||
            union_types = list(filter(is_objecttype, bases))
 | 
			
		||||
            if len(union_types) > 1:
 | 
			
		||||
                meta_attrs = dict(cls._meta.original_attrs, types=union_types)
 | 
			
		||||
                Meta = type('Meta', (object, ), meta_attrs)
 | 
			
		||||
                attrs['Meta'] = Meta
 | 
			
		||||
                attrs['__module__'] = cls.__module__
 | 
			
		||||
                attrs['__doc__'] = cls.__doc__
 | 
			
		||||
                return type(cls.__name__, (UnionType, ), attrs)
 | 
			
		||||
        return cls
 | 
			
		||||
 | 
			
		||||
    options_class = ObjectTypeOptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectType(six.with_metaclass(ObjectTypeMeta, FieldsClassType)):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    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 internal_type(cls, schema):
 | 
			
		||||
        if cls._meta.abstract:
 | 
			
		||||
            raise Exception("Abstract ObjectTypes don't have a specific type.")
 | 
			
		||||
 | 
			
		||||
        return GraphQLObjectType(
 | 
			
		||||
            cls._meta.type_name,
 | 
			
		||||
            description=cls._meta.description,
 | 
			
		||||
            interfaces=[schema.T(i) for i in cls._meta.interfaces],
 | 
			
		||||
            fields=partial(cls.fields_internal_types, schema),
 | 
			
		||||
            is_type_of=getattr(cls, 'is_type_of', None)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def wrap(cls, instance, args, info):
 | 
			
		||||
        return cls(_root=instance)
 | 
			
		||||
							
								
								
									
										47
									
								
								graphene/core/classtypes/options.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								graphene/core/classtypes/options.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
class Options(object):
 | 
			
		||||
    def __init__(self, meta=None, **defaults):
 | 
			
		||||
        self.meta = meta
 | 
			
		||||
        self.abstract = False
 | 
			
		||||
        for name, value in defaults.items():
 | 
			
		||||
            setattr(self, name, value)
 | 
			
		||||
        self.valid_attrs = list(defaults.keys()) + ['type_name', 'description', 'abstract']
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										21
									
								
								graphene/core/classtypes/scalar.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								graphene/core/classtypes/scalar.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
from graphql.core.type import GraphQLScalarType
 | 
			
		||||
 | 
			
		||||
from .base import ClassType
 | 
			
		||||
from ..types.base import MountedType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Scalar(ClassType, MountedType):
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def internal_type(cls, schema):
 | 
			
		||||
        serialize = getattr(cls, 'serialize')
 | 
			
		||||
        parse_literal = getattr(cls, 'parse_literal')
 | 
			
		||||
        parse_value = getattr(cls, 'parse_value')
 | 
			
		||||
 | 
			
		||||
        return GraphQLScalarType(
 | 
			
		||||
            name=cls._meta.type_name,
 | 
			
		||||
            description=cls._meta.description,
 | 
			
		||||
            serialize=serialize,
 | 
			
		||||
            parse_value=parse_value,
 | 
			
		||||
            parse_literal=parse_literal
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										0
									
								
								graphene/core/classtypes/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene/core/classtypes/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										40
									
								
								graphene/core/classtypes/tests/test_base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								graphene/core/classtypes/tests/test_base.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
from ..base import ClassType, FieldsClassType
 | 
			
		||||
from ...types import Field, String
 | 
			
		||||
from ...schema import Schema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_classtype_basic():
 | 
			
		||||
    class Character(ClassType):
 | 
			
		||||
        '''Character description'''
 | 
			
		||||
        pass
 | 
			
		||||
    assert Character._meta.type_name == 'Character'
 | 
			
		||||
    assert Character._meta.description == 'Character description'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_classtype_advanced():
 | 
			
		||||
    class Character(ClassType):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            type_name = 'OtherCharacter'
 | 
			
		||||
            description = 'OtherCharacter description'
 | 
			
		||||
    assert Character._meta.type_name == 'OtherCharacter'
 | 
			
		||||
    assert Character._meta.description == 'OtherCharacter description'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_fieldsclasstype():
 | 
			
		||||
    f = Field(String())
 | 
			
		||||
 | 
			
		||||
    class Character(FieldsClassType):
 | 
			
		||||
        field_name = f
 | 
			
		||||
 | 
			
		||||
    assert Character._meta.fields == [f]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_fieldsclasstype_fieldtype():
 | 
			
		||||
    f = Field(String())
 | 
			
		||||
 | 
			
		||||
    class Character(FieldsClassType):
 | 
			
		||||
        field_name = f
 | 
			
		||||
 | 
			
		||||
    schema = Schema(query=Character)
 | 
			
		||||
    assert Character.fields_internal_types(schema)['fieldName'] == schema.T(f)
 | 
			
		||||
    assert Character._meta.fields_map['field_name'] == f
 | 
			
		||||
							
								
								
									
										21
									
								
								graphene/core/classtypes/tests/test_inputobjecttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								graphene/core/classtypes/tests/test_inputobjecttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
from py.test import raises
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLInputObjectType
 | 
			
		||||
 | 
			
		||||
from graphene.core.schema import Schema
 | 
			
		||||
from graphene.core.types import String
 | 
			
		||||
from ..inputobjecttype import InputObjectType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_inputobjecttype():
 | 
			
		||||
    class InputCharacter(InputObjectType):
 | 
			
		||||
        '''InputCharacter description'''
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    schema = Schema()
 | 
			
		||||
 | 
			
		||||
    object_type = schema.T(InputCharacter)
 | 
			
		||||
    assert isinstance(object_type, GraphQLInputObjectType)
 | 
			
		||||
    assert InputCharacter._meta.type_name == 'InputCharacter'
 | 
			
		||||
    assert object_type.description == 'InputCharacter description'
 | 
			
		||||
    assert list(object_type.get_fields().keys()) == ['name']
 | 
			
		||||
							
								
								
									
										85
									
								
								graphene/core/classtypes/tests/test_interface.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								graphene/core/classtypes/tests/test_interface.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
from py.test import raises
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLInterfaceType, GraphQLObjectType
 | 
			
		||||
 | 
			
		||||
from graphene.core.schema import Schema
 | 
			
		||||
from graphene.core.types import String
 | 
			
		||||
from ..interface import Interface
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_interface():
 | 
			
		||||
    class Character(Interface):
 | 
			
		||||
        '''Character description'''
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    schema = Schema()
 | 
			
		||||
 | 
			
		||||
    object_type = schema.T(Character)
 | 
			
		||||
    assert issubclass(Character, Interface)
 | 
			
		||||
    assert isinstance(object_type, GraphQLInterfaceType)
 | 
			
		||||
    assert Character._meta.interface
 | 
			
		||||
    assert Character._meta.type_name == 'Character'
 | 
			
		||||
    assert object_type.description == 'Character description'
 | 
			
		||||
    assert list(object_type.get_fields().keys()) == ['name']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_interface_cannot_initialize():
 | 
			
		||||
    class Character(Interface):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    with raises(Exception) as excinfo:
 | 
			
		||||
        Character()
 | 
			
		||||
    assert 'An interface cannot be initialized' == str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_interface_inheritance_abstract():
 | 
			
		||||
    class Character(Interface):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    class ShouldBeInterface(Character):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            abstract = True
 | 
			
		||||
 | 
			
		||||
    class ShouldBeObjectType(ShouldBeInterface):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    assert ShouldBeInterface._meta.interface
 | 
			
		||||
    assert not ShouldBeObjectType._meta.interface
 | 
			
		||||
    assert issubclass(ShouldBeObjectType, ObjectType)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_interface_inheritance():
 | 
			
		||||
    class Character(Interface):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    class GeneralInterface(Interface):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    class ShouldBeObjectType(GeneralInterface, Character):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    schema = Schema()
 | 
			
		||||
 | 
			
		||||
    assert Character._meta.interface
 | 
			
		||||
    assert not ShouldBeObjectType._meta.interface
 | 
			
		||||
    assert issubclass(ShouldBeObjectType, ObjectType)
 | 
			
		||||
    assert Character in ShouldBeObjectType._meta.interfaces
 | 
			
		||||
    assert GeneralInterface in ShouldBeObjectType._meta.interfaces
 | 
			
		||||
    assert isinstance(schema.T(Character), GraphQLInterfaceType)
 | 
			
		||||
    assert isinstance(schema.T(ShouldBeObjectType), GraphQLObjectType)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_interface_inheritance_non_objects():
 | 
			
		||||
    class ComonClass(object):
 | 
			
		||||
        common_attr = True
 | 
			
		||||
 | 
			
		||||
    class Character(ComonClass, Interface):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    class ShouldBeObjectType(Character):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    assert Character._meta.interface
 | 
			
		||||
    assert Character.common_attr
 | 
			
		||||
    assert ShouldBeObjectType.common_attr
 | 
			
		||||
							
								
								
									
										27
									
								
								graphene/core/classtypes/tests/test_mutation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								graphene/core/classtypes/tests/test_mutation.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
from py.test import raises
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLObjectType
 | 
			
		||||
 | 
			
		||||
from graphene.core.schema import Schema
 | 
			
		||||
from graphene.core.types import String
 | 
			
		||||
from ..mutation import Mutation
 | 
			
		||||
from ...types.argument import ArgumentsGroup
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_mutation():
 | 
			
		||||
    class MyMutation(Mutation):
 | 
			
		||||
        '''MyMutation description'''
 | 
			
		||||
        class Input:
 | 
			
		||||
            arg_name = String()
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    schema = Schema()
 | 
			
		||||
 | 
			
		||||
    object_type = schema.T(MyMutation)
 | 
			
		||||
    assert MyMutation._meta.type_name == 'MyMutation'
 | 
			
		||||
    assert isinstance(object_type, GraphQLObjectType)
 | 
			
		||||
    assert object_type.description == 'MyMutation description'
 | 
			
		||||
    assert list(object_type.get_fields().keys()) == ['name']
 | 
			
		||||
    assert MyMutation._meta.fields_map['name'].object_type == MyMutation
 | 
			
		||||
    assert isinstance(MyMutation.arguments, ArgumentsGroup)
 | 
			
		||||
    assert 'argName' in MyMutation.arguments
 | 
			
		||||
							
								
								
									
										89
									
								
								graphene/core/classtypes/tests/test_objecttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								graphene/core/classtypes/tests/test_objecttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
from py.test import raises
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLObjectType
 | 
			
		||||
 | 
			
		||||
from graphene.core.schema import Schema
 | 
			
		||||
from graphene.core.types import Int, String
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
from ..uniontype import UnionType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_object_type():
 | 
			
		||||
    class Human(ObjectType):
 | 
			
		||||
        '''Human description'''
 | 
			
		||||
        name = String()
 | 
			
		||||
        friends = String()
 | 
			
		||||
 | 
			
		||||
    schema = Schema()
 | 
			
		||||
 | 
			
		||||
    object_type = schema.T(Human)
 | 
			
		||||
    assert Human._meta.type_name == 'Human'
 | 
			
		||||
    assert isinstance(object_type, GraphQLObjectType)
 | 
			
		||||
    assert object_type.description == 'Human description'
 | 
			
		||||
    assert list(object_type.get_fields().keys()) == ['name', 'friends']
 | 
			
		||||
    assert Human._meta.fields_map['name'].object_type == Human
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_object_type_container():
 | 
			
		||||
    class Human(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
        friends = String()
 | 
			
		||||
 | 
			
		||||
    h = Human(name='My name')
 | 
			
		||||
    assert h.name == 'My name'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_object_type_set_properties():
 | 
			
		||||
    class Human(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
        friends = String()
 | 
			
		||||
 | 
			
		||||
        @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
 | 
			
		||||
 | 
			
		||||
    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():
 | 
			
		||||
    class Human(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    with raises(TypeError):
 | 
			
		||||
        Human(invalid='My name')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_object_type_container_too_many_args():
 | 
			
		||||
    class Human(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    with raises(IndexError):
 | 
			
		||||
        Human('Peter', 'No friends :(', None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_object_type_union():
 | 
			
		||||
    class Human(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    class Pet(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    class Thing(Human, Pet):
 | 
			
		||||
        '''Thing union description'''
 | 
			
		||||
        my_attr = True
 | 
			
		||||
 | 
			
		||||
    assert issubclass(Thing, UnionType)
 | 
			
		||||
    assert Thing._meta.types == [Human, Pet]
 | 
			
		||||
    assert Thing._meta.type_name == 'Thing'
 | 
			
		||||
    assert Thing._meta.description == 'Thing union description'
 | 
			
		||||
    assert Thing.my_attr
 | 
			
		||||
							
								
								
									
										32
									
								
								graphene/core/classtypes/tests/test_scalar.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								graphene/core/classtypes/tests/test_scalar.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
from graphql.core.type import GraphQLScalarType
 | 
			
		||||
 | 
			
		||||
from ...schema import Schema
 | 
			
		||||
from ..scalar import Scalar
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_custom_scalar():
 | 
			
		||||
    import datetime
 | 
			
		||||
    from graphql.core.language import ast
 | 
			
		||||
 | 
			
		||||
    class DateTimeScalar(Scalar):
 | 
			
		||||
        '''DateTimeScalar Documentation'''
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def serialize(dt):
 | 
			
		||||
            return dt.isoformat()
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def parse_literal(node):
 | 
			
		||||
            if isinstance(node, ast.StringValue):
 | 
			
		||||
                return datetime.datetime.strptime(
 | 
			
		||||
                    node.value, "%Y-%m-%dT%H:%M:%S.%f")
 | 
			
		||||
 | 
			
		||||
        @staticmethod
 | 
			
		||||
        def parse_value(value):
 | 
			
		||||
            return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
 | 
			
		||||
 | 
			
		||||
    schema = Schema()
 | 
			
		||||
 | 
			
		||||
    scalar_type = schema.T(DateTimeScalar)
 | 
			
		||||
    assert isinstance(scalar_type, GraphQLScalarType)
 | 
			
		||||
    assert scalar_type.name == 'DateTimeScalar'
 | 
			
		||||
    assert scalar_type.description == 'DateTimeScalar Documentation'
 | 
			
		||||
							
								
								
									
										27
									
								
								graphene/core/classtypes/tests/test_uniontype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								graphene/core/classtypes/tests/test_uniontype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
from graphql.core.type import GraphQLUnionType
 | 
			
		||||
 | 
			
		||||
from graphene.core.schema import Schema
 | 
			
		||||
from graphene.core.types import String
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
from ..uniontype import UnionType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_uniontype():
 | 
			
		||||
    class Human(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    class Pet(ObjectType):
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
    class Thing(UnionType):
 | 
			
		||||
        '''Thing union description'''
 | 
			
		||||
        class Meta:
 | 
			
		||||
            types = [Human, Pet]
 | 
			
		||||
 | 
			
		||||
    schema = Schema()
 | 
			
		||||
 | 
			
		||||
    object_type = schema.T(Thing)
 | 
			
		||||
    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)]
 | 
			
		||||
							
								
								
									
										39
									
								
								graphene/core/classtypes/uniontype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								graphene/core/classtypes/uniontype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
import six
 | 
			
		||||
 | 
			
		||||
from graphql.core.type import GraphQLUnionType
 | 
			
		||||
 | 
			
		||||
from .base import FieldsOptions, FieldsClassType, FieldsClassTypeMeta
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnionTypeOptions(FieldsOptions):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(UnionTypeOptions, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.types = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnionTypeMeta(FieldsClassTypeMeta):
 | 
			
		||||
    options_class = UnionTypeOptions
 | 
			
		||||
 | 
			
		||||
    def get_options(cls, meta):
 | 
			
		||||
        return cls.options_class(meta, types=[])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnionType(six.with_metaclass(UnionTypeMeta, FieldsClassType)):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    @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.")
 | 
			
		||||
 | 
			
		||||
        return GraphQLUnionType(
 | 
			
		||||
            cls._meta.type_name,
 | 
			
		||||
            types=map(schema.T, cls._meta.types),
 | 
			
		||||
            resolve_type=cls._resolve_type,
 | 
			
		||||
            description=cls._meta.description,
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ from graphene import signals
 | 
			
		|||
 | 
			
		||||
from .types.base import BaseType
 | 
			
		||||
from .types.objecttype import BaseObjectType
 | 
			
		||||
from .classtypes.base import ClassType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GraphQLSchema(_GraphQLSchema):
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +43,7 @@ class Schema(object):
 | 
			
		|||
        if not object_type:
 | 
			
		||||
            return
 | 
			
		||||
        if inspect.isclass(object_type) and issubclass(
 | 
			
		||||
                object_type, BaseType) or isinstance(
 | 
			
		||||
                object_type, (BaseType, ClassType)) or isinstance(
 | 
			
		||||
                object_type, BaseType):
 | 
			
		||||
            if object_type not in self._types:
 | 
			
		||||
                internal_type = object_type.internal_type(self)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,9 @@ from functools import total_ordering
 | 
			
		|||
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from ..classtypes.base import FieldsClassType
 | 
			
		||||
from ..classtypes.inputobjecttype import InputObjectType as NewInputObjectType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseType(object):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -105,10 +108,10 @@ class FieldType(MirroredType):
 | 
			
		|||
 | 
			
		||||
    def contribute_to_class(self, cls, name):
 | 
			
		||||
        from ..types import BaseObjectType, InputObjectType
 | 
			
		||||
        if issubclass(cls, InputObjectType):
 | 
			
		||||
        if issubclass(cls, (InputObjectType, NewInputObjectType)):
 | 
			
		||||
            inputfield = self.as_inputfield()
 | 
			
		||||
            return inputfield.contribute_to_class(cls, name)
 | 
			
		||||
        elif issubclass(cls, BaseObjectType):
 | 
			
		||||
        elif issubclass(cls, (BaseObjectType, FieldsClassType)):
 | 
			
		||||
            field = self.as_field()
 | 
			
		||||
            return field.contribute_to_class(cls, name)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,8 @@ import six
 | 
			
		|||
from graphql.core.type import GraphQLField, GraphQLInputObjectField
 | 
			
		||||
 | 
			
		||||
from ...utils import to_camel_case
 | 
			
		||||
from ..classtypes.base import FieldsClassType
 | 
			
		||||
from ..classtypes.inputobjecttype import InputObjectType as NewInputObjectType
 | 
			
		||||
from ..types import BaseObjectType, InputObjectType
 | 
			
		||||
from .argument import ArgumentsGroup, snake_case_args
 | 
			
		||||
from .base import LazyType, MountType, OrderedType
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +34,7 @@ class Field(OrderedType):
 | 
			
		|||
 | 
			
		||||
    def contribute_to_class(self, cls, attname):
 | 
			
		||||
        assert issubclass(
 | 
			
		||||
            cls, BaseObjectType), 'Field {} cannot be mounted in {}'.format(
 | 
			
		||||
            cls, (BaseObjectType, FieldsClassType)), 'Field {} cannot be mounted in {}'.format(
 | 
			
		||||
            self, cls)
 | 
			
		||||
        if not self.name:
 | 
			
		||||
            self.name = to_camel_case(attname)
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +128,7 @@ class InputField(OrderedType):
 | 
			
		|||
 | 
			
		||||
    def contribute_to_class(self, cls, attname):
 | 
			
		||||
        assert issubclass(
 | 
			
		||||
            cls, InputObjectType), 'InputField {} cannot be mounted in {}'.format(
 | 
			
		||||
            cls, (InputObjectType, NewInputObjectType)), 'InputField {} cannot be mounted in {}'.format(
 | 
			
		||||
            self, cls)
 | 
			
		||||
        if not self.name:
 | 
			
		||||
            self.name = to_camel_case(attname)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user