From a7b3d193ebb372bf3181ff7bc4e410644d62e6f1 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 13 Aug 2016 01:50:32 -0700 Subject: [PATCH] Added Field arguments. --- graphene/new_types/argument.py | 33 +++++++++++++++++++++ graphene/new_types/field.py | 7 +++-- graphene/new_types/tests/test_field.py | 18 ++++++++++- graphene/new_types/tests/test_interface.py | 10 +++---- graphene/new_types/tests/test_objecttype.py | 10 +++---- graphene/new_types/unmountedtype.py | 21 ++++++------- graphene/new_types/utils.py | 10 +++++-- 7 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 graphene/new_types/argument.py diff --git a/graphene/new_types/argument.py b/graphene/new_types/argument.py new file mode 100644 index 00000000..b16aef47 --- /dev/null +++ b/graphene/new_types/argument.py @@ -0,0 +1,33 @@ +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/field.py b/graphene/new_types/field.py index d03787c0..f5fba407 100644 --- a/graphene/new_types/field.py +++ b/graphene/new_types/field.py @@ -1,9 +1,10 @@ import inspect from functools import partial -from collections import OrderedDict +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): @@ -25,7 +26,9 @@ class Field(OrderedType): if required: type = NonNull(type) self._type = type - self.args = args or OrderedDict() + if args: + assert isinstance(args, Mapping), 'Arguments in a field have to be a mapping, received "{}".'.format(args) + self.args = to_arguments(args or OrderedDict(), extra_args) # self.args = to_arguments(args, extra_args) assert not (source and resolver), ('A Field cannot have a source and a ' 'resolver in at the same time.') diff --git a/graphene/new_types/tests/test_field.py b/graphene/new_types/tests/test_field.py index 6ecc9427..ba537361 100644 --- a/graphene/new_types/tests/test_field.py +++ b/graphene/new_types/tests/test_field.py @@ -2,6 +2,7 @@ import pytest from ..field import Field from ..structures import NonNull +from ..argument import Argument class MyInstance(object): @@ -11,7 +12,7 @@ class MyInstance(object): def test_field_basic(): MyType = object() - args = {} + args = {'my arg': Argument(True)} resolver = lambda: None deprecation_reason = 'Deprecated now' description = 'My Field' @@ -49,7 +50,22 @@ def test_field_not_source_and_resolver(): 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_interface.py b/graphene/new_types/tests/test_interface.py index f53b997e..97804c5c 100644 --- a/graphene/new_types/tests/test_interface.py +++ b/graphene/new_types/tests/test_interface.py @@ -54,7 +54,7 @@ def test_ordered_fields_in_interface(): def test_generate_interface_unmountedtype(): class MyInterface(Interface): - field = MyScalar(MyType) + field = MyScalar() assert 'field' in MyInterface._meta.fields assert isinstance(MyInterface._meta.fields['field'], Field) @@ -62,10 +62,10 @@ def test_generate_interface_unmountedtype(): def test_generate_interface_inherit_abstracttype(): class MyAbstractType(AbstractType): - field1 = MyScalar(MyType) + field1 = MyScalar() class MyInterface(Interface, MyAbstractType): - field2 = MyScalar(MyType) + field2 = MyScalar() assert MyInterface._meta.fields.keys() == ['field1', 'field2'] assert [type(x) for x in MyInterface._meta.fields.values()] == [Field, Field] @@ -73,10 +73,10 @@ def test_generate_interface_inherit_abstracttype(): def test_generate_interface_inherit_abstracttype_reversed(): class MyAbstractType(AbstractType): - field1 = MyScalar(MyType) + field1 = MyScalar() class MyInterface(MyAbstractType, Interface): - field2 = MyScalar(MyType) + 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_objecttype.py b/graphene/new_types/tests/test_objecttype.py index 7ec66107..9b8b3913 100644 --- a/graphene/new_types/tests/test_objecttype.py +++ b/graphene/new_types/tests/test_objecttype.py @@ -62,10 +62,10 @@ def test_ordered_fields_in_objecttype(): def test_generate_objecttype_inherit_abstracttype(): class MyAbstractType(AbstractType): - field1 = MyScalar(MyType) + field1 = MyScalar() class MyObjectType(ObjectType, MyAbstractType): - field2 = MyScalar(MyType) + field2 = MyScalar() assert MyObjectType._meta.fields.keys() == ['field1', 'field2'] assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field] @@ -73,10 +73,10 @@ def test_generate_objecttype_inherit_abstracttype(): def test_generate_objecttype_inherit_abstracttype_reversed(): class MyAbstractType(AbstractType): - field1 = MyScalar(MyType) + field1 = MyScalar() class MyObjectType(MyAbstractType, ObjectType): - field2 = MyScalar(MyType) + field2 = MyScalar() assert MyObjectType._meta.fields.keys() == ['field1', 'field2'] assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field] @@ -84,7 +84,7 @@ def test_generate_objecttype_inherit_abstracttype_reversed(): def test_generate_objecttype_unmountedtype(): class MyObjectType(ObjectType): - field = MyScalar(MyType) + field = MyScalar() assert 'field' in MyObjectType._meta.fields assert isinstance(MyObjectType._meta.fields['field'], Field) diff --git a/graphene/new_types/unmountedtype.py b/graphene/new_types/unmountedtype.py index 84f98fc3..0fc34744 100644 --- a/graphene/new_types/unmountedtype.py +++ b/graphene/new_types/unmountedtype.py @@ -49,13 +49,14 @@ class UnmountedType(OrderedType): **self.kwargs ) - # def as_argument(self): - # ''' - # Mount the UnmountedType as Argument - # ''' - # return Argument( - # self.get_type(), - # *self.args, - # _creation_counter=self.creation_counter, - # **self.kwargs - # ) + 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 + ) diff --git a/graphene/new_types/utils.py b/graphene/new_types/utils.py index f672a943..efe80689 100644 --- a/graphene/new_types/utils.py +++ b/graphene/new_types/utils.py @@ -54,9 +54,13 @@ def get_fields_in_type(in_type, attrs): (attname, value) ) elif isinstance(value, UnmountedType): - fields_with_names.append( - (attname, unmounted_field_in_type(attname, value, in_type)) - ) + try: + value = unmounted_field_in_type(attname, value, in_type) + fields_with_names.append( + (attname, value) + ) + except Exception as e: + raise Exception('Exception while mounting the field "{}": {}'.format(attname, str(e))) return OrderedDict(sorted(fields_with_names, key=lambda f: f[1]))