diff --git a/graphene/core/fields.py b/graphene/core/fields.py index 7cb821aa..d2edef38 100644 --- a/graphene/core/fields.py +++ b/graphene/core/fields.py @@ -1,258 +1,38 @@ -import inspect -from functools import total_ordering, wraps - -import six - -from graphene.core.scalars import GraphQLSkipField -from graphene.core.types import BaseObjectType, InputObjectType -from graphene.utils import ProxySnakeDict, enum_to_graphql_enum, to_camel_case -from graphql.core.type import (GraphQLArgument, GraphQLBoolean, GraphQLField, - GraphQLFloat, GraphQLID, - GraphQLInputObjectField, GraphQLInt, - GraphQLList, GraphQLNonNull, GraphQLString) - -try: - from enum import Enum -except ImportError: - class Enum(object): - pass +from .types.field import Field +from .types.scalars import String, Int, Boolean, ID, Float +from .types.definitions import List, NonNull -class Empty(object): +class DeprecatedField(object): + def __init__(self, *args, **kwargs): + print("Using {} is not longer supported".format(self.__class__.__name__)) + kwargs['resolver'] = kwargs.pop('resolve', None) + return super(DeprecatedField, self).__init__(*args, **kwargs) + + +class StringField(DeprecatedField, String): pass -@total_ordering -class Field(object): - SKIP = GraphQLSkipField - creation_counter = 0 - required = False - - def __init__(self, field_type, name=None, resolve=None, required=False, args=None, description='', default=None, **extra_args): - self.field_type = field_type - self.resolve_fn = resolve - self.required = self.required or required - self.args = args or {} - self.extra_args = extra_args - self._type = None - self.name = name - self.description = description or self.__doc__ - self.object_type = None - self.default = default - self.creation_counter = Field.creation_counter - Field.creation_counter += 1 - - def get_default(self): - return self.default - - def contribute_to_class(self, cls, name, add=True): - if not self.name: - self.name = to_camel_case(name) - self.attname = name - self.object_type = cls - if isinstance(self.field_type, Field) and not self.field_type.object_type: - self.field_type.contribute_to_class(cls, name, False) - if add: - cls._meta.add_field(self) - - def resolve(self, instance, args, info): - schema = info and getattr(info.schema, 'graphene_schema', None) - resolve_fn = self.get_resolve_fn(schema) - if resolve_fn: - return resolve_fn(instance, ProxySnakeDict(args), info) - else: - return getattr(instance, self.attname, self.get_default()) - - def get_resolve_fn(self, schema): - object_type = self.get_object_type(schema) - if object_type and object_type._meta.is_mutation: - return object_type.mutate - elif self.resolve_fn: - return self.resolve_fn - else: - custom_resolve_fn_name = 'resolve_%s' % self.attname - if hasattr(self.object_type, custom_resolve_fn_name): - resolve_fn = getattr(self.object_type, custom_resolve_fn_name) - - @wraps(resolve_fn) - def custom_resolve_fn(instance, args, info): - return resolve_fn(instance, args, info) - return custom_resolve_fn - - def get_object_type(self, schema): - field_type = self.field_type - if inspect.isfunction(field_type): - field_type = field_type(self) - _is_class = inspect.isclass(field_type) - if isinstance(field_type, Field): - return field_type.get_object_type(schema) - if _is_class and issubclass(field_type, BaseObjectType): - return field_type - elif isinstance(field_type, six.string_types): - if field_type == 'self': - return self.object_type - else: - return schema.get_type(field_type) - - def type_wrapper(self, field_type): - if self.required: - field_type = GraphQLNonNull(field_type) - return field_type - - def internal_type(self, schema): - field_type = self.field_type - _is_class = inspect.isclass(field_type) - if isinstance(field_type, Field): - field_type = self.field_type.internal_type(schema) - elif _is_class and issubclass(field_type, Enum): - field_type = enum_to_graphql_enum(field_type) - else: - object_type = self.get_object_type(schema) - if object_type: - field_type = schema.T(object_type) - - field_type = self.type_wrapper(field_type) - return field_type - - def internal_field(self, schema): - if not self.object_type: - raise Exception( - 'Field could not be constructed in a non graphene.ObjectType or graphene.Interface') - - extra_args = self.extra_args.copy() - for arg_name, arg_value in self.extra_args.items(): - if isinstance(arg_value, GraphQLArgument): - self.args[arg_name] = arg_value - del extra_args[arg_name] - - if extra_args != {}: - raise TypeError("Field %s.%s initiated with invalid args: %s" % ( - self.object_type, - self.attname, - ','.join(extra_args.keys()) - )) - - args = self.args - - object_type = self.get_object_type(schema) - if object_type and object_type._meta.is_mutation: - assert not self.args, 'Arguments provided for mutations are defined in Input class in Mutation' - args = object_type.get_input_type().fields_as_arguments(schema) - - internal_type = self.internal_type(schema) - if not internal_type: - raise Exception("Internal type for field %s is None" % self) - - description = self.description - resolve_fn = self.get_resolve_fn(schema) - if resolve_fn: - description = resolve_fn.__doc__ or description - - @wraps(resolve_fn) - def resolver(*args): - return self.resolve(*args) - else: - resolver = self.resolve - - if issubclass(self.object_type, InputObjectType): - return GraphQLInputObjectField( - internal_type, - description=description, - ) - - return GraphQLField( - internal_type, - description=description, - args=args, - resolver=resolver, - ) - - def __str__(self): - """ Return "object_type.name". """ - return '%s.%s' % (self.object_type.__name__, self.attname) - - def __repr__(self): - """ - Displays the module, class and name of the field. - """ - path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__) - name = getattr(self, 'attname', None) - if name is not None: - return '<%s: %s>' % (path, name) - return '<%s>' % path - - def __eq__(self, other): - # Needed for @total_ordering - if isinstance(other, Field): - return self.creation_counter == other.creation_counter and \ - self.object_type == other.object_type - return NotImplemented - - def __lt__(self, other): - # This is needed because bisect does not take a comparison function. - if isinstance(other, Field): - return self.creation_counter < other.creation_counter - return NotImplemented - - def __hash__(self): - return hash((self.creation_counter, self.object_type)) - - def __copy__(self): - # We need to avoid hitting __reduce__, so define this - # slightly weird copy construct. - obj = Empty() - obj.__class__ = self.__class__ - obj.__dict__ = self.__dict__.copy() - if self.field_type == 'self': - obj.field_type = self.object_type - return obj +class IntField(DeprecatedField, Int): + pass -class LazyField(Field): - - def inner_field(self, schema): - return self.get_field(schema) - - def internal_type(self, schema): - return self.inner_field(schema).internal_type(schema) - - def internal_field(self, schema): - return self.inner_field(schema).internal_field(schema) +class BooleanField(DeprecatedField, Boolean): + pass -class TypeField(Field): - - def __init__(self, *args, **kwargs): - super(TypeField, self).__init__(self.field_type, *args, **kwargs) +class IDField(DeprecatedField, ID): + pass -class StringField(TypeField): - field_type = GraphQLString +class FloatField(DeprecatedField, Float): + pass -class IntField(TypeField): - field_type = GraphQLInt +class ListField(DeprecatedField, List): + pass -class BooleanField(TypeField): - field_type = GraphQLBoolean - - -class IDField(TypeField): - field_type = GraphQLID - - -class FloatField(TypeField): - field_type = GraphQLFloat - - -class ListField(Field): - - def type_wrapper(self, field_type): - return GraphQLList(field_type) - - -class NonNullField(Field): - - def type_wrapper(self, field_type): - return GraphQLNonNull(field_type) +class NonNullField(DeprecatedField, NonNull): + pass diff --git a/graphene/core/ntypes/tests/__init__.py b/graphene/core/ntypes/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/graphene/core/schema.py b/graphene/core/schema.py index 1ad1141d..66184b61 100644 --- a/graphene/core/schema.py +++ b/graphene/core/schema.py @@ -1,3 +1,4 @@ +import inspect from collections import OrderedDict from graphene import signals @@ -6,6 +7,7 @@ from graphql.core.execution.middlewares.sync import \ SynchronousExecutionMiddleware from graphql.core.type import GraphQLSchema as _GraphQLSchema from graphql.core.utils.introspection_query import introspection_query +from graphene.core.types.base import BaseType class GraphQLSchema(_GraphQLSchema): @@ -34,13 +36,15 @@ class Schema(object): def T(self, object_type): if not object_type: return - if object_type not in self._types: - internal_type = object_type.internal_type(self) - self._types[object_type] = internal_type - name = getattr(internal_type, 'name', None) - if name: - self._types_names[name] = object_type - return self._types[object_type] + # if inspect.isclass(object_type) and issubclass(object_type, BaseType): + if True: + if object_type not in self._types: + internal_type = object_type.internal_type(self) + self._types[object_type] = internal_type + name = getattr(internal_type, 'name', None) + if name: + self._types_names[name] = object_type + return self._types[object_type] @property def query(self): diff --git a/graphene/core/types/__init__.py b/graphene/core/types/__init__.py new file mode 100644 index 00000000..17c5283f --- /dev/null +++ b/graphene/core/types/__init__.py @@ -0,0 +1 @@ +from .objecttype import ObjectTypeMeta, BaseObjectType, ObjectType, Interface, Mutation, InputObjectType diff --git a/graphene/core/ntypes/argument.py b/graphene/core/types/argument.py similarity index 95% rename from graphene/core/ntypes/argument.py rename to graphene/core/types/argument.py index 6e8788ce..2e5ebaf6 100644 --- a/graphene/core/ntypes/argument.py +++ b/graphene/core/types/argument.py @@ -31,7 +31,7 @@ def to_arguments(*args, **kwargs): elif isinstance(arg, ArgumentType): argument = arg.as_argument() else: - raise ValueError('Unknown argument value type %r' % arg) + raise ValueError('Unknown argument %s=%r' % (name, arg)) if name: argument.name = to_camel_case(name) diff --git a/graphene/core/ntypes/base.py b/graphene/core/types/base.py similarity index 86% rename from graphene/core/ntypes/base.py rename to graphene/core/types/base.py index 20b1d271..e1ca98ae 100644 --- a/graphene/core/ntypes/base.py +++ b/graphene/core/types/base.py @@ -1,9 +1,14 @@ from functools import total_ordering -from ..types import BaseObjectType, InputObjectType + + +class BaseType(object): + @classmethod + def internal_type(cls, schema): + return getattr(cls, 'T', None) @total_ordering -class OrderedType(object): +class OrderedType(BaseType): creation_counter = 0 def __init__(self, _creation_counter=None): @@ -38,10 +43,6 @@ class MirroredType(OrderedType): self.args = args self.kwargs = kwargs - @classmethod - def internal_type(cls, schema): - return getattr(cls, 'T', None) - class ArgumentType(MirroredType): def as_argument(self): @@ -51,6 +52,7 @@ class ArgumentType(MirroredType): class FieldType(MirroredType): def contribute_to_class(self, cls, name): + from ..types import BaseObjectType, InputObjectType if issubclass(cls, InputObjectType): inputfield = self.as_inputfield() return inputfield.contribute_to_class(cls, name) @@ -60,11 +62,11 @@ class FieldType(MirroredType): def as_field(self): from .field import Field - return Field(self.__class__, _creation_counter=self.creation_counter, *self.args, **self.kwargs) + return Field(self, _creation_counter=self.creation_counter, *self.args, **self.kwargs) def as_inputfield(self): from .field import InputField - return InputField(self.__class__, _creation_counter=self.creation_counter, *self.args, **self.kwargs) + return InputField(self, _creation_counter=self.creation_counter, *self.args, **self.kwargs) class MountedType(FieldType, ArgumentType): diff --git a/graphene/core/ntypes/definitions.py b/graphene/core/types/definitions.py similarity index 100% rename from graphene/core/ntypes/definitions.py rename to graphene/core/types/definitions.py diff --git a/graphene/core/ntypes/field.py b/graphene/core/types/field.py similarity index 80% rename from graphene/core/ntypes/field.py rename to graphene/core/types/field.py index 13bdcf62..5e03e2d0 100644 --- a/graphene/core/ntypes/field.py +++ b/graphene/core/types/field.py @@ -9,6 +9,10 @@ from ...utils import to_camel_case from ..types import BaseObjectType, InputObjectType +class Empty(object): + pass + + class Field(OrderedType): def __init__(self, type, description=None, args=None, name=None, resolver=None, *args_list, **kwargs): _creation_counter = kwargs.pop('_creation_counter', None) @@ -18,6 +22,7 @@ class Field(OrderedType): self.description = description args = OrderedDict(args or {}, **kwargs) self.arguments = to_arguments(*args_list, **args) + self.object_type = None self.resolver = resolver def contribute_to_class(self, cls, attname): @@ -32,7 +37,7 @@ class Field(OrderedType): @property def resolver(self): - return self._resolver + return self._resolver or self.get_resolver_fn() @resolver.setter def resolver(self, value): @@ -51,8 +56,6 @@ class Field(OrderedType): def internal_type(self, schema): resolver = self.resolver description = self.description - if not resolver: - resolver = self.get_resolver_fn() if not description and resolver: description = resolver.__doc__ @@ -65,6 +68,26 @@ class Field(OrderedType): return OrderedDict([(arg.name, schema.T(arg)) for arg in self.arguments]) + def __copy__(self): + obj = Empty() + obj.__class__ = self.__class__ + obj.__dict__ = self.__dict__.copy() + obj.object_type = None + return obj + + def __repr__(self): + """ + Displays the module, class and name of the field. + """ + path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__) + name = getattr(self, 'attname', None) + if name is not None: + return '<%s: %s>' % (path, name) + return '<%s>' % path + + def __hash__(self): + return hash((self.creation_counter, self.object_type)) + class InputField(OrderedType): def __init__(self, type, description=None, default=None, name=None, _creation_counter=None): diff --git a/graphene/core/types.py b/graphene/core/types/objecttype.py similarity index 97% rename from graphene/core/types.py rename to graphene/core/types/objecttype.py index 2341c0fc..89a77ec3 100644 --- a/graphene/core/types.py +++ b/graphene/core/types/objecttype.py @@ -7,6 +7,7 @@ import six from graphene import signals from graphene.core.options import Options +from graphene.core.types.base import BaseType from graphql.core.type import (GraphQLArgument, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLObjectType) @@ -125,7 +126,7 @@ class ObjectTypeMeta(type): setattr(cls, name, value) -class BaseObjectType(object): +class BaseObjectType(BaseType): def __new__(cls, *args, **kwargs): if cls._meta.is_interface: @@ -185,7 +186,7 @@ class BaseObjectType(object): @classmethod def internal_type(cls, schema): - fields = lambda: OrderedDict([(f.name, f.internal_field(schema)) + fields = lambda: OrderedDict([(f.name, schema.T(f)) for f in cls._meta.fields]) if cls._meta.is_interface: return GraphQLInterfaceType( @@ -222,7 +223,7 @@ class InputObjectType(ObjectType): @classmethod def internal_type(cls, schema): - fields = lambda: OrderedDict([(f.name, f.internal_field(schema)) + fields = lambda: OrderedDict([(f.name, schema.T(f)) for f in cls._meta.fields]) return GraphQLInputObjectType( cls._meta.type_name, diff --git a/graphene/core/ntypes/scalars.py b/graphene/core/types/scalars.py similarity index 100% rename from graphene/core/ntypes/scalars.py rename to graphene/core/types/scalars.py diff --git a/graphene/core/ntypes/__init__.py b/graphene/core/types/tests/__init__.py similarity index 100% rename from graphene/core/ntypes/__init__.py rename to graphene/core/types/tests/__init__.py diff --git a/graphene/core/ntypes/tests/test_argument.py b/graphene/core/types/tests/test_argument.py similarity index 100% rename from graphene/core/ntypes/tests/test_argument.py rename to graphene/core/types/tests/test_argument.py diff --git a/graphene/core/ntypes/tests/test_base.py b/graphene/core/types/tests/test_base.py similarity index 94% rename from graphene/core/ntypes/tests/test_base.py rename to graphene/core/types/tests/test_base.py index 9537f0ea..c512e112 100644 --- a/graphene/core/ntypes/tests/test_base.py +++ b/graphene/core/types/tests/test_base.py @@ -21,7 +21,7 @@ def test_orderedtype_different(): assert b > a -@patch('graphene.core.ntypes.field.Field') +@patch('graphene.core.types.field.Field') def test_type_as_field_called(Field): resolver = lambda x: x a = MountedType(2, description='A', resolver=resolver) @@ -29,7 +29,7 @@ def test_type_as_field_called(Field): Field.assert_called_with(MountedType, 2, _creation_counter=a.creation_counter, description='A', resolver=resolver) -@patch('graphene.core.ntypes.argument.Argument') +@patch('graphene.core.types.argument.Argument') def test_type_as_argument_called(Argument): a = MountedType(2, description='A') a.as_argument() diff --git a/graphene/core/ntypes/tests/test_definitions.py b/graphene/core/types/tests/test_definitions.py similarity index 100% rename from graphene/core/ntypes/tests/test_definitions.py rename to graphene/core/types/tests/test_definitions.py diff --git a/graphene/core/ntypes/tests/test_field.py b/graphene/core/types/tests/test_field.py similarity index 100% rename from graphene/core/ntypes/tests/test_field.py rename to graphene/core/types/tests/test_field.py diff --git a/graphene/core/ntypes/tests/test_scalars.py b/graphene/core/types/tests/test_scalars.py similarity index 100% rename from graphene/core/ntypes/tests/test_scalars.py rename to graphene/core/types/tests/test_scalars.py diff --git a/tests/core/test_fields.py b/tests/core/test_fields.py index be565dd2..d7c4ed12 100644 --- a/tests/core/test_fields.py +++ b/tests/core/test_fields.py @@ -10,9 +10,7 @@ from graphql.core.type import (GraphQLBoolean, GraphQLField, GraphQLID, GraphQLInt, GraphQLNonNull, GraphQLString) -class ObjectType(object): - _meta = Options() - +class ot(ObjectType): def resolve_customdoc(self, *args, **kwargs): '''Resolver documentation''' return None @@ -20,22 +18,20 @@ class ObjectType(object): def __str__(self): return "ObjectType" -ot = ObjectType - schema = Schema() def test_field_no_contributed_raises_error(): f = Field(GraphQLString) with raises(Exception) as excinfo: - f.internal_field(schema) + schema.T(f) def test_field_type(): f = Field(GraphQLString) f.contribute_to_class(ot, 'field_name') - assert isinstance(f.internal_field(schema), GraphQLField) - assert f.internal_type(schema) == GraphQLString + assert isinstance(schema.T(f), GraphQLField) + assert schema.T(f).type == GraphQLString def test_field_name_automatic_camelcase(): diff --git a/tests/core/test_query.py b/tests/core/test_query.py index 4814460f..739bbdc6 100644 --- a/tests/core/test_query.py +++ b/tests/core/test_query.py @@ -4,8 +4,7 @@ from graphene.core.fields import Field, ListField, StringField from graphene.core.schema import Schema from graphene.core.types import Interface, ObjectType from graphql.core import graphql -from graphql.core.type import (GraphQLInterfaceType, GraphQLObjectType, - GraphQLSchema) +from graphql.core.type import GraphQLSchema class Character(Interface): @@ -38,8 +37,8 @@ Human_type = schema.T(Human) def test_type(): - assert Human._meta.fields_map['name'].resolve( - Human(object()), None, None) == 'Peter' + assert Human._meta.fields_map['name'].resolver( + Human(object()), {}, None) == 'Peter' def test_query():