mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-11-04 09:57:41 +03:00 
			
		
		
		
	Next types
This commit is contained in:
		
							parent
							
								
									04492600e5
								
							
						
					
					
						commit
						550ad386f0
					
				
							
								
								
									
										0
									
								
								graphene/new_types/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene/new_types/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										37
									
								
								graphene/new_types/abstracttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								graphene/new_types/abstracttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
import six
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
from ..utils.is_base_type import is_base_type
 | 
			
		||||
from .options import Options
 | 
			
		||||
 | 
			
		||||
from .utils import get_fields_in_type, attrs_without_fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def merge_fields_in_attrs(bases, attrs):
 | 
			
		||||
    for base in bases:
 | 
			
		||||
        if not issubclass(base, AbstractType):
 | 
			
		||||
            continue
 | 
			
		||||
        for name, field in base._meta.fields.items():
 | 
			
		||||
            if name in attrs:
 | 
			
		||||
                continue
 | 
			
		||||
            attrs[name] = field
 | 
			
		||||
    return attrs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractTypeMeta(type):
 | 
			
		||||
 | 
			
		||||
    def __new__(cls, name, bases, attrs):
 | 
			
		||||
        options = attrs.get('_meta', Options())
 | 
			
		||||
 | 
			
		||||
        attrs = merge_fields_in_attrs(bases, attrs)
 | 
			
		||||
        fields = get_fields_in_type(cls, attrs)
 | 
			
		||||
        options.fields = OrderedDict(sorted(fields, key=lambda f: f[1]))
 | 
			
		||||
 | 
			
		||||
        attrs = attrs_without_fields(attrs, fields)
 | 
			
		||||
        cls = type.__new__(cls, name, bases, dict(attrs, _meta=options))
 | 
			
		||||
 | 
			
		||||
        return cls
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractType(six.with_metaclass(AbstractTypeMeta)):
 | 
			
		||||
    pass
 | 
			
		||||
							
								
								
									
										70
									
								
								graphene/new_types/field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								graphene/new_types/field.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
# import inspect
 | 
			
		||||
from functools import partial
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
# from graphql.type import (GraphQLField, GraphQLInputObjectField)
 | 
			
		||||
# from graphql.utils.assert_valid_name import assert_valid_name
 | 
			
		||||
 | 
			
		||||
from ..utils.orderedtype import OrderedType
 | 
			
		||||
from .structures import NonNull
 | 
			
		||||
# from ..utils.str_converters import to_camel_case
 | 
			
		||||
# from .argument import to_arguments
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# class AbstractField(object):
 | 
			
		||||
 | 
			
		||||
#     @property
 | 
			
		||||
#     def name(self):
 | 
			
		||||
#         return self._name or self.attname and to_camel_case(self.attname)
 | 
			
		||||
 | 
			
		||||
#     @name.setter
 | 
			
		||||
#     def name(self, name):
 | 
			
		||||
#         if name is not None:
 | 
			
		||||
#             assert_valid_name(name)
 | 
			
		||||
#         self._name = name
 | 
			
		||||
 | 
			
		||||
#     @property
 | 
			
		||||
#     def type(self):
 | 
			
		||||
#         from ..utils.get_graphql_type import get_graphql_type
 | 
			
		||||
#         from .structures import NonNull
 | 
			
		||||
#         if inspect.isfunction(self._type):
 | 
			
		||||
#             _type = self._type()
 | 
			
		||||
#         else:
 | 
			
		||||
#             _type = self._type
 | 
			
		||||
 | 
			
		||||
#         if self.required:
 | 
			
		||||
#             return NonNull(_type)
 | 
			
		||||
#         return get_graphql_type(_type)
 | 
			
		||||
 | 
			
		||||
#     @type.setter
 | 
			
		||||
#     def type(self, type):
 | 
			
		||||
#         self._type = type
 | 
			
		||||
 | 
			
		||||
def source_resolver(source, root, args, context, info):
 | 
			
		||||
    resolved = getattr(root, source, None)
 | 
			
		||||
    if callable(resolved):
 | 
			
		||||
        return resolved()
 | 
			
		||||
    return resolved
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Field(OrderedType):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, type, args=None, resolver=None, source=None,
 | 
			
		||||
                 deprecation_reason=None, name=None, description=None,
 | 
			
		||||
                 required=False, _creation_counter=None, **extra_args):
 | 
			
		||||
        super(Field, self).__init__(_creation_counter=_creation_counter)
 | 
			
		||||
        self.name = name
 | 
			
		||||
        # self.attname = None
 | 
			
		||||
        # self.parent = None
 | 
			
		||||
        if required:
 | 
			
		||||
            type = NonNull(type)
 | 
			
		||||
        self.type = type
 | 
			
		||||
        self.args = args or OrderedDict()
 | 
			
		||||
        # self.args = to_arguments(args, extra_args)
 | 
			
		||||
        assert not (source and resolver), ('You cannot provide a source and a '
 | 
			
		||||
                                           'resolver in a Field at the same time.')
 | 
			
		||||
        if source:
 | 
			
		||||
            resolver = partial(source_resolver, source)
 | 
			
		||||
        self.resolver = resolver
 | 
			
		||||
        self.deprecation_reason = deprecation_reason
 | 
			
		||||
        self.description = description
 | 
			
		||||
							
								
								
									
										41
									
								
								graphene/new_types/interface.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								graphene/new_types/interface.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
import six
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
from ..utils.is_base_type import is_base_type
 | 
			
		||||
from .options import Options
 | 
			
		||||
 | 
			
		||||
from .utils import get_fields_in_type, attrs_without_fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InterfaceMeta(type):
 | 
			
		||||
 | 
			
		||||
    def __new__(cls, name, bases, attrs):
 | 
			
		||||
        # Also ensure initialization is only performed for subclasses of
 | 
			
		||||
        # ObjectType
 | 
			
		||||
        if not is_base_type(bases, InterfaceMeta):
 | 
			
		||||
            return type.__new__(cls, name, bases, attrs)
 | 
			
		||||
 | 
			
		||||
        options = Options(
 | 
			
		||||
            attrs.pop('Meta', None),
 | 
			
		||||
            name=name,
 | 
			
		||||
            description=attrs.get('__doc__'),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        fields = get_fields_in_type(Interface, attrs)
 | 
			
		||||
        options.fields = OrderedDict(sorted(fields, key=lambda f: f[1]))
 | 
			
		||||
 | 
			
		||||
        attrs = attrs_without_fields(attrs, fields)
 | 
			
		||||
        cls = super(InterfaceMeta, cls).__new__(cls, name, bases, dict(attrs, _meta=options))
 | 
			
		||||
 | 
			
		||||
        return cls
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Interface(six.with_metaclass(InterfaceMeta)):
 | 
			
		||||
    resolve_type = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        raise Exception("An interface cannot be intitialized")
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def implements(cls, objecttype):
 | 
			
		||||
        pass
 | 
			
		||||
							
								
								
									
										73
									
								
								graphene/new_types/objecttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								graphene/new_types/objecttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
import six
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
from ..utils.is_base_type import is_base_type
 | 
			
		||||
from .options import Options
 | 
			
		||||
 | 
			
		||||
from .utils import get_fields_in_type, attrs_without_fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectTypeMeta(type):
 | 
			
		||||
 | 
			
		||||
    def __new__(cls, name, bases, attrs):
 | 
			
		||||
        # Also ensure initialization is only performed for subclasses of
 | 
			
		||||
        # ObjectType
 | 
			
		||||
        if not is_base_type(bases, ObjectTypeMeta):
 | 
			
		||||
            return type.__new__(cls, name, bases, attrs)
 | 
			
		||||
 | 
			
		||||
        options = Options(
 | 
			
		||||
            attrs.pop('Meta', None),
 | 
			
		||||
            name=name,
 | 
			
		||||
            description=attrs.get('__doc__'),
 | 
			
		||||
            interfaces=(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        fields = get_fields_in_type(ObjectType, attrs)
 | 
			
		||||
        options.fields = OrderedDict(sorted(fields, key=lambda f: f[1]))
 | 
			
		||||
 | 
			
		||||
        attrs = attrs_without_fields(attrs, fields)
 | 
			
		||||
        cls = super(ObjectTypeMeta, cls).__new__(cls, name, bases, dict(attrs, _meta=options))
 | 
			
		||||
 | 
			
		||||
        return cls
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectType(six.with_metaclass(ObjectTypeMeta)):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        # GraphQL ObjectType acting as container
 | 
			
		||||
        args_len = len(args)
 | 
			
		||||
        fields = self._meta.fields.items()
 | 
			
		||||
        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, (name, field) in zip(args, fields_iter):
 | 
			
		||||
                setattr(self, name, val)
 | 
			
		||||
        else:
 | 
			
		||||
            for val, (name, field) in zip(args, fields_iter):
 | 
			
		||||
                setattr(self, name, val)
 | 
			
		||||
                kwargs.pop(name, None)
 | 
			
		||||
 | 
			
		||||
        for name, field in fields_iter:
 | 
			
		||||
            try:
 | 
			
		||||
                val = kwargs.pop(name)
 | 
			
		||||
                setattr(self, name, 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(
 | 
			
		||||
                    "'{}' is an invalid keyword argument for {}".format(
 | 
			
		||||
                        list(kwargs)[0],
 | 
			
		||||
                        self.__class__.__name__
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
							
								
								
									
										37
									
								
								graphene/new_types/options.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								graphene/new_types/options.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
import inspect
 | 
			
		||||
from ..utils.props import props
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Options(object):
 | 
			
		||||
    '''
 | 
			
		||||
    This is the class wrapper around Meta.
 | 
			
		||||
    It helps to validate and cointain the attributes inside
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, meta=None, **defaults):
 | 
			
		||||
        if meta:
 | 
			
		||||
            assert inspect.isclass(meta), (
 | 
			
		||||
                'Meta have to be a class, received "{}".'.format(repr(meta))
 | 
			
		||||
            )
 | 
			
		||||
        self.add_attrs_from_meta(meta, defaults)
 | 
			
		||||
 | 
			
		||||
    def add_attrs_from_meta(self, meta, defaults):
 | 
			
		||||
        meta_attrs = props(meta) if meta else {}
 | 
			
		||||
        for attr_name, value in defaults.items():
 | 
			
		||||
            if attr_name in meta_attrs:
 | 
			
		||||
                value = meta_attrs.pop(attr_name)
 | 
			
		||||
            elif hasattr(meta, attr_name):
 | 
			
		||||
                value = getattr(meta, attr_name)
 | 
			
		||||
            setattr(self, attr_name, value)
 | 
			
		||||
 | 
			
		||||
        # If meta_attrs is not empty, it implicit means
 | 
			
		||||
        # it received invalid attributes
 | 
			
		||||
        if meta_attrs:
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
                "Invalid attributes: {}".format(
 | 
			
		||||
                    ','.join(meta_attrs.keys())
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return '<Meta \n{} >'.format(props(self))
 | 
			
		||||
							
								
								
									
										23
									
								
								graphene/new_types/structures.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								graphene/new_types/structures.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
from .unmountedtype import UnmountedType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Structure(UnmountedType):
 | 
			
		||||
    '''
 | 
			
		||||
    A structure is a GraphQL type instance that
 | 
			
		||||
    wraps a main type with certain structure.
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, of_type, *args, **kwargs):
 | 
			
		||||
        super(Structure, self).__init__(*args, **kwargs)
 | 
			
		||||
        self.of_type = of_type
 | 
			
		||||
 | 
			
		||||
    def get_type(self):
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class List(Structure):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NonNull(Structure):
 | 
			
		||||
    pass
 | 
			
		||||
							
								
								
									
										0
									
								
								graphene/new_types/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene/new_types/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										99
									
								
								graphene/new_types/tests/test_abstracttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								graphene/new_types/tests/test_abstracttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
import pytest
 | 
			
		||||
 | 
			
		||||
from ..field import Field
 | 
			
		||||
from ..abstracttype import AbstractType
 | 
			
		||||
from ..unmountedtype import UnmountedType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyType(object):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyScalar(UnmountedType):
 | 
			
		||||
    def get_type(self):
 | 
			
		||||
        return MyType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_abstracttype_with_fields():
 | 
			
		||||
    class MyAbstractType(AbstractType):
 | 
			
		||||
        field = Field(MyType)
 | 
			
		||||
 | 
			
		||||
    assert 'field' in MyAbstractType._meta.fields
 | 
			
		||||
    assert isinstance(MyAbstractType._meta.fields['field'], Field)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_abstracttype_with_unmountedfields():
 | 
			
		||||
    class MyAbstractType(AbstractType):
 | 
			
		||||
        field = UnmountedType(MyType)
 | 
			
		||||
 | 
			
		||||
    assert 'field' in MyAbstractType._meta.fields
 | 
			
		||||
    assert isinstance(MyAbstractType._meta.fields['field'], UnmountedType)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_abstracttype_inheritance():
 | 
			
		||||
    class MyAbstractType1(AbstractType):
 | 
			
		||||
        field1 = UnmountedType(MyType)
 | 
			
		||||
 | 
			
		||||
    class MyAbstractType2(MyAbstractType1):
 | 
			
		||||
        field2 = UnmountedType(MyType)
 | 
			
		||||
 | 
			
		||||
    assert MyAbstractType2._meta.fields.keys() == ['field1', 'field2']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_ordered_fields_in_objecttype():
 | 
			
		||||
#     class MyObjectType(ObjectType):
 | 
			
		||||
#         b = Field(MyType)
 | 
			
		||||
#         a = Field(MyType)
 | 
			
		||||
#         field = MyScalar()
 | 
			
		||||
#         asa = Field(MyType)
 | 
			
		||||
 | 
			
		||||
#     assert list(MyObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_generate_objecttype_unmountedtype():
 | 
			
		||||
#     class MyObjectType(ObjectType):
 | 
			
		||||
#         field = MyScalar(MyType)
 | 
			
		||||
 | 
			
		||||
#     assert 'field' in MyObjectType._meta.fields
 | 
			
		||||
#     assert isinstance(MyObjectType._meta.fields['field'], Field)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_parent_container_get_fields():
 | 
			
		||||
#     assert list(Container._meta.fields.keys()) == ['field1', 'field2']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_objecttype_as_container_only_args():
 | 
			
		||||
#     container = Container("1", "2")
 | 
			
		||||
#     assert container.field1 == "1"
 | 
			
		||||
#     assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_objecttype_as_container_args_kwargs():
 | 
			
		||||
#     container = Container("1", field2="2")
 | 
			
		||||
#     assert container.field1 == "1"
 | 
			
		||||
#     assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_objecttype_as_container_few_kwargs():
 | 
			
		||||
#     container = Container(field2="2")
 | 
			
		||||
#     assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_objecttype_as_container_all_kwargs():
 | 
			
		||||
#     container = Container(field1="1", field2="2")
 | 
			
		||||
#     assert container.field1 == "1"
 | 
			
		||||
#     assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_objecttype_as_container_extra_args():
 | 
			
		||||
#     with pytest.raises(IndexError) as excinfo:
 | 
			
		||||
#         Container("1", "2", "3")
 | 
			
		||||
 | 
			
		||||
#     assert "Number of args exceeds number of fields" == str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_objecttype_as_container_invalid_kwargs():
 | 
			
		||||
#     with pytest.raises(TypeError) as excinfo:
 | 
			
		||||
#         Container(unexisting_field="3")
 | 
			
		||||
 | 
			
		||||
#     assert "'unexisting_field' is an invalid keyword argument for Container" == str(excinfo.value)
 | 
			
		||||
							
								
								
									
										55
									
								
								graphene/new_types/tests/test_field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								graphene/new_types/tests/test_field.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
import pytest
 | 
			
		||||
 | 
			
		||||
from ..field import Field
 | 
			
		||||
from ..structures import NonNull
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyInstance(object):
 | 
			
		||||
    value = 'value'
 | 
			
		||||
    value_func = staticmethod(lambda: 'value_func')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_basic():
 | 
			
		||||
    MyType = object()
 | 
			
		||||
    args = {}
 | 
			
		||||
    resolver = lambda: None
 | 
			
		||||
    deprecation_reason = 'Deprecated now'
 | 
			
		||||
    description = 'My Field'
 | 
			
		||||
    field = Field(
 | 
			
		||||
        MyType,
 | 
			
		||||
        name='name',
 | 
			
		||||
        args=args,
 | 
			
		||||
        resolver=resolver,
 | 
			
		||||
        description=description,
 | 
			
		||||
        deprecation_reason=deprecation_reason
 | 
			
		||||
    )
 | 
			
		||||
    assert field.name == 'name'
 | 
			
		||||
    assert field.args == args
 | 
			
		||||
    assert field.resolver == resolver
 | 
			
		||||
    assert field.deprecation_reason == deprecation_reason
 | 
			
		||||
    assert field.description == description
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_required():
 | 
			
		||||
    MyType = object()
 | 
			
		||||
    field = Field(MyType, required=True)
 | 
			
		||||
    assert isinstance(field.type, NonNull)
 | 
			
		||||
    assert field.type.of_type == MyType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_source():
 | 
			
		||||
    MyType = object()
 | 
			
		||||
    field = Field(MyType, source='value')
 | 
			
		||||
    assert field.resolver(MyInstance, {}, None, None) == MyInstance.value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_not_source_and_resolver():
 | 
			
		||||
    MyType = object()
 | 
			
		||||
    with pytest.raises(Exception) as exc_info:
 | 
			
		||||
        Field(MyType, source='value', resolver=lambda: None)
 | 
			
		||||
    assert str(exc_info.value) == 'You cannot provide a source and a resolver in a Field at the same time.'
 | 
			
		||||
 | 
			
		||||
def test_field_source_func():
 | 
			
		||||
    MyType = object()
 | 
			
		||||
    field = Field(MyType, source='value_func')
 | 
			
		||||
    assert field.resolver(MyInstance(), {}, None, None) == MyInstance.value_func()
 | 
			
		||||
							
								
								
									
										59
									
								
								graphene/new_types/tests/test_interface.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								graphene/new_types/tests/test_interface.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
import pytest
 | 
			
		||||
 | 
			
		||||
from ..field import Field
 | 
			
		||||
from ..interface import Interface
 | 
			
		||||
from ..unmountedtype import UnmountedType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyType(object):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyScalar(UnmountedType):
 | 
			
		||||
    def get_type(self):
 | 
			
		||||
        return MyType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_interface():
 | 
			
		||||
    class MyInterface(Interface):
 | 
			
		||||
        '''Documentation'''
 | 
			
		||||
 | 
			
		||||
    assert MyInterface._meta.name == "MyInterface"
 | 
			
		||||
    assert MyInterface._meta.description == "Documentation"
 | 
			
		||||
    assert MyInterface._meta.fields == {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_interface_with_meta():
 | 
			
		||||
    class MyInterface(Interface):
 | 
			
		||||
 | 
			
		||||
        class Meta:
 | 
			
		||||
            name = 'MyOtherInterface'
 | 
			
		||||
            description = 'Documentation'
 | 
			
		||||
 | 
			
		||||
    assert MyInterface._meta.name == "MyOtherInterface"
 | 
			
		||||
    assert MyInterface._meta.description == "Documentation"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_interface_with_fields():
 | 
			
		||||
    class MyInterface(Interface):
 | 
			
		||||
        field = Field(MyType)
 | 
			
		||||
 | 
			
		||||
    assert 'field' in MyInterface._meta.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_ordered_fields_in_interface():
 | 
			
		||||
    class MyInterface(Interface):
 | 
			
		||||
        b = Field(MyType)
 | 
			
		||||
        a = Field(MyType)
 | 
			
		||||
        field = MyScalar()
 | 
			
		||||
        asa = Field(MyType)
 | 
			
		||||
 | 
			
		||||
    assert list(MyInterface._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_interface_unmountedtype():
 | 
			
		||||
    class MyInterface(Interface):
 | 
			
		||||
        field = MyScalar(MyType)
 | 
			
		||||
 | 
			
		||||
    assert 'field' in MyInterface._meta.fields
 | 
			
		||||
    assert isinstance(MyInterface._meta.fields['field'], Field)
 | 
			
		||||
							
								
								
									
										108
									
								
								graphene/new_types/tests/test_objecttype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								graphene/new_types/tests/test_objecttype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,108 @@
 | 
			
		|||
import pytest
 | 
			
		||||
 | 
			
		||||
from ..field import Field
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
from ..unmountedtype import UnmountedType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyType(object):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(ObjectType):
 | 
			
		||||
    field1 = Field(MyType)
 | 
			
		||||
    field2 = Field(MyType)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyScalar(UnmountedType):
 | 
			
		||||
    def get_type(self):
 | 
			
		||||
        return MyType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_objecttype():
 | 
			
		||||
    class MyObjectType(ObjectType):
 | 
			
		||||
        '''Documentation'''
 | 
			
		||||
 | 
			
		||||
    assert MyObjectType._meta.name == "MyObjectType"
 | 
			
		||||
    assert MyObjectType._meta.description == "Documentation"
 | 
			
		||||
    assert MyObjectType._meta.interfaces == tuple()
 | 
			
		||||
    assert MyObjectType._meta.fields == {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_objecttype_with_meta():
 | 
			
		||||
    class MyObjectType(ObjectType):
 | 
			
		||||
 | 
			
		||||
        class Meta:
 | 
			
		||||
            name = 'MyOtherObjectType'
 | 
			
		||||
            description = 'Documentation'
 | 
			
		||||
            interfaces = (MyType, )
 | 
			
		||||
 | 
			
		||||
    assert MyObjectType._meta.name == "MyOtherObjectType"
 | 
			
		||||
    assert MyObjectType._meta.description == "Documentation"
 | 
			
		||||
    assert MyObjectType._meta.interfaces == (MyType, )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_objecttype_with_fields():
 | 
			
		||||
    class MyObjectType(ObjectType):
 | 
			
		||||
        field = Field(MyType)
 | 
			
		||||
 | 
			
		||||
    assert 'field' in MyObjectType._meta.fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_ordered_fields_in_objecttype():
 | 
			
		||||
    class MyObjectType(ObjectType):
 | 
			
		||||
        b = Field(MyType)
 | 
			
		||||
        a = Field(MyType)
 | 
			
		||||
        field = MyScalar()
 | 
			
		||||
        asa = Field(MyType)
 | 
			
		||||
 | 
			
		||||
    assert list(MyObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_objecttype_unmountedtype():
 | 
			
		||||
    class MyObjectType(ObjectType):
 | 
			
		||||
        field = MyScalar(MyType)
 | 
			
		||||
 | 
			
		||||
    assert 'field' in MyObjectType._meta.fields
 | 
			
		||||
    assert isinstance(MyObjectType._meta.fields['field'], Field)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_parent_container_get_fields():
 | 
			
		||||
    assert list(Container._meta.fields.keys()) == ['field1', 'field2']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_objecttype_as_container_only_args():
 | 
			
		||||
    container = Container("1", "2")
 | 
			
		||||
    assert container.field1 == "1"
 | 
			
		||||
    assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_objecttype_as_container_args_kwargs():
 | 
			
		||||
    container = Container("1", field2="2")
 | 
			
		||||
    assert container.field1 == "1"
 | 
			
		||||
    assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_objecttype_as_container_few_kwargs():
 | 
			
		||||
    container = Container(field2="2")
 | 
			
		||||
    assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_objecttype_as_container_all_kwargs():
 | 
			
		||||
    container = Container(field1="1", field2="2")
 | 
			
		||||
    assert container.field1 == "1"
 | 
			
		||||
    assert container.field2 == "2"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_objecttype_as_container_extra_args():
 | 
			
		||||
    with pytest.raises(IndexError) as excinfo:
 | 
			
		||||
        Container("1", "2", "3")
 | 
			
		||||
 | 
			
		||||
    assert "Number of args exceeds number of fields" == str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_objecttype_as_container_invalid_kwargs():
 | 
			
		||||
    with pytest.raises(TypeError) as excinfo:
 | 
			
		||||
        Container(unexisting_field="3")
 | 
			
		||||
 | 
			
		||||
    assert "'unexisting_field' is an invalid keyword argument for Container" == str(excinfo.value)
 | 
			
		||||
							
								
								
									
										60
									
								
								graphene/new_types/unmountedtype.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								graphene/new_types/unmountedtype.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
from ..utils.orderedtype import OrderedType
 | 
			
		||||
# from .argument import Argument
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnmountedType(OrderedType):
 | 
			
		||||
    '''
 | 
			
		||||
    This class acts a proxy for a Graphene Type, so it can be mounted
 | 
			
		||||
    as Field, InputField or Argument.
 | 
			
		||||
 | 
			
		||||
    Instead of writing
 | 
			
		||||
    >>> class MyObjectType(ObjectType):
 | 
			
		||||
    >>>     my_field = Field(String(), description='Description here')
 | 
			
		||||
 | 
			
		||||
    It let you write
 | 
			
		||||
    >>> class MyObjectType(ObjectType):
 | 
			
		||||
    >>>     my_field = String(description='Description here')
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super(UnmountedType, self).__init__()
 | 
			
		||||
        self.args = args
 | 
			
		||||
        self.kwargs = kwargs
 | 
			
		||||
 | 
			
		||||
    def get_type(self):
 | 
			
		||||
        raise NotImplementedError("get_type not implemented in {}".format(self))
 | 
			
		||||
 | 
			
		||||
    def as_field(self):
 | 
			
		||||
        '''
 | 
			
		||||
        Mount the UnmountedType as Field
 | 
			
		||||
        '''
 | 
			
		||||
        from .field import Field
 | 
			
		||||
        return Field(
 | 
			
		||||
            self.get_type(),
 | 
			
		||||
            *self.args,
 | 
			
		||||
            _creation_counter=self.creation_counter,
 | 
			
		||||
            **self.kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # def as_inputfield(self):
 | 
			
		||||
    #     '''
 | 
			
		||||
    #     Mount the UnmountedType as InputField
 | 
			
		||||
    #     '''
 | 
			
		||||
    #     return InputField(
 | 
			
		||||
    #         self.get_type(),
 | 
			
		||||
    #         *self.args,
 | 
			
		||||
    #         _creation_counter=self.creation_counter,
 | 
			
		||||
    #         **self.kwargs
 | 
			
		||||
    #     )
 | 
			
		||||
 | 
			
		||||
    # def as_argument(self):
 | 
			
		||||
    #     '''
 | 
			
		||||
    #     Mount the UnmountedType as Argument
 | 
			
		||||
    #     '''
 | 
			
		||||
    #     return Argument(
 | 
			
		||||
    #         self.get_type(),
 | 
			
		||||
    #         *self.args,
 | 
			
		||||
    #         _creation_counter=self.creation_counter,
 | 
			
		||||
    #         **self.kwargs
 | 
			
		||||
    #     )
 | 
			
		||||
							
								
								
									
										42
									
								
								graphene/new_types/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								graphene/new_types/utils.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
from .unmountedtype import UnmountedType
 | 
			
		||||
from .field import Field
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def unmounted_field_in_type(attname, unmounted_field, type):
 | 
			
		||||
    '''
 | 
			
		||||
    Mount the UnmountedType dinamically as Field or InputField
 | 
			
		||||
    depending on where mounted in.
 | 
			
		||||
 | 
			
		||||
    ObjectType -> Field
 | 
			
		||||
    InputObjectType -> InputField
 | 
			
		||||
    '''
 | 
			
		||||
    # from ..types.inputobjecttype import InputObjectType
 | 
			
		||||
    from ..new_types.objecttype import ObjectTypeMeta
 | 
			
		||||
    from ..new_types.interface import Interface
 | 
			
		||||
    from ..new_types.abstracttype import AbstractTypeMeta
 | 
			
		||||
 | 
			
		||||
    if issubclass(type, (ObjectTypeMeta, Interface)):
 | 
			
		||||
        return unmounted_field.as_field()
 | 
			
		||||
 | 
			
		||||
    elif issubclass(type, (AbstractTypeMeta)):
 | 
			
		||||
        return unmounted_field
 | 
			
		||||
    # elif issubclass(type, (InputObjectType)):
 | 
			
		||||
    #     return unmounted_field.as_inputfield()
 | 
			
		||||
 | 
			
		||||
    raise Exception(
 | 
			
		||||
        'Unmounted field "{}" cannot be mounted in {}.{}.'.format(
 | 
			
		||||
            unmounted_field, type, attname
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_fields_in_type(in_type, attrs):
 | 
			
		||||
    for attname, value in list(attrs.items()):
 | 
			
		||||
        if isinstance(value, (Field)):  # , InputField
 | 
			
		||||
            yield attname, value
 | 
			
		||||
        elif isinstance(value, UnmountedType):
 | 
			
		||||
            yield attname, unmounted_field_in_type(attname, value, in_type)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def attrs_without_fields(attrs, fields):
 | 
			
		||||
    return {k: v for k, v in attrs.items() if k not in fields}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user