From cb812958a2463bb94bcb810bbc6dd854a385de47 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 2 Feb 2016 18:52:00 -0800 Subject: [PATCH 1/2] Added maybe_func utility --- graphene/utils/__init__.py | 3 ++- graphene/utils/maybe_func.py | 7 +++++++ graphene/utils/tests/test_maybe_func.py | 9 +++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 graphene/utils/maybe_func.py create mode 100644 graphene/utils/tests/test_maybe_func.py diff --git a/graphene/utils/__init__.py b/graphene/utils/__init__.py index 6adb1f73..3fcd988c 100644 --- a/graphene/utils/__init__.py +++ b/graphene/utils/__init__.py @@ -1,11 +1,12 @@ from .str_converters import to_camel_case, to_snake_case from .proxy_snake_dict import ProxySnakeDict from .caching import cached_property, memoize +from .maybe_func import maybe_func from .misc import enum_to_graphql_enum from .resolve_only_args import resolve_only_args from .lazylist import LazyList __all__ = ['to_camel_case', 'to_snake_case', 'ProxySnakeDict', - 'cached_property', 'memoize', 'enum_to_graphql_enum', + 'cached_property', 'memoize', 'maybe_func', 'enum_to_graphql_enum', 'resolve_only_args', 'LazyList'] diff --git a/graphene/utils/maybe_func.py b/graphene/utils/maybe_func.py new file mode 100644 index 00000000..26c6a509 --- /dev/null +++ b/graphene/utils/maybe_func.py @@ -0,0 +1,7 @@ +import inspect + + +def maybe_func(f): + if inspect.isfunction(f): + return f() + return f diff --git a/graphene/utils/tests/test_maybe_func.py b/graphene/utils/tests/test_maybe_func.py new file mode 100644 index 00000000..5a447fb0 --- /dev/null +++ b/graphene/utils/tests/test_maybe_func.py @@ -0,0 +1,9 @@ +from ..maybe_func import maybe_func + + +def maybe_func_function(): + assert maybe_func(lambda: True) is True + + +def maybe_func_value(): + assert maybe_func(True) is True From a66f6f299076215a8b712502707669ed2eade25a Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 2 Feb 2016 19:00:36 -0800 Subject: [PATCH 2/2] Added source argument to Fields. Fixed #101 --- graphene/core/classtypes/objecttype.py | 3 ++ graphene/core/types/field.py | 14 +++++++-- graphene/core/types/tests/test_field.py | 38 +++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/graphene/core/classtypes/objecttype.py b/graphene/core/classtypes/objecttype.py index 442d78f5..d542f28c 100644 --- a/graphene/core/classtypes/objecttype.py +++ b/graphene/core/classtypes/objecttype.py @@ -46,6 +46,9 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta, FieldsClassType)): class Meta: abstract = True + def __getattr__(self, name): + return self._root and getattr(self._root, name) + def __init__(self, *args, **kwargs): signals.pre_init.send(self.__class__, args=args, kwargs=kwargs) self._root = kwargs.pop('_root', None) diff --git a/graphene/core/types/field.py b/graphene/core/types/field.py index 3db7ed68..3109ffe0 100644 --- a/graphene/core/types/field.py +++ b/graphene/core/types/field.py @@ -4,6 +4,7 @@ from functools import wraps import six from graphql.core.type import GraphQLField, GraphQLInputObjectField +from ...utils import maybe_func from ..classtypes.base import FieldsClassType from ..classtypes.inputobjecttype import InputObjectType from ..classtypes.mutation import Mutation @@ -18,7 +19,8 @@ class Field(NamedType, OrderedType): def __init__( self, type, description=None, args=None, name=None, resolver=None, - required=False, default=None, deprecation_reason=None, *args_list, **kwargs): + source=None, required=False, default=None, deprecation_reason=None, + *args_list, **kwargs): _creation_counter = kwargs.pop('_creation_counter', None) if isinstance(name, (Argument, ArgumentType)): kwargs['name'] = name @@ -33,7 +35,12 @@ class Field(NamedType, OrderedType): args = OrderedDict(args or {}, **kwargs) self.arguments = ArgumentsGroup(*args_list, **args) self.object_type = None + self.attname = None + self.default_name = None self.resolver_fn = resolver + self.source = source + assert not (self.source and self.resolver_fn), ('You cannot have a source' + ' and a resolver at the same time') self.default = default def contribute_to_class(self, cls, attname): @@ -68,7 +75,8 @@ class Field(NamedType, OrderedType): return getattr(self.object_type, resolve_fn_name) def default_getter(instance, args, info): - return getattr(instance, self.attname, self.default) + value = getattr(instance, self.source or self.attname, self.default) + return maybe_func(value) return default_getter def get_type(self, schema): @@ -80,6 +88,8 @@ class Field(NamedType, OrderedType): return snake_case_args(resolver) def internal_type(self, schema): + if not self.object_type: + raise Exception('The field is not mounted in any ClassType') resolver = self.resolver description = self.description arguments = self.arguments diff --git a/graphene/core/types/tests/test_field.py b/graphene/core/types/tests/test_field.py index 34690223..444e91b5 100644 --- a/graphene/core/types/tests/test_field.py +++ b/graphene/core/types/tests/test_field.py @@ -180,3 +180,41 @@ def test_field_internal_type_deprecated(): type = schema.T(field) assert isinstance(type, GraphQLField) assert type.deprecation_reason == deprecation_reason + + +def test_field_resolve_object(): + class Root(object): + att = True + + @staticmethod + def att_func(): + return True + + field = Field(String(), description='My argument') + field_func = Field(String(), description='My argument') + + class Query(ObjectType): + att = field + att_func = field_func + + assert field.resolver(Root, {}, None) is True + assert field.resolver(Root, {}, None) is True + + +def test_field_resolve_source_object(): + class Root(object): + att_source = True + + @staticmethod + def att_func_source(): + return True + + field = Field(String(), source='att_source', description='My argument') + field_func = Field(String(), source='att_source', description='My argument') + + class Query(ObjectType): + att = field + att_func = field_func + + assert field.resolver(Root, {}, None) is True + assert field.resolver(Root, {}, None) is True