From 2696ae9b735d1c2a6a17a2ca3365d3561ee09788 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 13 Aug 2016 13:47:10 -0700 Subject: [PATCH] Make new_types the deafult types --- graphene/new_types/__init__.py | 0 graphene/new_types/argument.py | 33 --- graphene/new_types/datetime.py | 39 --- graphene/new_types/enum.py | 54 ---- graphene/new_types/field.py | 46 ---- graphene/new_types/inputobjecttype.py | 36 --- graphene/new_types/interface.py | 42 --- graphene/new_types/mutation.py | 30 --- graphene/new_types/objecttype.py | 76 ------ graphene/new_types/options.py | 35 --- graphene/new_types/scalars.py | 155 ----------- graphene/new_types/schema.py | 112 -------- graphene/new_types/structures.py | 25 -- graphene/new_types/tests/__init__.py | 0 graphene/new_types/tests/test_enum.py | 73 ------ graphene/new_types/tests/test_field.py | 77 ------ .../new_types/tests/test_inputobjecttype.py | 83 ------ graphene/new_types/tests/test_interface.py | 82 ------ graphene/new_types/tests/test_mutation.py | 63 ----- graphene/new_types/tests/test_objecttype.py | 131 ---------- graphene/new_types/unmountedtype.py | 70 ----- graphene/types/__init__.py | 8 +- graphene/{new_types => types}/abstracttype.py | 0 graphene/types/argument.py | 66 +---- graphene/types/datetime.py | 5 + graphene/types/enum.py | 28 +- graphene/types/field.py | 243 +++--------------- graphene/{new_types => types}/inputfield.py | 0 graphene/types/inputobjecttype.py | 49 ++-- graphene/types/interface.py | 43 +++- graphene/types/mutation.py | 2 +- graphene/types/objecttype.py | 160 ++---------- graphene/types/options.py | 10 +- graphene/types/scalars.py | 149 +++++++++-- graphene/types/schema.py | 79 ++++-- graphene/types/structures.py | 25 +- .../tests/test_abstracttype.py | 0 graphene/types/tests/test_argument.py | 64 ----- .../tests/test_definition.py | 0 graphene/types/tests/test_enum.py | 46 ++-- graphene/types/tests/test_field.py | 108 ++++---- graphene/types/tests/test_inputobjecttype.py | 94 ++++--- graphene/types/tests/test_interface.py | 100 ++++--- graphene/types/tests/test_mutation.py | 59 ++--- graphene/types/tests/test_objecttype.py | 227 ++++------------ graphene/types/tests/test_options.py | 23 -- .../{new_types => types}/tests/test_query.py | 0 graphene/types/tests/test_scalars.py | 144 ----------- .../tests/test_scalars_serialization.py | 0 graphene/types/tests/test_schema.py | 96 ------- graphene/types/tests/test_structures.py | 51 ---- .../tests/test_typemap.py | 0 .../{new_types => types}/tests/test_union.py | 0 graphene/{new_types => types}/typemap.py | 0 graphene/{new_types => types}/union.py | 0 graphene/types/unmountedtype.py | 19 +- graphene/{new_types => types}/utils.py | 10 +- 57 files changed, 633 insertions(+), 2537 deletions(-) delete mode 100644 graphene/new_types/__init__.py delete mode 100644 graphene/new_types/argument.py delete mode 100644 graphene/new_types/datetime.py delete mode 100644 graphene/new_types/enum.py delete mode 100644 graphene/new_types/field.py delete mode 100644 graphene/new_types/inputobjecttype.py delete mode 100644 graphene/new_types/interface.py delete mode 100644 graphene/new_types/mutation.py delete mode 100644 graphene/new_types/objecttype.py delete mode 100644 graphene/new_types/options.py delete mode 100644 graphene/new_types/scalars.py delete mode 100644 graphene/new_types/schema.py delete mode 100644 graphene/new_types/structures.py delete mode 100644 graphene/new_types/tests/__init__.py delete mode 100644 graphene/new_types/tests/test_enum.py delete mode 100644 graphene/new_types/tests/test_field.py delete mode 100644 graphene/new_types/tests/test_inputobjecttype.py delete mode 100644 graphene/new_types/tests/test_interface.py delete mode 100644 graphene/new_types/tests/test_mutation.py delete mode 100644 graphene/new_types/tests/test_objecttype.py delete mode 100644 graphene/new_types/unmountedtype.py rename graphene/{new_types => types}/abstracttype.py (100%) rename graphene/{new_types => types}/inputfield.py (100%) rename graphene/{new_types => types}/tests/test_abstracttype.py (100%) delete mode 100644 graphene/types/tests/test_argument.py rename graphene/{new_types => types}/tests/test_definition.py (100%) delete mode 100644 graphene/types/tests/test_options.py rename graphene/{new_types => types}/tests/test_query.py (100%) delete mode 100644 graphene/types/tests/test_scalars.py rename graphene/{new_types => types}/tests/test_scalars_serialization.py (100%) delete mode 100644 graphene/types/tests/test_schema.py delete mode 100644 graphene/types/tests/test_structures.py rename graphene/{new_types => types}/tests/test_typemap.py (100%) rename graphene/{new_types => types}/tests/test_union.py (100%) rename graphene/{new_types => types}/typemap.py (100%) rename graphene/{new_types => types}/union.py (100%) rename graphene/{new_types => types}/utils.py (88%) diff --git a/graphene/new_types/__init__.py b/graphene/new_types/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/graphene/new_types/argument.py b/graphene/new_types/argument.py deleted file mode 100644 index b16aef47..00000000 --- a/graphene/new_types/argument.py +++ /dev/null @@ -1,33 +0,0 @@ -from collections import OrderedDict -from itertools import chain - -from ..utils.orderedtype import OrderedType - - -class Argument(OrderedType): - - def __init__(self, type, default_value=None, description=None, name=None, _creation_counter=None): - super(Argument, self).__init__(_creation_counter=_creation_counter) - self.name = name - self.type = type - self.default_value = default_value - self.description = description - - -def to_arguments(args, extra_args): - from .unmountedtype import UnmountedType - extra_args = sorted(extra_args.items(), key=lambda f: f[1]) - iter_arguments = chain(args.items() + extra_args) - arguments = OrderedDict() - for default_name, arg in iter_arguments: - if isinstance(arg, UnmountedType): - arg = arg.as_argument() - - if not isinstance(arg, Argument): - raise ValueError('Unknown argument "{}".'.format(default_name)) - - arg_name = default_name or arg.name - assert arg_name not in arguments, 'More than one Argument have same name "{}".'.format(arg.name) - arguments[arg_name] = arg - - return arguments diff --git a/graphene/new_types/datetime.py b/graphene/new_types/datetime.py deleted file mode 100644 index 9baa731e..00000000 --- a/graphene/new_types/datetime.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import absolute_import - -import datetime - -from graphql.language import ast - -from .scalars import Scalar - -try: - import iso8601 -except: - raise ImportError( - "iso8601 package is required for DateTime Scalar.\n" - "You can install it using: pip install iso8601." - ) - - -class DateTime(Scalar): - ''' - The `DateTime` scalar type represents a DateTime - value as specified by - [iso8601](https://en.wikipedia.org/wiki/ISO_8601). - ''' - - @staticmethod - def serialize(dt): - assert isinstance(dt, (datetime.datetime, datetime.date)), ( - 'Received not compatible datetime "{}"'.format(repr(dt)) - ) - return dt.isoformat() - - @staticmethod - def parse_literal(node): - if isinstance(node, ast.StringValue): - return iso8601.parse_date(node.value) - - @staticmethod - def parse_value(value): - return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f") diff --git a/graphene/new_types/enum.py b/graphene/new_types/enum.py deleted file mode 100644 index 02493b30..00000000 --- a/graphene/new_types/enum.py +++ /dev/null @@ -1,54 +0,0 @@ -from collections import OrderedDict - -import six - -from ..generators import generate_enum -from ..utils.is_base_type import is_base_type -from .options import Options -from .unmountedtype import UnmountedType - -try: - from enum import Enum as PyEnum -except ImportError: - from ..utils.enum import Enum as PyEnum - - -class EnumTypeMeta(type): - - def __new__(cls, name, bases, attrs): - # Also ensure initialization is only performed for subclasses of Model - # (excluding Model class itself). - if not is_base_type(bases, EnumTypeMeta): - return type.__new__(cls, name, bases, attrs) - - options = Options( - attrs.pop('Meta', None), - name=name, - description=attrs.get('__doc__'), - enum=None, - ) - if not options.enum: - options.enum = PyEnum(cls.__name__, attrs) - - new_attrs = OrderedDict(attrs, _meta=options, **options.enum.__members__) - return type.__new__(cls, name, bases, new_attrs) - - def __prepare__(name, bases, **kwargs): # noqa: N805 - return OrderedDict() - - def __call__(cls, *args, **kwargs): # noqa: N805 - if cls is Enum: - description = kwargs.pop('description', None) - return cls.from_enum(PyEnum(*args, **kwargs), description=description) - return super(EnumTypeMeta, cls).__call__(*args, **kwargs) - - def from_enum(cls, enum, description=None): - meta_class = type('Meta', (object,), {'enum': enum, 'description': description}) - return type(meta_class.enum.__name__, (Enum,), {'Meta': meta_class}) - - def __str__(cls): - return cls._meta.name - - -class Enum(six.with_metaclass(EnumTypeMeta, UnmountedType)): - pass diff --git a/graphene/new_types/field.py b/graphene/new_types/field.py deleted file mode 100644 index 883bbfdd..00000000 --- a/graphene/new_types/field.py +++ /dev/null @@ -1,46 +0,0 @@ -import inspect -from functools import partial -from collections import OrderedDict, Mapping - -from ..utils.orderedtype import OrderedType -from .structures import NonNull -from .argument import to_arguments - - -def source_resolver(source, root, args, context, info): - resolved = getattr(root, source, None) - if inspect.isfunction(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) - assert not args or isinstance(args, Mapping), ( - 'Arguments in a field have to be a mapping, received "{}".' - ).format(args) - assert not (source and resolver), ( - 'A Field cannot have a source and a resolver in at the same time.' - ) - - if required: - type = NonNull(type) - - self.name = name - self._type = type - self.args = to_arguments(args or OrderedDict(), extra_args) - if source: - resolver = partial(source_resolver, source) - self.resolver = resolver - self.deprecation_reason = deprecation_reason - self.description = description - - @property - def type(self): - if inspect.isfunction(self._type): - return self._type() - return self._type diff --git a/graphene/new_types/inputobjecttype.py b/graphene/new_types/inputobjecttype.py deleted file mode 100644 index aa8f24cc..00000000 --- a/graphene/new_types/inputobjecttype.py +++ /dev/null @@ -1,36 +0,0 @@ -import six - -from ..utils.is_base_type import is_base_type -from .options import Options - -from .abstracttype import AbstractTypeMeta -from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs - - -class InputObjectTypeMeta(AbstractTypeMeta): - - def __new__(cls, name, bases, attrs): - # Also ensure initialization is only performed for subclasses of - # InputObjectType - if not is_base_type(bases, InputObjectTypeMeta): - return type.__new__(cls, name, bases, attrs) - - options = Options( - attrs.pop('Meta', None), - name=name, - description=attrs.get('__doc__'), - ) - - attrs = merge_fields_in_attrs(bases, attrs) - options.fields = get_fields_in_type(InputObjectType, attrs) - yank_fields_from_attrs(attrs, options.fields) - - return type.__new__(cls, name, bases, dict(attrs, _meta=options)) - - def __str__(cls): - return cls._meta.name - - -class InputObjectType(six.with_metaclass(InputObjectTypeMeta)): - def __init__(self, *args, **kwargs): - raise Exception("An InputObjectType cannot be intitialized") diff --git a/graphene/new_types/interface.py b/graphene/new_types/interface.py deleted file mode 100644 index d6c1fe37..00000000 --- a/graphene/new_types/interface.py +++ /dev/null @@ -1,42 +0,0 @@ -import six - -from ..utils.is_base_type import is_base_type -from .options import Options - -from .abstracttype import AbstractTypeMeta -from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs - - -class InterfaceMeta(AbstractTypeMeta): - - def __new__(cls, name, bases, attrs): - # Also ensure initialization is only performed for subclasses of - # Interface - 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__'), - ) - - attrs = merge_fields_in_attrs(bases, attrs) - options.fields = get_fields_in_type(Interface, attrs) - yank_fields_from_attrs(attrs, options.fields) - - return type.__new__(cls, name, bases, dict(attrs, _meta=options)) - - def __str__(cls): - return cls._meta.name - - -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 diff --git a/graphene/new_types/mutation.py b/graphene/new_types/mutation.py deleted file mode 100644 index d54740ec..00000000 --- a/graphene/new_types/mutation.py +++ /dev/null @@ -1,30 +0,0 @@ -from functools import partial - -import six - -from ..utils.is_base_type import is_base_type -from ..utils.props import props -from .field import Field -from .objecttype import ObjectType, ObjectTypeMeta - - -class MutationMeta(ObjectTypeMeta): - - def __new__(cls, name, bases, attrs): - # Also ensure initialization is only performed for subclasses of - # Mutation - if not is_base_type(bases, MutationMeta): - return type.__new__(cls, name, bases, attrs) - - input_class = attrs.pop('Input', None) - - cls = ObjectTypeMeta.__new__(cls, name, bases, attrs) - field_args = props(input_class) if input_class else {} - resolver = getattr(cls, 'mutate', None) - assert resolver, 'All mutations must define a mutate method in it' - cls.Field = partial(Field, cls, args=field_args, resolver=resolver) - return cls - - -class Mutation(six.with_metaclass(MutationMeta, ObjectType)): - pass diff --git a/graphene/new_types/objecttype.py b/graphene/new_types/objecttype.py deleted file mode 100644 index 3c1c7791..00000000 --- a/graphene/new_types/objecttype.py +++ /dev/null @@ -1,76 +0,0 @@ -import six - -from ..utils.is_base_type import is_base_type -from .options import Options - -from .abstracttype import AbstractTypeMeta -from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs - - -class ObjectTypeMeta(AbstractTypeMeta): - - 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=(), - ) - - attrs = merge_fields_in_attrs(bases, attrs) - options.fields = get_fields_in_type(ObjectType, attrs) - yank_fields_from_attrs(attrs, options.fields) - - return type.__new__(cls, name, bases, dict(attrs, _meta=options)) - - def __str__(cls): - return cls._meta.name - - -class ObjectType(six.with_metaclass(ObjectTypeMeta)): - - is_type_of = None - - 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__ - ) - ) diff --git a/graphene/new_types/options.py b/graphene/new_types/options.py deleted file mode 100644 index 89c1eb5a..00000000 --- a/graphene/new_types/options.py +++ /dev/null @@ -1,35 +0,0 @@ -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)) - ) - - 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 ''.format(props(self)) diff --git a/graphene/new_types/scalars.py b/graphene/new_types/scalars.py deleted file mode 100644 index 838fbd2e..00000000 --- a/graphene/new_types/scalars.py +++ /dev/null @@ -1,155 +0,0 @@ -import six - -from graphql.language.ast import BooleanValue, FloatValue, IntValue, StringValue - -from ..utils.is_base_type import is_base_type -from .options import Options -from .unmountedtype import UnmountedType - - -class ScalarTypeMeta(type): - - def __new__(cls, name, bases, attrs): - super_new = super(ScalarTypeMeta, cls).__new__ - - # Also ensure initialization is only performed for subclasses of Model - # (excluding Model class itself). - if not is_base_type(bases, ScalarTypeMeta): - return super_new(cls, name, bases, attrs) - - options = Options( - attrs.pop('Meta', None), - name=name, - description=attrs.get('__doc__'), - ) - - return super_new(cls, name, bases, dict(attrs, _meta=options)) - - def __str__(cls): - return cls._meta.name - - -class Scalar(six.with_metaclass(ScalarTypeMeta, UnmountedType)): - serialize = None - parse_value = None - parse_literal = None - - @classmethod - def get_type(cls): - return cls - -# As per the GraphQL Spec, Integers are only treated as valid when a valid -# 32-bit signed integer, providing the broadest support across platforms. -# -# n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because -# they are internally represented as IEEE 754 doubles. -MAX_INT = 2147483647 -MIN_INT = -2147483648 - - -class Int(Scalar): - ''' - The `Int` scalar type represents non-fractional signed whole numeric - values. Int can represent values between -(2^53 - 1) and 2^53 - 1 since - represented in JSON as double-precision floating point numbers specified - by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). - ''' - - @staticmethod - def coerce_int(value): - try: - num = int(value) - except ValueError: - try: - num = int(float(value)) - except ValueError: - return None - if MIN_INT <= num <= MAX_INT: - return num - - serialize = coerce_int - parse_value = coerce_int - - @staticmethod - def parse_literal(ast): - if isinstance(ast, IntValue): - num = int(ast.value) - if MIN_INT <= num <= MAX_INT: - return num - - -class Float(Scalar): - ''' - The `Float` scalar type represents signed double-precision fractional - values as specified by - [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). - ''' - - @staticmethod - def coerce_float(value): - try: - return float(value) - except ValueError: - return None - - serialize = coerce_float - parse_value = coerce_float - - @staticmethod - def parse_literal(ast): - if isinstance(ast, (FloatValue, IntValue)): - return float(ast.value) - - -class String(Scalar): - ''' - The `String` scalar type represents textual data, represented as UTF-8 - character sequences. The String type is most often used by GraphQL to - represent free-form human-readable text. - ''' - - @staticmethod - def coerce_string(value): - if isinstance(value, bool): - return u'true' if value else u'false' - return six.text_type(value) - - serialize = coerce_string - parse_value = coerce_string - - @staticmethod - def parse_literal(ast): - if isinstance(ast, StringValue): - return ast.value - - -class Boolean(Scalar): - ''' - The `Boolean` scalar type represents `true` or `false`. - ''' - - serialize = bool - parse_value = bool - - @staticmethod - def parse_literal(ast): - if isinstance(ast, BooleanValue): - return ast.value - - -class ID(Scalar): - ''' - The `ID` scalar type represents a unique identifier, often used to - refetch an object or as key for a cache. The ID type appears in a JSON - response as a String; however, it is not intended to be human-readable. - When expected as an input type, any string (such as `"4"`) or integer - (such as `4`) input value will be accepted as an ID. - ''' - - serialize = str - parse_value = str - - @staticmethod - def parse_literal(ast): - if isinstance(ast, (StringValue, IntValue)): - return ast.value diff --git a/graphene/new_types/schema.py b/graphene/new_types/schema.py deleted file mode 100644 index 624ce9e1..00000000 --- a/graphene/new_types/schema.py +++ /dev/null @@ -1,112 +0,0 @@ -import inspect - -from graphql import GraphQLSchema, graphql, is_type -from graphql.utils.introspection_query import introspection_query -from graphql.utils.schema_printer import print_schema - - -from .objecttype import ObjectType -from .structures import List, NonNull -from .scalars import Scalar, String -# from ..utils.get_graphql_type import get_graphql_type - - -# from graphql.type.schema import assert_object_implements_interface - -# from collections import defaultdict - - -from graphql.type.directives import (GraphQLDirective, GraphQLIncludeDirective, - GraphQLSkipDirective) -from graphql.type.introspection import IntrospectionSchema -from .typemap import TypeMap, is_graphene_type - - -class Schema(GraphQLSchema): - - def __init__(self, query=None, mutation=None, subscription=None, directives=None, types=None, executor=None): - self._query = query - self._mutation = mutation - self._subscription = subscription - self.types = types - self._executor = executor - if directives is None: - directives = [ - GraphQLIncludeDirective, - GraphQLSkipDirective - ] - - assert all(isinstance(d, GraphQLDirective) for d in directives), \ - 'Schema directives must be List[GraphQLDirective] if provided but got: {}.'.format( - directives - ) - - self._directives = directives - initial_types = [ - query, - mutation, - subscription, - IntrospectionSchema - ] - if types: - initial_types += types - self._type_map = TypeMap(initial_types) - - def get_query_type(self): - return self.get_graphql_type(self._query) - - def get_mutation_type(self): - return self.get_graphql_type(self._mutation) - - def get_subscription_type(self): - return self.get_graphql_type(self._subscription) - - def get_graphql_type(self, _type): - if is_type(_type): - return _type - if is_graphene_type(_type): - graphql_type = self.get_type(_type._meta.name) - assert graphql_type, "Type {} not found in this schema.".format(_type._meta.name) - assert graphql_type.graphene_type == _type - return graphql_type - raise Exception("{} is not a valid GraphQL type.".format(_type)) - - def execute(self, request_string='', root_value=None, variable_values=None, - context_value=None, operation_name=None, executor=None): - return graphql( - schema=self, - request_string=request_string, - root_value=root_value, - context_value=context_value, - variable_values=variable_values, - operation_name=operation_name, - executor=executor or self._executor - ) - - def register(self, object_type): - self.types.append(object_type) - - def introspect(self): - return self.execute(introspection_query).data - - def __str__(self): - return print_schema(self) - - def lazy(self, _type): - return lambda: self.get_type(_type) - - # def rebuild(self): - # self._possible_type_map = defaultdict(set) - # self._type_map = self._build_type_map(self.types) - # # Keep track of all implementations by interface name. - # self._implementations = defaultdict(list) - # for type in self._type_map.values(): - # if isinstance(type, GraphQLObjectType): - # for interface in type.get_interfaces(): - # self._implementations[interface.name].append(type) - - # # Enforce correct interface implementations. - # for type in self._type_map.values(): - # if isinstance(type, GraphQLObjectType): - # for interface in type.get_interfaces(): - # assert_object_implements_interface(self, type, interface) diff --git a/graphene/new_types/structures.py b/graphene/new_types/structures.py deleted file mode 100644 index 41fcd5dd..00000000 --- a/graphene/new_types/structures.py +++ /dev/null @@ -1,25 +0,0 @@ -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): - def __str__(self): - return '[{}]'.format(self.of_type) - - -class NonNull(Structure): - def __str__(self): - return '{}!'.format(self.of_type) diff --git a/graphene/new_types/tests/__init__.py b/graphene/new_types/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/graphene/new_types/tests/test_enum.py b/graphene/new_types/tests/test_enum.py deleted file mode 100644 index c16cbc99..00000000 --- a/graphene/new_types/tests/test_enum.py +++ /dev/null @@ -1,73 +0,0 @@ -from ..enum import Enum, PyEnum - - -def test_enum_construction(): - class RGB(Enum): - '''Description''' - RED = 1 - GREEN = 2 - BLUE = 3 - - @property - def description(self): - return "Description {}".format(self.name) - - assert RGB._meta.name == 'RGB' - assert RGB._meta.description == 'Description' - - values = RGB._meta.enum.__members__.values() - assert sorted([v.name for v in values]) == [ - 'BLUE', - 'GREEN', - 'RED' - ] - assert sorted([v.description for v in values]) == [ - 'Description BLUE', - 'Description GREEN', - 'Description RED' - ] - - -def test_enum_construction_meta(): - class RGB(Enum): - class Meta: - name = 'RGBEnum' - description = 'Description' - RED = 1 - GREEN = 2 - BLUE = 3 - - assert RGB._meta.name == 'RGBEnum' - assert RGB._meta.description == 'Description' - - -def test_enum_instance_construction(): - RGB = Enum('RGB', 'RED,GREEN,BLUE') - - values = RGB._meta.enum.__members__.values() - assert sorted([v.name for v in values]) == [ - 'BLUE', - 'GREEN', - 'RED' - ] - - -def test_enum_from_builtin_enum(): - PyRGB = PyEnum('RGB', 'RED,GREEN,BLUE') - - RGB = Enum.from_enum(PyRGB) - assert RGB._meta.enum == PyRGB - assert RGB.RED - assert RGB.GREEN - assert RGB.BLUE - - -def test_enum_value_from_class(): - class RGB(Enum): - RED = 1 - GREEN = 2 - BLUE = 3 - - assert RGB.RED.value == 1 - assert RGB.GREEN.value == 2 - assert RGB.BLUE.value == 3 diff --git a/graphene/new_types/tests/test_field.py b/graphene/new_types/tests/test_field.py deleted file mode 100644 index 31fcf037..00000000 --- a/graphene/new_types/tests/test_field.py +++ /dev/null @@ -1,77 +0,0 @@ -import pytest - -from ..field import Field -from ..structures import NonNull -from ..argument import Argument - - -class MyInstance(object): - value = 'value' - value_func = staticmethod(lambda: 'value_func') - - -def test_field_basic(): - MyType = object() - args = {'my arg': Argument(True)} - 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_with_lazy_type(): - MyType = object() - field = Field(lambda: MyType) - assert field.type == MyType - - -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) == 'A Field cannot have a source and a resolver in 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() - - -def test_field_source_argument_as_kw(): - MyType = object() - field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False)) - assert field.args.keys() == ['b', 'c', 'a'] - assert isinstance(field.args['b'], Argument) - assert isinstance(field.args['b'].type, NonNull) - assert field.args['b'].type.of_type is True - assert isinstance(field.args['c'], Argument) - assert field.args['c'].type is None - assert isinstance(field.args['a'], Argument) - assert isinstance(field.args['a'].type, NonNull) - assert field.args['a'].type.of_type is False diff --git a/graphene/new_types/tests/test_inputobjecttype.py b/graphene/new_types/tests/test_inputobjecttype.py deleted file mode 100644 index 52796749..00000000 --- a/graphene/new_types/tests/test_inputobjecttype.py +++ /dev/null @@ -1,83 +0,0 @@ -import pytest - -from ..field import Field -from ..inputfield import InputField -from ..inputobjecttype import InputObjectType -from ..unmountedtype import UnmountedType -from ..abstracttype import AbstractType - - -class MyType(object): - pass - - -class MyScalar(UnmountedType): - def get_type(self): - return MyType - - -def test_generate_inputobjecttype(): - class MyInputObjectType(InputObjectType): - '''Documentation''' - - assert MyInputObjectType._meta.name == "MyInputObjectType" - assert MyInputObjectType._meta.description == "Documentation" - assert MyInputObjectType._meta.fields == {} - - -def test_generate_inputobjecttype_with_meta(): - class MyInputObjectType(InputObjectType): - - class Meta: - name = 'MyOtherInputObjectType' - description = 'Documentation' - - assert MyInputObjectType._meta.name == "MyOtherInputObjectType" - assert MyInputObjectType._meta.description == "Documentation" - - -def test_generate_inputobjecttype_with_fields(): - class MyInputObjectType(InputObjectType): - field = Field(MyType) - - assert 'field' in MyInputObjectType._meta.fields - - -def test_ordered_fields_in_inputobjecttype(): - class MyInputObjectType(InputObjectType): - b = InputField(MyType) - a = InputField(MyType) - field = MyScalar() - asa = InputField(MyType) - - assert list(MyInputObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa'] - - -def test_generate_inputobjecttype_unmountedtype(): - class MyInputObjectType(InputObjectType): - field = MyScalar(MyType) - - assert 'field' in MyInputObjectType._meta.fields - assert isinstance(MyInputObjectType._meta.fields['field'], InputField) - - -def test_generate_inputobjecttype_inherit_abstracttype(): - class MyAbstractType(AbstractType): - field1 = MyScalar(MyType) - - class MyInputObjectType(InputObjectType, MyAbstractType): - field2 = MyScalar(MyType) - - assert MyInputObjectType._meta.fields.keys() == ['field1', 'field2'] - assert [type(x) for x in MyInputObjectType._meta.fields.values()] == [InputField, InputField] - - -def test_generate_inputobjecttype_inherit_abstracttype_reversed(): - class MyAbstractType(AbstractType): - field1 = MyScalar(MyType) - - class MyInputObjectType(MyAbstractType, InputObjectType): - field2 = MyScalar(MyType) - - assert MyInputObjectType._meta.fields.keys() == ['field1', 'field2'] - assert [type(x) for x in MyInputObjectType._meta.fields.values()] == [InputField, InputField] diff --git a/graphene/new_types/tests/test_interface.py b/graphene/new_types/tests/test_interface.py deleted file mode 100644 index 97804c5c..00000000 --- a/graphene/new_types/tests/test_interface.py +++ /dev/null @@ -1,82 +0,0 @@ -import pytest - -from ..field import Field -from ..interface import Interface -from ..unmountedtype import UnmountedType -from ..abstracttype import AbstractType - - -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() - - assert 'field' in MyInterface._meta.fields - assert isinstance(MyInterface._meta.fields['field'], Field) - - -def test_generate_interface_inherit_abstracttype(): - class MyAbstractType(AbstractType): - field1 = MyScalar() - - class MyInterface(Interface, MyAbstractType): - field2 = MyScalar() - - assert MyInterface._meta.fields.keys() == ['field1', 'field2'] - assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] - - -def test_generate_interface_inherit_abstracttype_reversed(): - class MyAbstractType(AbstractType): - field1 = MyScalar() - - class MyInterface(MyAbstractType, Interface): - field2 = MyScalar() - - assert MyInterface._meta.fields.keys() == ['field1', 'field2'] - assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] diff --git a/graphene/new_types/tests/test_mutation.py b/graphene/new_types/tests/test_mutation.py deleted file mode 100644 index 2b4a8859..00000000 --- a/graphene/new_types/tests/test_mutation.py +++ /dev/null @@ -1,63 +0,0 @@ -import pytest - -from ..field import Field -from ..mutation import Mutation -from ..objecttype import ObjectType -from ..scalars import String - - -def test_generate_mutation_no_args(): - class MyMutation(Mutation): - '''Documentation''' - @classmethod - def mutate(cls, *args, **kwargs): - pass - - assert issubclass(MyMutation, ObjectType) - assert MyMutation._meta.name == "MyMutation" - assert MyMutation._meta.description == "Documentation" - assert MyMutation.Field().resolver == MyMutation.mutate - - -# def test_generate_mutation_with_args(): -# class MyMutation(Mutation): -# '''Documentation''' -# class Input: -# s = String() - -# @classmethod -# def mutate(cls, *args, **kwargs): -# pass - -# graphql_type = MyMutation._meta.graphql_type -# field = MyMutation.Field() -# assert graphql_type.name == "MyMutation" -# assert graphql_type.description == "Documentation" -# assert isinstance(field, Field) -# assert field.type == MyMutation._meta.graphql_type -# assert 's' in field.args -# assert field.args['s'].type == String - - -def test_generate_mutation_with_meta(): - class MyMutation(Mutation): - - class Meta: - name = 'MyOtherMutation' - description = 'Documentation' - - @classmethod - def mutate(cls, *args, **kwargs): - pass - - assert MyMutation._meta.name == "MyOtherMutation" - assert MyMutation._meta.description == "Documentation" - assert MyMutation.Field().resolver == MyMutation.mutate - - -def test_mutation_raises_exception_if_no_mutate(): - with pytest.raises(AssertionError) as excinfo: - class MyMutation(Mutation): - pass - - assert "All mutations must define a mutate method in it" == str(excinfo.value) diff --git a/graphene/new_types/tests/test_objecttype.py b/graphene/new_types/tests/test_objecttype.py deleted file mode 100644 index 9b8b3913..00000000 --- a/graphene/new_types/tests/test_objecttype.py +++ /dev/null @@ -1,131 +0,0 @@ -import pytest - -from ..field import Field -from ..objecttype import ObjectType -from ..unmountedtype import UnmountedType -from ..abstracttype import AbstractType - - -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_inherit_abstracttype(): - class MyAbstractType(AbstractType): - field1 = MyScalar() - - class MyObjectType(ObjectType, MyAbstractType): - field2 = MyScalar() - - assert MyObjectType._meta.fields.keys() == ['field1', 'field2'] - assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field] - - -def test_generate_objecttype_inherit_abstracttype_reversed(): - class MyAbstractType(AbstractType): - field1 = MyScalar() - - class MyObjectType(MyAbstractType, ObjectType): - field2 = MyScalar() - - assert MyObjectType._meta.fields.keys() == ['field1', 'field2'] - assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field] - - -def test_generate_objecttype_unmountedtype(): - class MyObjectType(ObjectType): - field = MyScalar() - - 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) diff --git a/graphene/new_types/unmountedtype.py b/graphene/new_types/unmountedtype.py deleted file mode 100644 index ac813e8e..00000000 --- a/graphene/new_types/unmountedtype.py +++ /dev/null @@ -1,70 +0,0 @@ -from ..utils.orderedtype import OrderedType - - -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 - ''' - from .inputfield import InputField - return InputField( - self.get_type(), - *self.args, - _creation_counter=self.creation_counter, - **self.kwargs - ) - - def as_argument(self): - ''' - Mount the UnmountedType as Argument - ''' - from .argument import Argument - return Argument( - self.get_type(), - *self.args, - _creation_counter=self.creation_counter, - **self.kwargs - ) - - def __eq__(self, other): - return ( - self is other or ( - isinstance(other, UnmountedType) and - self.get_type() == other.get_type() and - self.args == other.args and - self.kwargs == other.kwargs - ) - ) diff --git a/graphene/types/__init__.py b/graphene/types/__init__.py index e43ad73a..9523d7fd 100644 --- a/graphene/types/__init__.py +++ b/graphene/types/__init__.py @@ -1,13 +1,17 @@ -from .objecttype import ObjectType, Interface +from .objecttype import ObjectType +from .abstracttype import AbstractType +from .interface import Interface from .scalars import Scalar, String, ID, Int, Float, Boolean from .schema import Schema from .structures import List, NonNull from .enum import Enum -from .field import Field, InputField +from .field import Field +from .inputfield import InputField from .argument import Argument from .inputobjecttype import InputObjectType __all__ = [ + 'AbstractType', 'ObjectType', 'InputObjectType', 'Interface', diff --git a/graphene/new_types/abstracttype.py b/graphene/types/abstracttype.py similarity index 100% rename from graphene/new_types/abstracttype.py rename to graphene/types/abstracttype.py diff --git a/graphene/types/argument.py b/graphene/types/argument.py index fa816a5f..b16aef47 100644 --- a/graphene/types/argument.py +++ b/graphene/types/argument.py @@ -1,77 +1,33 @@ -import inspect from collections import OrderedDict from itertools import chain -from graphql.type.definition import GraphQLArgument, GraphQLArgumentDefinition -from graphql.utils.assert_valid_name import assert_valid_name - from ..utils.orderedtype import OrderedType -from ..utils.str_converters import to_camel_case -class Argument(GraphQLArgument, OrderedType): +class Argument(OrderedType): def __init__(self, type, default_value=None, description=None, name=None, _creation_counter=None): + super(Argument, self).__init__(_creation_counter=_creation_counter) self.name = name self.type = type self.default_value = default_value self.description = description - OrderedType.__init__(self, _creation_counter) - - @property - def name(self): - return self._name - - @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 - if inspect.isfunction(self._type): - return get_graphql_type(self._type()) - return get_graphql_type(self._type) - - @type.setter - def type(self, type): - self._type = type - - @classmethod - def copy_from(cls, argument): - if isinstance(argument, (GraphQLArgumentDefinition, Argument)): - name = argument.name - else: - name = None - return cls( - type=argument.type, - default_value=argument.default_value, - description=argument.description, - name=name, - _creation_counter=argument.creation_counter if isinstance(argument, Argument) else None, - ) -def to_arguments(*args, **extra): +def to_arguments(args, extra_args): from .unmountedtype import UnmountedType - args = list(filter(None, args)) + [extra] - arguments = [] - iter_arguments = chain(*[arg.items() for arg in args]) - arguments_names = set() + extra_args = sorted(extra_args.items(), key=lambda f: f[1]) + iter_arguments = chain(args.items() + extra_args) + arguments = OrderedDict() for default_name, arg in iter_arguments: if isinstance(arg, UnmountedType): arg = arg.as_argument() - if not isinstance(arg, GraphQLArgument): + if not isinstance(arg, Argument): raise ValueError('Unknown argument "{}".'.format(default_name)) - arg = Argument.copy_from(arg) - arg.name = arg.name or default_name and to_camel_case(default_name) - assert arg.name, 'All arguments must have a name.' - assert arg.name not in arguments_names, 'More than one Argument have same name "{}".'.format(arg.name) - arguments.append(arg) - arguments_names.add(arg.name) + arg_name = default_name or arg.name + assert arg_name not in arguments, 'More than one Argument have same name "{}".'.format(arg.name) + arguments[arg_name] = arg - return OrderedDict([(a.name, a) for a in sorted(arguments)]) + return arguments diff --git a/graphene/types/datetime.py b/graphene/types/datetime.py index bdf5870e..9baa731e 100644 --- a/graphene/types/datetime.py +++ b/graphene/types/datetime.py @@ -16,6 +16,11 @@ except: class DateTime(Scalar): + ''' + The `DateTime` scalar type represents a DateTime + value as specified by + [iso8601](https://en.wikipedia.org/wiki/ISO_8601). + ''' @staticmethod def serialize(dt): diff --git a/graphene/types/enum.py b/graphene/types/enum.py index 0f8ba122..02493b30 100644 --- a/graphene/types/enum.py +++ b/graphene/types/enum.py @@ -16,31 +16,22 @@ except ImportError: class EnumTypeMeta(type): def __new__(cls, name, bases, attrs): - super_new = type.__new__ - # Also ensure initialization is only performed for subclasses of Model # (excluding Model class itself). if not is_base_type(bases, EnumTypeMeta): - return super_new(cls, name, bases, attrs) + return type.__new__(cls, name, bases, attrs) options = Options( attrs.pop('Meta', None), - name=None, - description=None, + name=name, + description=attrs.get('__doc__'), enum=None, - graphql_type=None ) if not options.enum: options.enum = PyEnum(cls.__name__, attrs) new_attrs = OrderedDict(attrs, _meta=options, **options.enum.__members__) - - cls = super_new(cls, name, bases, new_attrs) - - if not options.graphql_type: - options.graphql_type = generate_enum(cls) - - return cls + return type.__new__(cls, name, bases, new_attrs) def __prepare__(name, bases, **kwargs): # noqa: N805 return OrderedDict() @@ -51,10 +42,13 @@ class EnumTypeMeta(type): return cls.from_enum(PyEnum(*args, **kwargs), description=description) return super(EnumTypeMeta, cls).__call__(*args, **kwargs) - -class Enum(six.with_metaclass(EnumTypeMeta, UnmountedType)): - - @classmethod def from_enum(cls, enum, description=None): meta_class = type('Meta', (object,), {'enum': enum, 'description': description}) return type(meta_class.enum.__name__, (Enum,), {'Meta': meta_class}) + + def __str__(cls): + return cls._meta.name + + +class Enum(six.with_metaclass(EnumTypeMeta, UnmountedType)): + pass diff --git a/graphene/types/field.py b/graphene/types/field.py index 2f687381..883bbfdd 100644 --- a/graphene/types/field.py +++ b/graphene/types/field.py @@ -1,221 +1,46 @@ import inspect -from collections import OrderedDict - -from graphql.type import (GraphQLField, GraphQLFieldDefinition, - GraphQLInputObjectField) -from graphql.utils.assert_valid_name import assert_valid_name +from functools import partial +from collections import OrderedDict, Mapping from ..utils.orderedtype import OrderedType -from ..utils.str_converters import to_camel_case +from .structures import NonNull from .argument import to_arguments -class AbstractField(object): +def source_resolver(source, root, args, context, info): + resolved = getattr(root, source, None) + if inspect.isfunction(resolved): + return resolved() + return resolved - @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 +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) + assert not args or isinstance(args, Mapping), ( + 'Arguments in a field have to be a mapping, received "{}".' + ).format(args) + assert not (source and resolver), ( + 'A Field cannot have a source and a resolver in at the same time.' + ) + + if required: + type = NonNull(type) + + self.name = name + self._type = type + self.args = to_arguments(args or OrderedDict(), extra_args) + if source: + resolver = partial(source_resolver, source) + self.resolver = resolver + self.deprecation_reason = deprecation_reason + self.description = description @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 - - -class Field(AbstractField, GraphQLField, 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): - self.name = name - self.attname = None - self.parent = None - self.type = type - self.args = to_arguments(args, extra_args) - assert not (source and resolver), ('You cannot have a source ' - 'and a resolver at the same time') - - self.resolver = resolver - self.source = source - self.required = required - self.deprecation_reason = deprecation_reason - self.description = description - OrderedType.__init__(self, _creation_counter=_creation_counter) - - def mount_error_message(self, where): - return 'Field "{}" can only be mounted in ObjectType or Interface, received {}.'.format( - self, - where.__name__ - ) - - def mount(self, parent, attname=None): - from .objecttype import ObjectType - from .interface import Interface - assert issubclass(parent, (ObjectType, Interface)), self.mount_error_message(parent) - - self.attname = attname - self.parent = parent - - def default_resolver(self, root, args, context, info): - return getattr(root, self.source or self.attname, None) - - @property - def resolver(self): - resolver = getattr(self.parent, 'resolve_{}'.format(self.attname), None) - - # We try to get the resolver from the interfaces - # This is not needed anymore as Interfaces could be extended now with Python syntax - # if not resolver and issubclass(self.parent, ObjectType): - # graphql_type = self.parent._meta.graphql_type - # interfaces = graphql_type._provided_interfaces or [] - # for interface in interfaces: - # if not isinstance(interface, GrapheneInterfaceType): - # continue - # fields = interface.get_fields() - # if self.attname in fields: - # resolver = getattr(interface.graphene_type, 'resolve_{}'.format(self.attname), None) - # if resolver: - # # We remove the bounding to the method - # resolver = resolver #.__func__ - # break - - if resolver: - resolver = getattr(resolver, '__func__', resolver) - else: - resolver = self.default_resolver - - # def resolver_wrapper(root, *args, **kwargs): - # if not isinstance(root, self.parent): - # root = self.parent() - # return resolver(root, *args, **kwargs) - - return self._resolver or resolver # resolver_wrapper - - @resolver.setter - def resolver(self, resolver): - self._resolver = resolver - - def __copy__(self): - return self.copy_and_extend(self) - - @classmethod - def copy_and_extend( - cls, field, type=None, args=None, resolver=None, source=None, deprecation_reason=None, name=None, - description=None, required=False, _creation_counter=False, parent=None, attname=None, **extra_args): - if isinstance(field, Field): - type = type or field._type - resolver = resolver or field._resolver - source = source or field.source - name = name or field._name - required = required or field.required - _creation_counter = field.creation_counter if _creation_counter is False else None - attname = attname or field.attname - parent = parent or field.parent - args = to_arguments(args, field.args) - else: - # If is a GraphQLField - type = type or field.type - resolver = resolver or field.resolver - field_args = field.args - if isinstance(field, GraphQLFieldDefinition): - name = name or field.name - field_args = OrderedDict((a.name, a) for a in field_args) - args = to_arguments(args, field_args) - _creation_counter = None - attname = attname or name - parent = parent - - new_field = cls( - type=type, - args=args, - resolver=resolver, - source=source, - deprecation_reason=field.deprecation_reason, - name=name, - required=required, - description=field.description, - _creation_counter=_creation_counter, - **extra_args - ) - new_field.attname = attname - new_field.parent = parent - return new_field - - def __str__(self): - if not self.parent: - return 'Not bounded field' - return "{}.{}".format(self.parent._meta.graphql_type, self.attname) - - -class InputField(AbstractField, GraphQLInputObjectField, OrderedType): - - def __init__(self, type, default_value=None, description=None, name=None, required=False, _creation_counter=None): - self.name = name - self.type = type - self.default_value = default_value - self.description = description - self.required = required - self.attname = None - self.parent = None - OrderedType.__init__(self, _creation_counter=_creation_counter) - - def mount_error_message(self, where): - return 'InputField {} can only be mounted in InputObjectType classes, received {}.'.format( - self, - where.__name__ - ) - - def mount(self, parent, attname): - from .inputobjecttype import InputObjectType - - assert issubclass(parent, (InputObjectType)), self.mount_error_message(parent) - self.attname = attname - self.parent = parent - - def __copy__(self): - return self.copy_and_extend(self) - - @classmethod - def copy_and_extend(cls, field, type=None, default_value=None, description=None, name=None, - required=False, parent=None, attname=None, _creation_counter=False): - if isinstance(field, Field): - type = type or field._type - name = name or field._name - required = required or field.required - _creation_counter = field.creation_counter if _creation_counter is False else None - attname = attname or field.attname - parent = parent or field.parent - else: - # If is a GraphQLField - type = type or field.type - name = field.name - _creation_counter = None - - new_field = cls( - type=type, - name=name, - required=required, - default_value=default_value or field.default_value, - description=description or field.description, - _creation_counter=_creation_counter, - ) - new_field.attname = attname - new_field.parent = parent - return new_field + return self._type() + return self._type diff --git a/graphene/new_types/inputfield.py b/graphene/types/inputfield.py similarity index 100% rename from graphene/new_types/inputfield.py rename to graphene/types/inputfield.py diff --git a/graphene/types/inputobjecttype.py b/graphene/types/inputobjecttype.py index 46a1ffdd..aa8f24cc 100644 --- a/graphene/types/inputobjecttype.py +++ b/graphene/types/inputobjecttype.py @@ -1,49 +1,36 @@ import six -from ..generators import generate_inputobjecttype -from ..utils.copy_fields import copy_fields -from ..utils.get_fields import get_fields from ..utils.is_base_type import is_base_type -from .field import InputField -from .objecttype import attrs_without_fields from .options import Options -from .unmountedtype import UnmountedType + +from .abstracttype import AbstractTypeMeta +from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs -class InputObjectTypeMeta(type): +class InputObjectTypeMeta(AbstractTypeMeta): def __new__(cls, name, bases, attrs): - super_new = super(InputObjectTypeMeta, cls).__new__ - - # Also ensure initialization is only performed for subclasses of Model - # (excluding Model class itself). + # Also ensure initialization is only performed for subclasses of + # InputObjectType if not is_base_type(bases, InputObjectTypeMeta): - return super_new(cls, name, bases, attrs) + return type.__new__(cls, name, bases, attrs) options = Options( attrs.pop('Meta', None), - name=None, - description=None, - graphql_type=None, + name=name, + description=attrs.get('__doc__'), ) - fields = get_fields(InputObjectType, attrs, bases) - attrs = attrs_without_fields(attrs, fields) - cls = super_new(cls, name, bases, dict(attrs, _meta=options)) + attrs = merge_fields_in_attrs(bases, attrs) + options.fields = get_fields_in_type(InputObjectType, attrs) + yank_fields_from_attrs(attrs, options.fields) - if not options.graphql_type: - fields = copy_fields(InputField, fields, parent=cls) - options.get_fields = lambda: fields - options.graphql_type = generate_inputobjecttype(cls) - else: - assert not fields, "Can't mount InputFields in an InputObjectType with a defined graphql_type" - fields = copy_fields(InputField, options.graphql_type.get_fields(), parent=cls) + return type.__new__(cls, name, bases, dict(attrs, _meta=options)) - for name, field in fields.items(): - setattr(cls, field.attname or name, field) - - return cls + def __str__(cls): + return cls._meta.name -class InputObjectType(six.with_metaclass(InputObjectTypeMeta, UnmountedType)): - pass +class InputObjectType(six.with_metaclass(InputObjectTypeMeta)): + def __init__(self, *args, **kwargs): + raise Exception("An InputObjectType cannot be intitialized") diff --git a/graphene/types/interface.py b/graphene/types/interface.py index 56fc0ebb..d6c1fe37 100644 --- a/graphene/types/interface.py +++ b/graphene/types/interface.py @@ -1,3 +1,42 @@ -from .objecttype import Interface +import six -__all__ = ['Interface'] +from ..utils.is_base_type import is_base_type +from .options import Options + +from .abstracttype import AbstractTypeMeta +from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs + + +class InterfaceMeta(AbstractTypeMeta): + + def __new__(cls, name, bases, attrs): + # Also ensure initialization is only performed for subclasses of + # Interface + 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__'), + ) + + attrs = merge_fields_in_attrs(bases, attrs) + options.fields = get_fields_in_type(Interface, attrs) + yank_fields_from_attrs(attrs, options.fields) + + return type.__new__(cls, name, bases, dict(attrs, _meta=options)) + + def __str__(cls): + return cls._meta.name + + +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 diff --git a/graphene/types/mutation.py b/graphene/types/mutation.py index 018f8ef9..d54740ec 100644 --- a/graphene/types/mutation.py +++ b/graphene/types/mutation.py @@ -18,7 +18,7 @@ class MutationMeta(ObjectTypeMeta): input_class = attrs.pop('Input', None) - cls = cls._create_objecttype(cls, name, bases, attrs) + cls = ObjectTypeMeta.__new__(cls, name, bases, attrs) field_args = props(input_class) if input_class else {} resolver = getattr(cls, 'mutate', None) assert resolver, 'All mutations must define a mutate method in it' diff --git a/graphene/types/objecttype.py b/graphene/types/objecttype.py index acee8265..3c1c7791 100644 --- a/graphene/types/objecttype.py +++ b/graphene/types/objecttype.py @@ -1,132 +1,45 @@ -import inspect import six -from ..utils.copy_fields import copy_fields -from ..utils.get_fields import get_fields from ..utils.is_base_type import is_base_type -from .field import Field from .options import Options - -from ..generators import generate_interface, generate_objecttype +from .abstracttype import AbstractTypeMeta +from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs -def get_interfaces(interfaces): - from ..utils.get_graphql_type import get_graphql_type - - for interface in interfaces: - graphql_type = get_graphql_type(interface) - yield graphql_type - - -def is_objecttype(bases): - for base in bases: - if issubclass(base, ObjectType): - return True - return False - - -def attrs_without_fields(attrs, fields): - return {k: v for k, v in attrs.items() if k not in fields} - - -# We inherit from InterfaceTypeMeta instead of type for being able -# to have ObjectTypes extending Interfaces using Python syntax, like: -# class MyObjectType(ObjectType, MyInterface) -class ObjectTypeMeta(type): +class ObjectTypeMeta(AbstractTypeMeta): def __new__(cls, name, bases, attrs): # Also ensure initialization is only performed for subclasses of - # ObjectType,or Interfaces + # ObjectType if not is_base_type(bases, ObjectTypeMeta): return type.__new__(cls, name, bases, attrs) - if not is_objecttype(bases): - return cls._create_interface(cls, name, bases, attrs) - - return cls._create_objecttype(cls, name, bases, attrs) - - def is_object_type(cls): # noqa: N805 - return issubclass(cls, ObjectType) - - @staticmethod - def _get_interface_options(meta): - return Options( - meta, - name=None, - description=None, - graphql_type=None, - ) - - @staticmethod - def _create_interface(cls, name, bases, attrs): - options = cls._get_interface_options(attrs.pop('Meta', None)) - - fields = get_fields(Interface, attrs, bases) - attrs = attrs_without_fields(attrs, fields) - cls = type.__new__(cls, name, bases, dict(attrs, _meta=options)) - - if not options.graphql_type: - fields = copy_fields(Field, fields, parent=cls) - options.get_fields = lambda: fields - options.graphql_type = generate_interface(cls) - else: - assert not fields, "Can't mount Fields in an Interface with a defined graphql_type" - fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls) - options.get_fields = lambda: fields - - for name, field in fields.items(): - setattr(cls, field.attname or name, field) - - return cls - - @staticmethod - def _create_objecttype(cls, name, bases, attrs): options = Options( attrs.pop('Meta', None), - name=None, - description=None, - graphql_type=None, + name=name, + description=attrs.get('__doc__'), interfaces=(), ) - interfaces = tuple(options.interfaces) - fields = get_fields(ObjectType, attrs, bases, interfaces) - attrs = attrs_without_fields(attrs, fields) - cls = type.__new__(cls, name, bases, dict(attrs, _meta=options)) + attrs = merge_fields_in_attrs(bases, attrs) + options.fields = get_fields_in_type(ObjectType, attrs) + yank_fields_from_attrs(attrs, options.fields) - if not options.graphql_type: - fields = copy_fields(Field, fields, parent=cls) - inherited_interfaces = tuple(b for b in bases if issubclass(b, Interface)) - options.get_fields = lambda: fields - options.interfaces = interfaces + inherited_interfaces - options.get_interfaces = tuple(get_interfaces(options.interfaces)) - options.graphql_type = generate_objecttype(cls) - for i in options.interfaces: - if inspect.isclass(i) and issubclass(i, Interface): - i.implements(cls) - else: - assert not fields, "Can't mount Fields in an ObjectType with a defined graphql_type" - assert not interfaces, "Can't have extra interfaces with a defined graphql_type" - fields = copy_fields(Field, options.graphql_type.get_fields(), parent=cls) + return type.__new__(cls, name, bases, dict(attrs, _meta=options)) - options.get_fields = lambda: fields - - for name, field in fields.items(): - setattr(cls, field.attname or name, field) - - return cls + def __str__(cls): + return cls._meta.name class ObjectType(six.with_metaclass(ObjectTypeMeta)): + is_type_of = None + def __init__(self, *args, **kwargs): # GraphQL ObjectType acting as container args_len = len(args) - fields = self._meta.get_fields().items() - for name, f in fields: - setattr(self, getattr(f, 'attname', name), None) - + 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") @@ -134,19 +47,16 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)): if not kwargs: for val, (name, field) in zip(args, fields_iter): - attname = getattr(field, 'attname', name) - setattr(self, attname, val) + setattr(self, name, val) else: for val, (name, field) in zip(args, fields_iter): - attname = getattr(field, 'attname', name) - setattr(self, attname, val) - kwargs.pop(attname, None) + setattr(self, name, val) + kwargs.pop(name, None) for name, field in fields_iter: try: - attname = getattr(field, 'attname', name) - val = kwargs.pop(attname) - setattr(self, attname, val) + val = kwargs.pop(name) + setattr(self, name, val) except KeyError: pass @@ -159,28 +69,8 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)): pass if kwargs: raise TypeError( - "'%s' is an invalid keyword argument for this function" % - list(kwargs)[0]) - - @classmethod - def is_type_of(cls, interface, context, info): - from ..utils.get_graphql_type import get_graphql_type - try: - graphql_type = get_graphql_type(type(interface)) - return graphql_type.name == cls._meta.graphql_type.name - except: - return False - - -class Interface(six.with_metaclass(ObjectTypeMeta)): - resolve_type = None - - def __init__(self, *args, **kwargs): - from .objecttype import ObjectType - if not isinstance(self, ObjectType): - raise Exception("An interface cannot be intitialized") - super(Interface, self).__init__(*args, **kwargs) - - @classmethod - def implements(cls, objecttype): - pass + "'{}' is an invalid keyword argument for {}".format( + list(kwargs)[0], + self.__class__.__name__ + ) + ) diff --git a/graphene/types/options.py b/graphene/types/options.py index 0b810b5e..89c1eb5a 100644 --- a/graphene/types/options.py +++ b/graphene/types/options.py @@ -1,3 +1,4 @@ +import inspect from ..utils.props import props @@ -8,9 +9,11 @@ class Options(object): ''' def __init__(self, meta=None, **defaults): - self.add_attrs_from_meta(meta, defaults) + if meta: + assert inspect.isclass(meta), ( + 'Meta have to be a class, received "{}".'.format(repr(meta)) + ) - 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: @@ -27,3 +30,6 @@ class Options(object): ','.join(meta_attrs.keys()) ) ) + + def __repr__(self): + return ''.format(props(self)) diff --git a/graphene/types/scalars.py b/graphene/types/scalars.py index d6966e19..838fbd2e 100644 --- a/graphene/types/scalars.py +++ b/graphene/types/scalars.py @@ -1,9 +1,7 @@ import six -from graphql import (GraphQLBoolean, GraphQLFloat, GraphQLID, GraphQLInt, - GraphQLString) +from graphql.language.ast import BooleanValue, FloatValue, IntValue, StringValue -from ..generators import generate_scalar from ..utils.is_base_type import is_base_type from .options import Options from .unmountedtype import UnmountedType @@ -21,34 +19,137 @@ class ScalarTypeMeta(type): options = Options( attrs.pop('Meta', None), - name=None, - description=None, - graphql_type=None + name=name, + description=attrs.get('__doc__'), ) - cls = super_new(cls, name, bases, dict(attrs, _meta=options)) + return super_new(cls, name, bases, dict(attrs, _meta=options)) - if not options.graphql_type: - options.graphql_type = generate_scalar(cls) - - return cls + def __str__(cls): + return cls._meta.name class Scalar(six.with_metaclass(ScalarTypeMeta, UnmountedType)): - pass + serialize = None + parse_value = None + parse_literal = None + + @classmethod + def get_type(cls): + return cls + +# As per the GraphQL Spec, Integers are only treated as valid when a valid +# 32-bit signed integer, providing the broadest support across platforms. +# +# n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because +# they are internally represented as IEEE 754 doubles. +MAX_INT = 2147483647 +MIN_INT = -2147483648 -def construct_scalar_class(graphql_type): - # This is equivalent to - # class String(Scalar): - # class Meta: - # graphql_type = graphql_type - meta_class = type('Meta', (object,), {'graphql_type': graphql_type}) - return type(graphql_type.name, (Scalar, ), {'Meta': meta_class}) +class Int(Scalar): + ''' + The `Int` scalar type represents non-fractional signed whole numeric + values. Int can represent values between -(2^53 - 1) and 2^53 - 1 since + represented in JSON as double-precision floating point numbers specified + by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). + ''' + + @staticmethod + def coerce_int(value): + try: + num = int(value) + except ValueError: + try: + num = int(float(value)) + except ValueError: + return None + if MIN_INT <= num <= MAX_INT: + return num + + serialize = coerce_int + parse_value = coerce_int + + @staticmethod + def parse_literal(ast): + if isinstance(ast, IntValue): + num = int(ast.value) + if MIN_INT <= num <= MAX_INT: + return num -String = construct_scalar_class(GraphQLString) -Int = construct_scalar_class(GraphQLInt) -Float = construct_scalar_class(GraphQLFloat) -Boolean = construct_scalar_class(GraphQLBoolean) -ID = construct_scalar_class(GraphQLID) +class Float(Scalar): + ''' + The `Float` scalar type represents signed double-precision fractional + values as specified by + [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). + ''' + + @staticmethod + def coerce_float(value): + try: + return float(value) + except ValueError: + return None + + serialize = coerce_float + parse_value = coerce_float + + @staticmethod + def parse_literal(ast): + if isinstance(ast, (FloatValue, IntValue)): + return float(ast.value) + + +class String(Scalar): + ''' + The `String` scalar type represents textual data, represented as UTF-8 + character sequences. The String type is most often used by GraphQL to + represent free-form human-readable text. + ''' + + @staticmethod + def coerce_string(value): + if isinstance(value, bool): + return u'true' if value else u'false' + return six.text_type(value) + + serialize = coerce_string + parse_value = coerce_string + + @staticmethod + def parse_literal(ast): + if isinstance(ast, StringValue): + return ast.value + + +class Boolean(Scalar): + ''' + The `Boolean` scalar type represents `true` or `false`. + ''' + + serialize = bool + parse_value = bool + + @staticmethod + def parse_literal(ast): + if isinstance(ast, BooleanValue): + return ast.value + + +class ID(Scalar): + ''' + The `ID` scalar type represents a unique identifier, often used to + refetch an object or as key for a cache. The ID type appears in a JSON + response as a String; however, it is not intended to be human-readable. + When expected as an input type, any string (such as `"4"`) or integer + (such as `4`) input value will be accepted as an ID. + ''' + + serialize = str + parse_value = str + + @staticmethod + def parse_literal(ast): + if isinstance(ast, (StringValue, IntValue)): + return ast.value diff --git a/graphene/types/schema.py b/graphene/types/schema.py index 24b68702..624ce9e1 100644 --- a/graphene/types/schema.py +++ b/graphene/types/schema.py @@ -1,8 +1,14 @@ -from graphql import GraphQLSchema, graphql +import inspect + +from graphql import GraphQLSchema, graphql, is_type from graphql.utils.introspection_query import introspection_query from graphql.utils.schema_printer import print_schema -from ..utils.get_graphql_type import get_graphql_type + +from .objecttype import ObjectType +from .structures import List, NonNull +from .scalars import Scalar, String +# from ..utils.get_graphql_type import get_graphql_type # from graphql.type.schema import assert_object_implements_interface @@ -10,32 +16,60 @@ from ..utils.get_graphql_type import get_graphql_type # from collections import defaultdict +from graphql.type.directives import (GraphQLDirective, GraphQLIncludeDirective, + GraphQLSkipDirective) +from graphql.type.introspection import IntrospectionSchema +from .typemap import TypeMap, is_graphene_type + + class Schema(GraphQLSchema): def __init__(self, query=None, mutation=None, subscription=None, directives=None, types=None, executor=None): - if query: - query = get_graphql_type(query) - if mutation: - mutation = get_graphql_type(mutation) - if subscription: - subscription = get_graphql_type(subscription) + self._query = query + self._mutation = mutation + self._subscription = subscription self.types = types self._executor = executor - super(Schema, self).__init__( - query=query, - mutation=mutation, - subscription=subscription, - directives=directives, - types=self.types + if directives is None: + directives = [ + GraphQLIncludeDirective, + GraphQLSkipDirective + ] + + assert all(isinstance(d, GraphQLDirective) for d in directives), \ + 'Schema directives must be List[GraphQLDirective] if provided but got: {}.'.format( + directives ) - @property - def types(self): - return map(get_graphql_type, self._types or []) + self._directives = directives + initial_types = [ + query, + mutation, + subscription, + IntrospectionSchema + ] + if types: + initial_types += types + self._type_map = TypeMap(initial_types) - @types.setter - def types(self, value): - self._types = value + def get_query_type(self): + return self.get_graphql_type(self._query) + + def get_mutation_type(self): + return self.get_graphql_type(self._mutation) + + def get_subscription_type(self): + return self.get_graphql_type(self._subscription) + + def get_graphql_type(self, _type): + if is_type(_type): + return _type + if is_graphene_type(_type): + graphql_type = self.get_type(_type._meta.name) + assert graphql_type, "Type {} not found in this schema.".format(_type._meta.name) + assert graphql_type.graphene_type == _type + return graphql_type + raise Exception("{} is not a valid GraphQL type.".format(_type)) def execute(self, request_string='', root_value=None, variable_values=None, context_value=None, operation_name=None, executor=None): @@ -50,7 +84,7 @@ class Schema(GraphQLSchema): ) def register(self, object_type): - self._types.append(object_type) + self.types.append(object_type) def introspect(self): return self.execute(introspection_query).data @@ -58,9 +92,6 @@ class Schema(GraphQLSchema): def __str__(self): return print_schema(self) - def get_type(self, _type): - return self._type_map[_type] - def lazy(self, _type): return lambda: self.get_type(_type) diff --git a/graphene/types/structures.py b/graphene/types/structures.py index d577098f..41fcd5dd 100644 --- a/graphene/types/structures.py +++ b/graphene/types/structures.py @@ -1,7 +1,3 @@ -import inspect - -from graphql import GraphQLList, GraphQLNonNull - from .unmountedtype import UnmountedType @@ -18,21 +14,12 @@ class Structure(UnmountedType): def get_type(self): return self - @property - def of_type(self): - from ..utils.get_graphql_type import get_graphql_type - if inspect.isfunction(self._of_type): - return get_graphql_type(self._of_type()) - return get_graphql_type(self._of_type) - @of_type.setter - def of_type(self, value): - self._of_type = value +class List(Structure): + def __str__(self): + return '[{}]'.format(self.of_type) -class List(Structure, GraphQLList): - pass - - -class NonNull(Structure, GraphQLNonNull): - pass +class NonNull(Structure): + def __str__(self): + return '{}!'.format(self.of_type) diff --git a/graphene/new_types/tests/test_abstracttype.py b/graphene/types/tests/test_abstracttype.py similarity index 100% rename from graphene/new_types/tests/test_abstracttype.py rename to graphene/types/tests/test_abstracttype.py diff --git a/graphene/types/tests/test_argument.py b/graphene/types/tests/test_argument.py deleted file mode 100644 index 6969130f..00000000 --- a/graphene/types/tests/test_argument.py +++ /dev/null @@ -1,64 +0,0 @@ -import copy - -import pytest - -from graphql import GraphQLArgument, GraphQLString - -from ..argument import Argument, to_arguments -from ..scalars import String - - -def test_argument(): - argument = Argument(GraphQLString, name="name", description="description") - assert isinstance(argument, GraphQLArgument) - assert argument.name == "name" - assert argument.description == "description" - assert argument.type == GraphQLString - - -def test_field_wrong_name(): - with pytest.raises(AssertionError) as excinfo: - Argument(GraphQLString, name="a field") - - assert """Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "a field" does not.""" == str(excinfo.value) - - -def test_argument_type(): - argument = Argument(lambda: GraphQLString) - assert argument.type == GraphQLString - - -def test_argument_graphene_type(): - argument = Argument(String) - assert argument.type == GraphQLString - - -def test_argument_proxy_graphene_type(): - proxy = String() - argument = proxy.as_argument() - assert argument.type == GraphQLString - - -def test_copy_argument_works(): - argument = Argument(GraphQLString) - copy.copy(argument) - - -def test_to_arguments(): - arguments = to_arguments(a=String(), b=Argument(GraphQLString), c=Argument(String)) - assert list(arguments.keys()) == ['a', 'b', 'c'] - assert [a.type for a in arguments.values()] == [GraphQLString] * 3 - - -def test_to_arguments_incorrect(): - with pytest.raises(ValueError) as excinfo: - to_arguments(incorrect=object()) - - assert """Unknown argument "incorrect".""" == str(excinfo.value) - - -def test_to_arguments_no_name(): - with pytest.raises(AssertionError) as excinfo: - to_arguments(dict(a=String()), dict(a=String())) - - assert """More than one Argument have same name "a".""" == str(excinfo.value) diff --git a/graphene/new_types/tests/test_definition.py b/graphene/types/tests/test_definition.py similarity index 100% rename from graphene/new_types/tests/test_definition.py rename to graphene/types/tests/test_definition.py diff --git a/graphene/types/tests/test_enum.py b/graphene/types/tests/test_enum.py index 4abd185e..c16cbc99 100644 --- a/graphene/types/tests/test_enum.py +++ b/graphene/types/tests/test_enum.py @@ -1,12 +1,9 @@ -from graphql.type import GraphQLEnumType - -from ..argument import Argument from ..enum import Enum, PyEnum -from ..field import Field def test_enum_construction(): class RGB(Enum): + '''Description''' RED = 1 GREEN = 2 BLUE = 3 @@ -15,8 +12,10 @@ def test_enum_construction(): def description(self): return "Description {}".format(self.name) - assert isinstance(RGB._meta.graphql_type, GraphQLEnumType) - values = RGB._meta.graphql_type.get_values() + assert RGB._meta.name == 'RGB' + assert RGB._meta.description == 'Description' + + values = RGB._meta.enum.__members__.values() assert sorted([v.name for v in values]) == [ 'BLUE', 'GREEN', @@ -27,37 +26,40 @@ def test_enum_construction(): 'Description GREEN', 'Description RED' ] - assert isinstance(RGB(name='field_name').as_field(), Field) - assert isinstance(RGB(name='field_name').as_argument(), Argument) + + +def test_enum_construction_meta(): + class RGB(Enum): + class Meta: + name = 'RGBEnum' + description = 'Description' + RED = 1 + GREEN = 2 + BLUE = 3 + + assert RGB._meta.name == 'RGBEnum' + assert RGB._meta.description == 'Description' def test_enum_instance_construction(): RGB = Enum('RGB', 'RED,GREEN,BLUE') - assert isinstance(RGB._meta.graphql_type, GraphQLEnumType) - values = RGB._meta.graphql_type.get_values() + values = RGB._meta.enum.__members__.values() assert sorted([v.name for v in values]) == [ 'BLUE', 'GREEN', 'RED' ] - assert isinstance(RGB(name='field_name').as_field(), Field) - assert isinstance(RGB(name='field_name').as_argument(), Argument) def test_enum_from_builtin_enum(): PyRGB = PyEnum('RGB', 'RED,GREEN,BLUE') RGB = Enum.from_enum(PyRGB) - assert isinstance(RGB._meta.graphql_type, GraphQLEnumType) - values = RGB._meta.graphql_type.get_values() - assert sorted([v.name for v in values]) == [ - 'BLUE', - 'GREEN', - 'RED' - ] - assert isinstance(RGB(name='field_name').as_field(), Field) - assert isinstance(RGB(name='field_name').as_argument(), Argument) + assert RGB._meta.enum == PyRGB + assert RGB.RED + assert RGB.GREEN + assert RGB.BLUE def test_enum_value_from_class(): @@ -67,3 +69,5 @@ def test_enum_value_from_class(): BLUE = 3 assert RGB.RED.value == 1 + assert RGB.GREEN.value == 2 + assert RGB.BLUE.value == 3 diff --git a/graphene/types/tests/test_field.py b/graphene/types/tests/test_field.py index 521f9b7d..31fcf037 100644 --- a/graphene/types/tests/test_field.py +++ b/graphene/types/tests/test_field.py @@ -1,67 +1,77 @@ -import copy - import pytest -from graphql import GraphQLField, GraphQLInt, GraphQLNonNull, GraphQLString - -from ..argument import Argument from ..field import Field -from ..scalars import Int, String +from ..structures import NonNull +from ..argument import Argument -def test_field(): - field = Field(GraphQLString, name="name", description="description") - assert isinstance(field, GraphQLField) - assert field.name == "name" - assert field.description == "description" - assert field.type == GraphQLString +class MyInstance(object): + value = 'value' + value_func = staticmethod(lambda: 'value_func') + + +def test_field_basic(): + MyType = object() + args = {'my arg': Argument(True)} + 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(): - field = Field(GraphQLString, required=True) - assert isinstance(field, GraphQLField) - assert isinstance(field.type, GraphQLNonNull) - assert field.type.of_type == GraphQLString + MyType = object() + field = Field(MyType, required=True) + assert isinstance(field.type, NonNull) + assert field.type.of_type == MyType -def test_field_wrong_name(): - with pytest.raises(AssertionError) as excinfo: - Field(GraphQLString, name="a field") - - assert """Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "a field" does not.""" == str(excinfo.value) +def test_field_source(): + MyType = object() + field = Field(MyType, source='value') + assert field.resolver(MyInstance, {}, None, None) == MyInstance.value -def test_not_source_and_resolver(): - with pytest.raises(AssertionError) as excinfo: - Field(GraphQLString, source="a", resolver=lambda *_: None) - - assert "You cannot have a source and a resolver at the same time" == str(excinfo.value) +def test_field_with_lazy_type(): + MyType = object() + field = Field(lambda: MyType) + assert field.type == MyType -def test_copy_field_works(): - field = Field(GraphQLString) - copy.copy(field) +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) == 'A Field cannot have a source and a resolver in at the same time.' -def test_field_callable_type(): - field = Field(lambda: GraphQLString) - assert field.type == GraphQLString +def test_field_source_func(): + MyType = object() + field = Field(MyType, source='value_func') + assert field.resolver(MyInstance(), {}, None, None) == MyInstance.value_func() -def test_field_with_arguments(): - field = Field(GraphQLString, name="name", description="description", input=Argument(GraphQLString)) - assert isinstance(field, GraphQLField) - assert field.name == "name" - assert field.description == "description" - assert 'input' in field.args - assert field.args['input'].type == GraphQLString - - -def test_field_with_argument_proxies(): - field = Field(GraphQLString, name="name", description="description", int=Int(), string=String()) - assert isinstance(field, GraphQLField) - assert field.name == "name" - assert field.description == "description" - assert list(field.args.keys()) == ['int', 'string'] - assert field.args['string'].type == GraphQLString - assert field.args['int'].type == GraphQLInt +def test_field_source_argument_as_kw(): + MyType = object() + field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False)) + assert field.args.keys() == ['b', 'c', 'a'] + assert isinstance(field.args['b'], Argument) + assert isinstance(field.args['b'].type, NonNull) + assert field.args['b'].type.of_type is True + assert isinstance(field.args['c'], Argument) + assert field.args['c'].type is None + assert isinstance(field.args['a'], Argument) + assert isinstance(field.args['a'].type, NonNull) + assert field.args['a'].type.of_type is False diff --git a/graphene/types/tests/test_inputobjecttype.py b/graphene/types/tests/test_inputobjecttype.py index 8036b96a..52796749 100644 --- a/graphene/types/tests/test_inputobjecttype.py +++ b/graphene/types/tests/test_inputobjecttype.py @@ -1,57 +1,83 @@ +import pytest -from graphql import GraphQLInputObjectType, GraphQLString -from graphql.type.definition import GraphQLInputFieldDefinition - -from ..field import InputField +from ..field import Field +from ..inputfield import InputField from ..inputobjecttype import InputObjectType -from ..scalars import String +from ..unmountedtype import UnmountedType +from ..abstracttype import AbstractType + + +class MyType(object): + pass + + +class MyScalar(UnmountedType): + def get_type(self): + return MyType def test_generate_inputobjecttype(): - class MyObjectType(InputObjectType): + class MyInputObjectType(InputObjectType): '''Documentation''' - graphql_type = MyObjectType._meta.graphql_type - assert isinstance(graphql_type, GraphQLInputObjectType) - assert graphql_type.name == "MyObjectType" - assert graphql_type.description == "Documentation" + assert MyInputObjectType._meta.name == "MyInputObjectType" + assert MyInputObjectType._meta.description == "Documentation" + assert MyInputObjectType._meta.fields == {} def test_generate_inputobjecttype_with_meta(): - class MyObjectType(InputObjectType): + class MyInputObjectType(InputObjectType): class Meta: - name = 'MyOtherObjectType' + name = 'MyOtherInputObjectType' description = 'Documentation' - graphql_type = MyObjectType._meta.graphql_type - assert isinstance(graphql_type, GraphQLInputObjectType) - assert graphql_type.name == "MyOtherObjectType" - assert graphql_type.description == "Documentation" + assert MyInputObjectType._meta.name == "MyOtherInputObjectType" + assert MyInputObjectType._meta.description == "Documentation" -def test_empty_inputobjecttype_has_meta(): - class MyObjectType(InputObjectType): - pass +def test_generate_inputobjecttype_with_fields(): + class MyInputObjectType(InputObjectType): + field = Field(MyType) - assert MyObjectType._meta + assert 'field' in MyInputObjectType._meta.fields -def test_generate_objecttype_with_fields(): - class MyObjectType(InputObjectType): - field = InputField(GraphQLString) +def test_ordered_fields_in_inputobjecttype(): + class MyInputObjectType(InputObjectType): + b = InputField(MyType) + a = InputField(MyType) + field = MyScalar() + asa = InputField(MyType) - graphql_type = MyObjectType._meta.graphql_type - fields = graphql_type.get_fields() - assert 'field' in fields - assert isinstance(fields['field'], GraphQLInputFieldDefinition) + assert list(MyInputObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa'] -def test_generate_objecttype_with_graphene_fields(): - class MyObjectType(InputObjectType): - field = String() +def test_generate_inputobjecttype_unmountedtype(): + class MyInputObjectType(InputObjectType): + field = MyScalar(MyType) - graphql_type = MyObjectType._meta.graphql_type - fields = graphql_type.get_fields() - assert 'field' in fields - assert isinstance(fields['field'], GraphQLInputFieldDefinition) + assert 'field' in MyInputObjectType._meta.fields + assert isinstance(MyInputObjectType._meta.fields['field'], InputField) + + +def test_generate_inputobjecttype_inherit_abstracttype(): + class MyAbstractType(AbstractType): + field1 = MyScalar(MyType) + + class MyInputObjectType(InputObjectType, MyAbstractType): + field2 = MyScalar(MyType) + + assert MyInputObjectType._meta.fields.keys() == ['field1', 'field2'] + assert [type(x) for x in MyInputObjectType._meta.fields.values()] == [InputField, InputField] + + +def test_generate_inputobjecttype_inherit_abstracttype_reversed(): + class MyAbstractType(AbstractType): + field1 = MyScalar(MyType) + + class MyInputObjectType(MyAbstractType, InputObjectType): + field2 = MyScalar(MyType) + + assert MyInputObjectType._meta.fields.keys() == ['field1', 'field2'] + assert [type(x) for x in MyInputObjectType._meta.fields.values()] == [InputField, InputField] diff --git a/graphene/types/tests/test_interface.py b/graphene/types/tests/test_interface.py index 09136e04..97804c5c 100644 --- a/graphene/types/tests/test_interface.py +++ b/graphene/types/tests/test_interface.py @@ -1,19 +1,27 @@ import pytest -from graphql import GraphQLField, GraphQLInterfaceType, GraphQLString - from ..field import Field from ..interface import Interface +from ..unmountedtype import UnmountedType +from ..abstracttype import AbstractType + + +class MyType(object): + pass + + +class MyScalar(UnmountedType): + def get_type(self): + return MyType def test_generate_interface(): class MyInterface(Interface): '''Documentation''' - graphql_type = MyInterface._meta.graphql_type - assert isinstance(graphql_type, GraphQLInterfaceType) - assert graphql_type.name == "MyInterface" - assert graphql_type.description == "Documentation" + assert MyInterface._meta.name == "MyInterface" + assert MyInterface._meta.description == "Documentation" + assert MyInterface._meta.fields == {} def test_generate_interface_with_meta(): @@ -23,62 +31,52 @@ def test_generate_interface_with_meta(): name = 'MyOtherInterface' description = 'Documentation' - graphql_type = MyInterface._meta.graphql_type - assert isinstance(graphql_type, GraphQLInterfaceType) - assert graphql_type.name == "MyOtherInterface" - assert graphql_type.description == "Documentation" - - -def test_empty_interface_has_meta(): - class MyInterface(Interface): - pass - - assert MyInterface._meta + assert MyInterface._meta.name == "MyOtherInterface" + assert MyInterface._meta.description == "Documentation" def test_generate_interface_with_fields(): class MyInterface(Interface): - field = Field(GraphQLString) + field = Field(MyType) - graphql_type = MyInterface._meta.graphql_type - fields = graphql_type.get_fields() - assert 'field' in fields + assert 'field' in MyInterface._meta.fields -def test_interface_inheritance(): - class MyInheritedInterface(Interface): - inherited = Field(GraphQLString) - - class MyInterface(MyInheritedInterface): - field = Field(GraphQLString) - - graphql_type = MyInterface._meta.graphql_type - fields = graphql_type.get_fields() - assert 'field' in fields - assert 'inherited' in fields - assert MyInterface.field > MyInheritedInterface.inherited - - -def test_interface_instance(): +def test_ordered_fields_in_interface(): class MyInterface(Interface): - inherited = Field(GraphQLString) + b = Field(MyType) + a = Field(MyType) + field = MyScalar() + asa = Field(MyType) - with pytest.raises(Exception) as excinfo: - MyInterface() - - assert "An interface cannot be intitialized" in str(excinfo.value) + assert list(MyInterface._meta.fields.keys()) == ['b', 'a', 'field', 'asa'] -def test_interface_add_fields_in_reused_graphql_type(): - MyGraphQLType = GraphQLInterfaceType('MyGraphQLType', fields={ - 'field': GraphQLField(GraphQLString) - }) +def test_generate_interface_unmountedtype(): + class MyInterface(Interface): + field = MyScalar() - with pytest.raises(AssertionError) as excinfo: - class GrapheneInterface(Interface): - field = Field(GraphQLString) + assert 'field' in MyInterface._meta.fields + assert isinstance(MyInterface._meta.fields['field'], Field) - class Meta: - graphql_type = MyGraphQLType - assert """Can't mount Fields in an Interface with a defined graphql_type""" == str(excinfo.value) +def test_generate_interface_inherit_abstracttype(): + class MyAbstractType(AbstractType): + field1 = MyScalar() + + class MyInterface(Interface, MyAbstractType): + field2 = MyScalar() + + assert MyInterface._meta.fields.keys() == ['field1', 'field2'] + assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] + + +def test_generate_interface_inherit_abstracttype_reversed(): + class MyAbstractType(AbstractType): + field1 = MyScalar() + + class MyInterface(MyAbstractType, Interface): + field2 = MyScalar() + + assert MyInterface._meta.fields.keys() == ['field1', 'field2'] + assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] diff --git a/graphene/types/tests/test_mutation.py b/graphene/types/tests/test_mutation.py index 49855746..2b4a8859 100644 --- a/graphene/types/tests/test_mutation.py +++ b/graphene/types/tests/test_mutation.py @@ -1,7 +1,5 @@ import pytest -from graphql import GraphQLObjectType, GraphQLString - from ..field import Field from ..mutation import Mutation from ..objecttype import ObjectType @@ -16,31 +14,29 @@ def test_generate_mutation_no_args(): pass assert issubclass(MyMutation, ObjectType) - graphql_type = MyMutation._meta.graphql_type - assert isinstance(graphql_type, GraphQLObjectType) - assert graphql_type.name == "MyMutation" - assert graphql_type.description == "Documentation" + assert MyMutation._meta.name == "MyMutation" + assert MyMutation._meta.description == "Documentation" + assert MyMutation.Field().resolver == MyMutation.mutate -def test_generate_mutation_with_args(): - class MyMutation(Mutation): - '''Documentation''' - class Input: - s = String() +# def test_generate_mutation_with_args(): +# class MyMutation(Mutation): +# '''Documentation''' +# class Input: +# s = String() - @classmethod - def mutate(cls, *args, **kwargs): - pass +# @classmethod +# def mutate(cls, *args, **kwargs): +# pass - graphql_type = MyMutation._meta.graphql_type - field = MyMutation.Field() - assert isinstance(graphql_type, GraphQLObjectType) - assert graphql_type.name == "MyMutation" - assert graphql_type.description == "Documentation" - assert isinstance(field, Field) - assert field.type == MyMutation._meta.graphql_type - assert 's' in field.args - assert field.args['s'].type == GraphQLString +# graphql_type = MyMutation._meta.graphql_type +# field = MyMutation.Field() +# assert graphql_type.name == "MyMutation" +# assert graphql_type.description == "Documentation" +# assert isinstance(field, Field) +# assert field.type == MyMutation._meta.graphql_type +# assert 's' in field.args +# assert field.args['s'].type == String def test_generate_mutation_with_meta(): @@ -54,20 +50,9 @@ def test_generate_mutation_with_meta(): def mutate(cls, *args, **kwargs): pass - graphql_type = MyMutation._meta.graphql_type - assert isinstance(graphql_type, GraphQLObjectType) - assert graphql_type.name == "MyOtherMutation" - assert graphql_type.description == "Documentation" - - -def test_empty_mutation_has_meta(): - class MyMutation(Mutation): - - @classmethod - def mutate(cls, *args, **kwargs): - pass - - assert MyMutation._meta + assert MyMutation._meta.name == "MyOtherMutation" + assert MyMutation._meta.description == "Documentation" + assert MyMutation.Field().resolver == MyMutation.mutate def test_mutation_raises_exception_if_no_mutate(): diff --git a/graphene/types/tests/test_objecttype.py b/graphene/types/tests/test_objecttype.py index 3a2f124e..9b8b3913 100644 --- a/graphene/types/tests/test_objecttype.py +++ b/graphene/types/tests/test_objecttype.py @@ -1,26 +1,33 @@ import pytest -from graphql import (GraphQLField, GraphQLInterfaceType, GraphQLObjectType, - GraphQLString) - from ..field import Field -from ..interface import Interface from ..objecttype import ObjectType +from ..unmountedtype import UnmountedType +from ..abstracttype import AbstractType + + +class MyType(object): + pass class Container(ObjectType): - field1 = Field(GraphQLString, name='field1') - field2 = Field(GraphQLString, name='field2') + field1 = Field(MyType) + field2 = Field(MyType) + + +class MyScalar(UnmountedType): + def get_type(self): + return MyType def test_generate_objecttype(): class MyObjectType(ObjectType): '''Documentation''' - graphql_type = MyObjectType._meta.graphql_type - assert isinstance(graphql_type, GraphQLObjectType) - assert graphql_type.name == "MyObjectType" - assert graphql_type.description == "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(): @@ -29,66 +36,62 @@ def test_generate_objecttype_with_meta(): class Meta: name = 'MyOtherObjectType' description = 'Documentation' + interfaces = (MyType, ) - graphql_type = MyObjectType._meta.graphql_type - assert isinstance(graphql_type, GraphQLObjectType) - assert graphql_type.name == "MyOtherObjectType" - assert graphql_type.description == "Documentation" - - -def test_empty_objecttype_has_meta(): - class MyObjectType(ObjectType): - pass - - assert MyObjectType._meta + 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(GraphQLString) + field = Field(MyType) - graphql_type = MyObjectType._meta.graphql_type - fields = graphql_type.get_fields() - assert 'field' in fields + assert 'field' in MyObjectType._meta.fields def test_ordered_fields_in_objecttype(): class MyObjectType(ObjectType): - b = Field(GraphQLString) - a = Field(GraphQLString) - field = Field(GraphQLString) - asa = Field(GraphQLString) + b = Field(MyType) + a = Field(MyType) + field = MyScalar() + asa = Field(MyType) - graphql_type = MyObjectType._meta.graphql_type - fields = graphql_type.get_fields() - assert list(fields.keys()) == ['b', 'a', 'field', 'asa'] + assert list(MyObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa'] -def test_objecttype_inheritance(): - class MyInheritedObjectType(ObjectType): - inherited = Field(GraphQLString) +def test_generate_objecttype_inherit_abstracttype(): + class MyAbstractType(AbstractType): + field1 = MyScalar() - class MyObjectType(MyInheritedObjectType): - field1 = Field(GraphQLString) - field2 = Field(GraphQLString) + class MyObjectType(ObjectType, MyAbstractType): + field2 = MyScalar() - graphql_type = MyObjectType._meta.graphql_type - fields = graphql_type.get_fields() - assert list(fields.keys()) == ['inherited', 'field1', 'field2'] + assert MyObjectType._meta.fields.keys() == ['field1', 'field2'] + assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field] -def test_objecttype_as_container_get_fields(): +def test_generate_objecttype_inherit_abstracttype_reversed(): + class MyAbstractType(AbstractType): + field1 = MyScalar() - class Container(ObjectType): - field1 = Field(GraphQLString) - field2 = Field(GraphQLString) + class MyObjectType(MyAbstractType, ObjectType): + field2 = MyScalar() - assert list(Container._meta.graphql_type.get_fields().keys()) == ['field1', 'field2'] + assert MyObjectType._meta.fields.keys() == ['field1', 'field2'] + assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field] + + +def test_generate_objecttype_unmountedtype(): + class MyObjectType(ObjectType): + field = MyScalar() + + assert 'field' in MyObjectType._meta.fields + assert isinstance(MyObjectType._meta.fields['field'], Field) def test_parent_container_get_fields(): - fields = Container._meta.graphql_type.get_fields() - assert list(fields.keys()) == ['field1', 'field2'] + assert list(Container._meta.fields.keys()) == ['field1', 'field2'] def test_objecttype_as_container_only_args(): @@ -125,130 +128,4 @@ 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 this function" == str(excinfo.value) - - -def test_objecttype_reuse_graphql_type(): - MyGraphQLType = GraphQLObjectType('MyGraphQLType', fields={ - 'field': GraphQLField(GraphQLString) - }) - - class GrapheneObjectType(ObjectType): - - class Meta: - graphql_type = MyGraphQLType - - graphql_type = GrapheneObjectType._meta.graphql_type - assert graphql_type == MyGraphQLType - instance = GrapheneObjectType(field="A") - assert instance.field == "A" - - -def test_objecttype_add_fields_in_reused_graphql_type(): - MyGraphQLType = GraphQLObjectType('MyGraphQLType', fields={ - 'field': GraphQLField(GraphQLString) - }) - - with pytest.raises(AssertionError) as excinfo: - class GrapheneObjectType(ObjectType): - field = Field(GraphQLString) - - class Meta: - graphql_type = MyGraphQLType - - assert """Can't mount Fields in an ObjectType with a defined graphql_type""" == str(excinfo.value) - - -def test_objecttype_graphql_interface(): - MyInterface = GraphQLInterfaceType('MyInterface', fields={ - 'field': GraphQLField(GraphQLString) - }) - - class GrapheneObjectType(ObjectType): - - class Meta: - interfaces = [MyInterface] - - graphql_type = GrapheneObjectType._meta.graphql_type - assert graphql_type.get_interfaces() == (MyInterface, ) - # assert graphql_type.is_type_of(MyInterface, None, None) - fields = graphql_type.get_fields() - assert 'field' in fields - - -def test_objecttype_graphene_interface(): - class GrapheneInterface(Interface): - name = Field(GraphQLString) - extended = Field(GraphQLString) - - class GrapheneObjectType(ObjectType): - - class Meta: - interfaces = [GrapheneInterface] - - field = Field(GraphQLString) - - graphql_type = GrapheneObjectType._meta.graphql_type - assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, ) - assert graphql_type.is_type_of(GrapheneObjectType(), None, None) - fields = graphql_type.get_fields().keys() == ['name', 'extended', 'field'] - - -def test_objecttype_graphene_inherit_interface(): - class GrapheneInterface(Interface): - name = Field(GraphQLString) - extended = Field(GraphQLString) - - class GrapheneObjectType(ObjectType, GrapheneInterface): - field = Field(GraphQLString) - - graphql_type = GrapheneObjectType._meta.graphql_type - assert graphql_type.get_interfaces() == (GrapheneInterface._meta.graphql_type, ) - assert graphql_type.is_type_of(GrapheneObjectType(), None, None) - fields = graphql_type.get_fields() - fields = graphql_type.get_fields().keys() == ['name', 'extended', 'field'] - assert issubclass(GrapheneObjectType, GrapheneInterface) - - -# def test_objecttype_graphene_interface_extended(): -# class GrapheneInterface(Interface): -# field = Field(GraphQLString) - -# class GrapheneObjectType(ObjectType): -# class Meta: -# interfaces = [GrapheneInterface] - -# schema = Schema(query=GrapheneObjectType) -# assert str(schema) == """ -# schema { -# query: GrapheneObjectType -# } - -# interface GrapheneInterface { -# field: String -# } - -# type GrapheneObjectType implements GrapheneInterface { -# field: String -# } -# """.lstrip() -# GrapheneInterface._meta.graphql_type.add_field(Field(String, name='dynamic')) -# # GrapheneObjectType._meta.graphql_type._field_map = None -# assert GrapheneInterface._meta.graphql_type.get_fields().keys() == ['field', 'dynamic'] -# assert GrapheneObjectType._meta.graphql_type.get_fields().keys() == ['field', 'dynamic'] -# schema.rebuild() -# assert str(schema) == """ -# schema { -# query: GrapheneObjectType -# } - -# interface GrapheneInterface { -# field: String -# dynamic: String -# } - -# type GrapheneObjectType implements GrapheneInterface { -# field: String -# dynamic: String -# } -# """.lstrip() + assert "'unexisting_field' is an invalid keyword argument for Container" == str(excinfo.value) diff --git a/graphene/types/tests/test_options.py b/graphene/types/tests/test_options.py deleted file mode 100644 index 59a708cc..00000000 --- a/graphene/types/tests/test_options.py +++ /dev/null @@ -1,23 +0,0 @@ -import pytest - -from ..options import Options - - -def test_options_defaults(): - class Meta: - valid_second = True - - options = Options(Meta, valid_second=False, valid_first=False) - - assert not options.valid_first - assert options.valid_second - - -def test_options_invalid_attrs(): - class Meta: - invalid = True - - with pytest.raises(TypeError) as excinfo: - Options(Meta, valid=True) - - assert "Invalid attributes: invalid" == str(excinfo.value) diff --git a/graphene/new_types/tests/test_query.py b/graphene/types/tests/test_query.py similarity index 100% rename from graphene/new_types/tests/test_query.py rename to graphene/types/tests/test_query.py diff --git a/graphene/types/tests/test_scalars.py b/graphene/types/tests/test_scalars.py deleted file mode 100644 index 8a2b7b12..00000000 --- a/graphene/types/tests/test_scalars.py +++ /dev/null @@ -1,144 +0,0 @@ -import datetime - -import pytest - -from graphene.utils.get_graphql_type import get_graphql_type -from graphql import graphql -from graphql.language import ast -from graphql.type import (GraphQLBoolean, GraphQLFieldDefinition, GraphQLFloat, - GraphQLInt, GraphQLScalarType, GraphQLString) - -from ..field import Field -from ..objecttype import ObjectType -from ..scalars import Boolean, Float, Int, Scalar, String -from ..schema import Schema - - -class DatetimeScalar(Scalar): - - class Meta: - name = 'DateTime' - - @staticmethod - def serialize(dt): - assert isinstance(dt, datetime.datetime) - 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") - - -def serialize_date_time(dt): - assert isinstance(dt, datetime.datetime) - return dt.isoformat() - - -def parse_literal(node): - if isinstance(node, ast.StringValue): - return datetime.datetime.strptime(node.value, "%Y-%m-%dT%H:%M:%S.%f") - - -def parse_value(value): - return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f") - - -GraphQLDateTimeType = GraphQLScalarType( - name='DateTime', - serialize=serialize_date_time, - parse_literal=parse_literal, - parse_value=parse_value -) - - -class DatetimeScalarGraphQL(Scalar): - - class Meta: - graphql_type = GraphQLDateTimeType - - -scalar_classes = { - DatetimeScalar: DatetimeScalar._meta.graphql_type, - DatetimeScalarGraphQL: GraphQLDateTimeType, - String: GraphQLString, - Int: GraphQLInt, - Float: GraphQLFloat, - Boolean: GraphQLBoolean, -} - - -@pytest.mark.parametrize("scalar_class,expected_graphql_type", scalar_classes.items()) -def test_scalar_as_field(scalar_class, expected_graphql_type): - field_before = Field(None) - scalar = scalar_class() - field = scalar.as_field() - graphql_type = get_graphql_type(scalar_class) - field_after = Field(None) - assert isinstance(field, Field) - assert field.type == graphql_type - assert graphql_type == expected_graphql_type - assert field_before < field < field_after - - -@pytest.mark.parametrize("scalar_class,graphql_type", scalar_classes.items()) -def test_scalar_in_objecttype(scalar_class, graphql_type): - class MyObjectType(ObjectType): - before = Field(scalar_class) - field = scalar_class() - after = Field(scalar_class) - - graphql_type = get_graphql_type(MyObjectType) - fields = graphql_type.get_fields() - assert list(fields.keys()) == ['before', 'field', 'after'] - assert isinstance(fields['field'], GraphQLFieldDefinition) - - -def test_custom_scalar_empty(): - with pytest.raises(AssertionError) as excinfo: - class DatetimeScalar(Scalar): - pass - - assert """DatetimeScalar must provide "serialize" function.""" in str(excinfo.value) - - -@pytest.mark.parametrize("scalar_class", (DatetimeScalar, DatetimeScalarGraphQL)) -def test_custom_scalar_query(scalar_class): - class Query(ObjectType): - datetime = scalar_class(_in=scalar_class(name='in')) - - def resolve_datetime(self, args, context, info): - return args.get('in') - - now = datetime.datetime.now() - isoformat = now.isoformat() - - schema = Schema(query=Query) - - response = graphql(schema, ''' - { - datetime(in: "%s") - } - ''' % isoformat) - - assert not response.errors - assert response.data == { - 'datetime': isoformat - } - - response = graphql(schema, ''' - query Test($date: DateTime) { - datetime(in: $date) - } - ''', variable_values={ - 'date': isoformat - }) - - assert not response.errors - assert response.data == { - 'datetime': isoformat - } diff --git a/graphene/new_types/tests/test_scalars_serialization.py b/graphene/types/tests/test_scalars_serialization.py similarity index 100% rename from graphene/new_types/tests/test_scalars_serialization.py rename to graphene/types/tests/test_scalars_serialization.py diff --git a/graphene/types/tests/test_schema.py b/graphene/types/tests/test_schema.py deleted file mode 100644 index 5f3a7edc..00000000 --- a/graphene/types/tests/test_schema.py +++ /dev/null @@ -1,96 +0,0 @@ -from ..field import Field -from ..interface import Interface -from ..objecttype import ObjectType -from ..scalars import String -from ..schema import Schema -from ..structures import List - - -class Character(Interface): - name = String() - friends = List(lambda: Character) - best_friend = Field(lambda: Character) - - def resolve_friends(self, *args): - return [Human(name='Peter')] - - def resolve_best_friend(self, *args): - return Human(name='Best') - - -class Pet(ObjectType): - type = String() - - -class Human(ObjectType): - - class Meta: - interfaces = [Character] - - pet = Field(Pet) - - def resolve_pet(self, *args): - return Pet(type='Dog') - - -class RootQuery(ObjectType): - character = Field(Character) - - def resolve_character(self, *_): - return Human(name='Harry') - - -schema = Schema(query=RootQuery, types=[Human]) - - -def test_schema(): - executed = schema.execute( - '{ character {name, bestFriend { name }, friends { name}, ...on Human {pet { type } } } }') - assert not executed.errors - assert executed.data == {'character': {'name': 'Harry', 'bestFriend': { - 'name': 'Best'}, 'friends': [{'name': 'Peter'}], 'pet': {'type': 'Dog'}}} - - -def test_schema_introspect(): - introspection = schema.introspect() - assert '__schema' in introspection - - -def test_schema_str(): - expected = """ -schema { - query: RootQuery -} - -interface Character { - name: String - friends: [Character] - bestFriend: Character -} - -type Human implements Character { - name: String - friends: [Character] - bestFriend: Character - pet: Pet -} - -type Pet { - type: String -} - -type RootQuery { - character: Character -} -""".lstrip() - assert str(schema) == expected - - -def test_schema_get_type(): - pet = schema.get_type('Pet') - assert pet == Pet._meta.graphql_type - - -def test_schema_lazy_type(): - pet = schema.lazy('Pet') - assert pet() == Pet._meta.graphql_type diff --git a/graphene/types/tests/test_structures.py b/graphene/types/tests/test_structures.py deleted file mode 100644 index 42025705..00000000 --- a/graphene/types/tests/test_structures.py +++ /dev/null @@ -1,51 +0,0 @@ - -from graphql import GraphQLList, GraphQLNonNull, GraphQLString - -from ..field import Field -from ..scalars import String -from ..structures import List, NonNull - - -def test_list(): - list_instance = List(String) - assert isinstance(list_instance, GraphQLList) - assert list_instance.of_type == GraphQLString - - -def test_list_lambda(): - list_instance = List(lambda: String) - assert isinstance(list_instance, GraphQLList) - assert list_instance.of_type == GraphQLString - - -def test_list_list(): - list_instance = List(List(String)) - assert isinstance(list_instance, GraphQLList) - assert isinstance(list_instance.of_type, GraphQLList) - assert list_instance.of_type.of_type == GraphQLString - - -def test_nonnull(): - list_instance = NonNull(String) - assert isinstance(list_instance, GraphQLNonNull) - assert list_instance.of_type == GraphQLString - - -def test_nonnull_lambda(): - list_instance = NonNull(lambda: String) - assert isinstance(list_instance, GraphQLNonNull) - assert list_instance.of_type == GraphQLString - - -def test_nonnull_list(): - list_instance = NonNull(List(String)) - assert isinstance(list_instance, GraphQLNonNull) - assert isinstance(list_instance.of_type, GraphQLList) - assert list_instance.of_type.of_type == GraphQLString - - -def test_preserve_order(): - field1 = List(lambda: None) - field2 = Field(lambda: None) - - assert field1 < field2 diff --git a/graphene/new_types/tests/test_typemap.py b/graphene/types/tests/test_typemap.py similarity index 100% rename from graphene/new_types/tests/test_typemap.py rename to graphene/types/tests/test_typemap.py diff --git a/graphene/new_types/tests/test_union.py b/graphene/types/tests/test_union.py similarity index 100% rename from graphene/new_types/tests/test_union.py rename to graphene/types/tests/test_union.py diff --git a/graphene/new_types/typemap.py b/graphene/types/typemap.py similarity index 100% rename from graphene/new_types/typemap.py rename to graphene/types/typemap.py diff --git a/graphene/new_types/union.py b/graphene/types/union.py similarity index 100% rename from graphene/new_types/union.py rename to graphene/types/union.py diff --git a/graphene/types/unmountedtype.py b/graphene/types/unmountedtype.py index b3a20f55..ac813e8e 100644 --- a/graphene/types/unmountedtype.py +++ b/graphene/types/unmountedtype.py @@ -1,6 +1,4 @@ from ..utils.orderedtype import OrderedType -from .argument import Argument -from .field import Field, InputField class UnmountedType(OrderedType): @@ -18,17 +16,18 @@ class UnmountedType(OrderedType): ''' def __init__(self, *args, **kwargs): + super(UnmountedType, self).__init__() self.args = args self.kwargs = kwargs - super(UnmountedType, self).__init__() def get_type(self): - return self._meta.graphql_type + 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, @@ -40,6 +39,7 @@ class UnmountedType(OrderedType): ''' Mount the UnmountedType as InputField ''' + from .inputfield import InputField return InputField( self.get_type(), *self.args, @@ -51,9 +51,20 @@ class UnmountedType(OrderedType): ''' Mount the UnmountedType as Argument ''' + from .argument import Argument return Argument( self.get_type(), *self.args, _creation_counter=self.creation_counter, **self.kwargs ) + + def __eq__(self, other): + return ( + self is other or ( + isinstance(other, UnmountedType) and + self.get_type() == other.get_type() and + self.args == other.args and + self.kwargs == other.kwargs + ) + ) diff --git a/graphene/new_types/utils.py b/graphene/types/utils.py similarity index 88% rename from graphene/new_types/utils.py rename to graphene/types/utils.py index dab29884..74eb52dc 100644 --- a/graphene/new_types/utils.py +++ b/graphene/types/utils.py @@ -6,7 +6,7 @@ from .inputfield import InputField def merge_fields_in_attrs(bases, attrs): - from ..new_types.abstracttype import AbstractType + from ..types.abstracttype import AbstractType for base in bases: if base == AbstractType or not issubclass(base, AbstractType): continue @@ -26,10 +26,10 @@ def unmounted_field_in_type(attname, unmounted_field, type): InputObjectType -> InputField ''' # from ..types.inputobjecttype import InputObjectType - from ..new_types.objecttype import ObjectType - from ..new_types.interface import Interface - from ..new_types.abstracttype import AbstractType - from ..new_types.inputobjecttype import InputObjectType + from ..types.objecttype import ObjectType + from ..types.interface import Interface + from ..types.abstracttype import AbstractType + from ..types.inputobjecttype import InputObjectType if issubclass(type, (ObjectType, Interface)): return unmounted_field.as_field()