mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-23 01:56:54 +03:00
Merge branch 'refs/heads/features/plugins-autocamelcase' into features/django-debug
This commit is contained in:
commit
2ad5bc203a
|
@ -27,7 +27,7 @@ class ConnectionOrListField(Field):
|
||||||
if not field_object_type:
|
if not field_object_type:
|
||||||
raise SkipField()
|
raise SkipField()
|
||||||
if is_node(field_object_type):
|
if is_node(field_object_type):
|
||||||
field = DjangoConnectionField(field_object_type)
|
field = ConnectionField(field_object_type)
|
||||||
else:
|
else:
|
||||||
field = Field(List(field_object_type))
|
field = Field(List(field_object_type))
|
||||||
field.contribute_to_class(self.object_type, self.attname)
|
field.contribute_to_class(self.object_type, self.attname)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.manager import Manager
|
from django.db.models.manager import Manager
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
|
||||||
|
from graphene.utils import LazyList
|
||||||
|
|
||||||
|
|
||||||
def get_type_for_model(schema, model):
|
def get_type_for_model(schema, model):
|
||||||
|
@ -19,7 +22,18 @@ def get_reverse_fields(model):
|
||||||
yield related
|
yield related
|
||||||
|
|
||||||
|
|
||||||
|
class WrappedQueryset(LazyList):
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
# Dont calculate the length using len(queryset), as this will
|
||||||
|
# evaluate the whole queryset and return it's length.
|
||||||
|
# Use .count() instead
|
||||||
|
return self._origin.count()
|
||||||
|
|
||||||
|
|
||||||
def maybe_queryset(value):
|
def maybe_queryset(value):
|
||||||
if isinstance(value, Manager):
|
if isinstance(value, Manager):
|
||||||
value = value.get_queryset()
|
value = value.get_queryset()
|
||||||
|
if isinstance(value, QuerySet):
|
||||||
|
return WrappedQueryset(value)
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import copy
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
|
from functools import partial
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ..exceptions import SkipField
|
|
||||||
from .options import Options
|
from .options import Options
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,8 +48,8 @@ class ClassTypeMeta(type):
|
||||||
|
|
||||||
if not cls._meta.abstract:
|
if not cls._meta.abstract:
|
||||||
from ..types import List, NonNull
|
from ..types import List, NonNull
|
||||||
setattr(cls, 'NonNull', NonNull(cls))
|
setattr(cls, 'NonNull', partial(NonNull, cls))
|
||||||
setattr(cls, 'List', List(cls))
|
setattr(cls, 'List', partial(List, cls))
|
||||||
|
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
@ -81,13 +81,18 @@ class FieldsOptions(Options):
|
||||||
def fields_map(self):
|
def fields_map(self):
|
||||||
return OrderedDict([(f.attname, f) for f in self.fields])
|
return OrderedDict([(f.attname, f) for f in self.fields])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fields_group_type(self):
|
||||||
|
from ..types.field import FieldsGroupType
|
||||||
|
return FieldsGroupType(*self.local_fields)
|
||||||
|
|
||||||
|
|
||||||
class FieldsClassTypeMeta(ClassTypeMeta):
|
class FieldsClassTypeMeta(ClassTypeMeta):
|
||||||
options_class = FieldsOptions
|
options_class = FieldsOptions
|
||||||
|
|
||||||
def extend_fields(cls, bases):
|
def extend_fields(cls, bases):
|
||||||
new_fields = cls._meta.local_fields
|
new_fields = cls._meta.local_fields
|
||||||
field_names = {f.name: f for f in new_fields}
|
field_names = {f.attname: f for f in new_fields}
|
||||||
|
|
||||||
for base in bases:
|
for base in bases:
|
||||||
if not isinstance(base, FieldsClassTypeMeta):
|
if not isinstance(base, FieldsClassTypeMeta):
|
||||||
|
@ -95,17 +100,17 @@ class FieldsClassTypeMeta(ClassTypeMeta):
|
||||||
|
|
||||||
parent_fields = base._meta.local_fields
|
parent_fields = base._meta.local_fields
|
||||||
for field in parent_fields:
|
for field in parent_fields:
|
||||||
if field.name in field_names and field.type.__class__ != field_names[
|
if field.attname in field_names and field.type.__class__ != field_names[
|
||||||
field.name].type.__class__:
|
field.attname].type.__class__:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Local field %r in class %r (%r) clashes '
|
'Local field %r in class %r (%r) clashes '
|
||||||
'with field with similar name from '
|
'with field with similar name from '
|
||||||
'Interface %s (%r)' % (
|
'Interface %s (%r)' % (
|
||||||
field.name,
|
field.attname,
|
||||||
cls.__name__,
|
cls.__name__,
|
||||||
field.__class__,
|
field.__class__,
|
||||||
base.__name__,
|
base.__name__,
|
||||||
field_names[field.name].__class__)
|
field_names[field.attname].__class__)
|
||||||
)
|
)
|
||||||
new_field = copy.copy(field)
|
new_field = copy.copy(field)
|
||||||
cls.add_to_class(field.attname, new_field)
|
cls.add_to_class(field.attname, new_field)
|
||||||
|
@ -123,11 +128,4 @@ class FieldsClassType(six.with_metaclass(FieldsClassTypeMeta, ClassType)):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fields_internal_types(cls, schema):
|
def fields_internal_types(cls, schema):
|
||||||
fields = []
|
return schema.T(cls._meta.fields_group_type)
|
||||||
for field in cls._meta.fields:
|
|
||||||
try:
|
|
||||||
fields.append((field.name, schema.T(field)))
|
|
||||||
except SkipField:
|
|
||||||
continue
|
|
||||||
|
|
||||||
return OrderedDict(fields)
|
|
||||||
|
|
|
@ -23,15 +23,26 @@ def test_classtype_advanced():
|
||||||
def test_classtype_definition_list():
|
def test_classtype_definition_list():
|
||||||
class Character(ClassType):
|
class Character(ClassType):
|
||||||
'''Character description'''
|
'''Character description'''
|
||||||
assert isinstance(Character.List, List)
|
assert isinstance(Character.List(), List)
|
||||||
assert Character.List.of_type == Character
|
assert Character.List().of_type == Character
|
||||||
|
|
||||||
|
|
||||||
def test_classtype_definition_nonnull():
|
def test_classtype_definition_nonnull():
|
||||||
class Character(ClassType):
|
class Character(ClassType):
|
||||||
'''Character description'''
|
'''Character description'''
|
||||||
assert isinstance(Character.NonNull, NonNull)
|
assert isinstance(Character.NonNull(), NonNull)
|
||||||
assert Character.NonNull.of_type == Character
|
assert Character.NonNull().of_type == Character
|
||||||
|
|
||||||
|
|
||||||
|
def test_fieldsclasstype_definition_order():
|
||||||
|
class Character(ClassType):
|
||||||
|
'''Character description'''
|
||||||
|
|
||||||
|
class Query(FieldsClassType):
|
||||||
|
name = String()
|
||||||
|
char = Character.NonNull()
|
||||||
|
|
||||||
|
assert list(Query._meta.fields_map.keys()) == ['name', 'char']
|
||||||
|
|
||||||
|
|
||||||
def test_fieldsclasstype():
|
def test_fieldsclasstype():
|
||||||
|
|
|
@ -24,4 +24,4 @@ def test_mutation():
|
||||||
assert list(object_type.get_fields().keys()) == ['name']
|
assert list(object_type.get_fields().keys()) == ['name']
|
||||||
assert MyMutation._meta.fields_map['name'].object_type == MyMutation
|
assert MyMutation._meta.fields_map['name'].object_type == MyMutation
|
||||||
assert isinstance(MyMutation.arguments, ArgumentsGroup)
|
assert isinstance(MyMutation.arguments, ArgumentsGroup)
|
||||||
assert 'argName' in MyMutation.arguments
|
assert 'argName' in schema.T(MyMutation.arguments)
|
||||||
|
|
|
@ -12,6 +12,7 @@ from graphene import signals
|
||||||
|
|
||||||
from .classtypes.base import ClassType
|
from .classtypes.base import ClassType
|
||||||
from .types.base import BaseType
|
from .types.base import BaseType
|
||||||
|
from ..plugins import Plugin, CamelCase
|
||||||
|
|
||||||
|
|
||||||
class GraphQLSchema(_GraphQLSchema):
|
class GraphQLSchema(_GraphQLSchema):
|
||||||
|
@ -25,7 +26,7 @@ class Schema(object):
|
||||||
_executor = None
|
_executor = None
|
||||||
|
|
||||||
def __init__(self, query=None, mutation=None, subscription=None,
|
def __init__(self, query=None, mutation=None, subscription=None,
|
||||||
name='Schema', executor=None):
|
name='Schema', executor=None, plugins=None, auto_camelcase=True):
|
||||||
self._types_names = {}
|
self._types_names = {}
|
||||||
self._types = {}
|
self._types = {}
|
||||||
self.mutation = mutation
|
self.mutation = mutation
|
||||||
|
@ -33,11 +34,27 @@ class Schema(object):
|
||||||
self.subscription = subscription
|
self.subscription = subscription
|
||||||
self.name = name
|
self.name = name
|
||||||
self.executor = executor
|
self.executor = executor
|
||||||
|
self.plugins = []
|
||||||
|
plugins = plugins or []
|
||||||
|
if auto_camelcase:
|
||||||
|
plugins.append(CamelCase())
|
||||||
|
for plugin in plugins:
|
||||||
|
self.add_plugin(plugin)
|
||||||
signals.init_schema.send(self)
|
signals.init_schema.send(self)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Schema: %s (%s)>' % (str(self.name), hash(self))
|
return '<Schema: %s (%s)>' % (str(self.name), hash(self))
|
||||||
|
|
||||||
|
def add_plugin(self, plugin):
|
||||||
|
assert isinstance(plugin, Plugin), 'A plugin need to subclass graphene.Plugin and be instantiated'
|
||||||
|
plugin.contribute_to_schema(self)
|
||||||
|
self.plugins.append(plugin)
|
||||||
|
|
||||||
|
def get_internal_type(self, objecttype):
|
||||||
|
for plugin in self.plugins:
|
||||||
|
objecttype = plugin.transform_type(objecttype)
|
||||||
|
return objecttype.internal_type(self)
|
||||||
|
|
||||||
def T(self, object_type):
|
def T(self, object_type):
|
||||||
if not object_type:
|
if not object_type:
|
||||||
return
|
return
|
||||||
|
@ -45,7 +62,7 @@ class Schema(object):
|
||||||
object_type, (BaseType, ClassType)) or isinstance(
|
object_type, (BaseType, ClassType)) or isinstance(
|
||||||
object_type, BaseType):
|
object_type, BaseType):
|
||||||
if object_type not in self._types:
|
if object_type not in self._types:
|
||||||
internal_type = object_type.internal_type(self)
|
internal_type = self.get_internal_type(object_type)
|
||||||
self._types[object_type] = internal_type
|
self._types[object_type] = internal_type
|
||||||
is_objecttype = inspect.isclass(
|
is_objecttype = inspect.isclass(
|
||||||
object_type) and issubclass(object_type, ClassType)
|
object_type) and issubclass(object_type, ClassType)
|
||||||
|
|
|
@ -34,10 +34,11 @@ def test_field_type():
|
||||||
assert schema.T(f).type == GraphQLString
|
assert schema.T(f).type == GraphQLString
|
||||||
|
|
||||||
|
|
||||||
def test_field_name_automatic_camelcase():
|
def test_field_name():
|
||||||
f = Field(GraphQLString)
|
f = Field(GraphQLString)
|
||||||
f.contribute_to_class(MyOt, 'field_name')
|
f.contribute_to_class(MyOt, 'field_name')
|
||||||
assert f.name == 'fieldName'
|
assert f.name is None
|
||||||
|
assert f.attname == 'field_name'
|
||||||
|
|
||||||
|
|
||||||
def test_field_name_use_name_if_exists():
|
def test_field_name_use_name_if_exists():
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
from collections import OrderedDict
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from graphql.core.type import GraphQLArgument
|
from graphql.core.type import GraphQLArgument
|
||||||
|
|
||||||
from ...utils import ProxySnakeDict, to_camel_case
|
from ...utils import ProxySnakeDict
|
||||||
from .base import ArgumentType, BaseType, OrderedType
|
from .base import ArgumentType, GroupNamedType, NamedType, OrderedType
|
||||||
|
|
||||||
|
|
||||||
class Argument(OrderedType):
|
class Argument(NamedType, OrderedType):
|
||||||
|
|
||||||
def __init__(self, type, description=None, default=None,
|
def __init__(self, type, description=None, default=None,
|
||||||
name=None, _creation_counter=None):
|
name=None, _creation_counter=None):
|
||||||
super(Argument, self).__init__(_creation_counter=_creation_counter)
|
super(Argument, self).__init__(_creation_counter=_creation_counter)
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.attname = None
|
||||||
self.type = type
|
self.type = type
|
||||||
self.description = description
|
self.description = description
|
||||||
self.default = default
|
self.default = default
|
||||||
|
@ -27,47 +27,32 @@ class Argument(OrderedType):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class ArgumentsGroup(BaseType):
|
class ArgumentsGroup(GroupNamedType):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
arguments = to_arguments(*args, **kwargs)
|
arguments = to_arguments(*args, **kwargs)
|
||||||
self.arguments = OrderedDict([(arg.name, arg) for arg in arguments])
|
super(ArgumentsGroup, self).__init__(*arguments)
|
||||||
|
|
||||||
def internal_type(self, schema):
|
|
||||||
return OrderedDict([(arg.name, schema.T(arg))
|
|
||||||
for arg in self.arguments.values()])
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.arguments)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(self.arguments)
|
|
||||||
|
|
||||||
def __contains__(self, *args):
|
|
||||||
return self.arguments.__contains__(*args)
|
|
||||||
|
|
||||||
def __getitem__(self, *args):
|
|
||||||
return self.arguments.__getitem__(*args)
|
|
||||||
|
|
||||||
|
|
||||||
def to_arguments(*args, **kwargs):
|
def to_arguments(*args, **kwargs):
|
||||||
arguments = {}
|
arguments = {}
|
||||||
iter_arguments = chain(kwargs.items(), [(None, a) for a in args])
|
iter_arguments = chain(kwargs.items(), [(None, a) for a in args])
|
||||||
|
|
||||||
for name, arg in iter_arguments:
|
for attname, arg in iter_arguments:
|
||||||
if isinstance(arg, Argument):
|
if isinstance(arg, Argument):
|
||||||
argument = arg
|
argument = arg
|
||||||
elif isinstance(arg, ArgumentType):
|
elif isinstance(arg, ArgumentType):
|
||||||
argument = arg.as_argument()
|
argument = arg.as_argument()
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown argument %s=%r' % (name, arg))
|
raise ValueError('Unknown argument %s=%r' % (attname, arg))
|
||||||
|
|
||||||
if name:
|
if attname:
|
||||||
argument.name = to_camel_case(name)
|
argument.attname = attname
|
||||||
assert argument.name, 'Argument in field must have a name'
|
|
||||||
assert argument.name not in arguments, 'Found more than one Argument with same name {}'.format(
|
name = argument.name or argument.attname
|
||||||
argument.name)
|
assert name, 'Argument in field must have a name'
|
||||||
arguments[argument.name] = argument
|
assert name not in arguments, 'Found more than one Argument with same name {}'.format(name)
|
||||||
|
arguments[name] = argument
|
||||||
|
|
||||||
return sorted(arguments.values())
|
return sorted(arguments.values())
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from functools import total_ordering
|
from collections import OrderedDict
|
||||||
|
from functools import total_ordering, partial
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from ...utils import to_camel_case
|
||||||
|
|
||||||
|
|
||||||
class BaseType(object):
|
class BaseType(object):
|
||||||
|
|
||||||
|
@ -126,3 +129,31 @@ class FieldType(MirroredType):
|
||||||
|
|
||||||
class MountedType(FieldType, ArgumentType):
|
class MountedType(FieldType, ArgumentType):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NamedType(BaseType):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GroupNamedType(BaseType):
|
||||||
|
def __init__(self, *types):
|
||||||
|
self.types = types
|
||||||
|
|
||||||
|
def get_named_type(self, schema, type):
|
||||||
|
name = type.name or type.attname
|
||||||
|
return name, schema.T(type)
|
||||||
|
|
||||||
|
def internal_type(self, schema):
|
||||||
|
return OrderedDict(map(partial(self.get_named_type, schema), self.types))
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.types)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.types)
|
||||||
|
|
||||||
|
def __contains__(self, *args):
|
||||||
|
return self.types.__contains__(*args)
|
||||||
|
|
||||||
|
def __getitem__(self, *args):
|
||||||
|
return self.types.__getitem__(*args)
|
||||||
|
|
|
@ -4,16 +4,16 @@ from functools import wraps
|
||||||
import six
|
import six
|
||||||
from graphql.core.type import GraphQLField, GraphQLInputObjectField
|
from graphql.core.type import GraphQLField, GraphQLInputObjectField
|
||||||
|
|
||||||
from ...utils import to_camel_case
|
|
||||||
from ..classtypes.base import FieldsClassType
|
from ..classtypes.base import FieldsClassType
|
||||||
from ..classtypes.inputobjecttype import InputObjectType
|
from ..classtypes.inputobjecttype import InputObjectType
|
||||||
from ..classtypes.mutation import Mutation
|
from ..classtypes.mutation import Mutation
|
||||||
|
from ..exceptions import SkipField
|
||||||
from .argument import ArgumentsGroup, snake_case_args
|
from .argument import ArgumentsGroup, snake_case_args
|
||||||
from .base import LazyType, MountType, OrderedType
|
from .base import LazyType, NamedType, MountType, OrderedType, GroupNamedType
|
||||||
from .definitions import NonNull
|
from .definitions import NonNull
|
||||||
|
|
||||||
|
|
||||||
class Field(OrderedType):
|
class Field(NamedType, OrderedType):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, type, description=None, args=None, name=None, resolver=None,
|
self, type, description=None, args=None, name=None, resolver=None,
|
||||||
|
@ -36,8 +36,6 @@ class Field(OrderedType):
|
||||||
assert issubclass(
|
assert issubclass(
|
||||||
cls, (FieldsClassType)), 'Field {} cannot be mounted in {}'.format(
|
cls, (FieldsClassType)), 'Field {} cannot be mounted in {}'.format(
|
||||||
self, cls)
|
self, cls)
|
||||||
if not self.name:
|
|
||||||
self.name = to_camel_case(attname)
|
|
||||||
self.attname = attname
|
self.attname = attname
|
||||||
self.object_type = cls
|
self.object_type = cls
|
||||||
self.mount(cls)
|
self.mount(cls)
|
||||||
|
@ -63,6 +61,9 @@ class Field(OrderedType):
|
||||||
return NonNull(self.type)
|
return NonNull(self.type)
|
||||||
return self.type
|
return self.type
|
||||||
|
|
||||||
|
def decorate_resolver(self, resolver):
|
||||||
|
return snake_case_args(resolver)
|
||||||
|
|
||||||
def internal_type(self, schema):
|
def internal_type(self, schema):
|
||||||
resolver = self.resolver
|
resolver = self.resolver
|
||||||
description = self.description
|
description = self.description
|
||||||
|
@ -85,9 +86,9 @@ class Field(OrderedType):
|
||||||
return my_resolver(instance, args, info)
|
return my_resolver(instance, args, info)
|
||||||
resolver = wrapped_func
|
resolver = wrapped_func
|
||||||
|
|
||||||
resolver = snake_case_args(resolver)
|
|
||||||
assert type, 'Internal type for field %s is None' % str(self)
|
assert type, 'Internal type for field %s is None' % str(self)
|
||||||
return GraphQLField(type, args=schema.T(arguments), resolver=resolver,
|
return GraphQLField(type, args=schema.T(arguments),
|
||||||
|
resolver=self.decorate_resolver(resolver),
|
||||||
description=description,)
|
description=description,)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -114,7 +115,7 @@ class Field(OrderedType):
|
||||||
return hash((self.creation_counter, self.object_type))
|
return hash((self.creation_counter, self.object_type))
|
||||||
|
|
||||||
|
|
||||||
class InputField(OrderedType):
|
class InputField(NamedType, OrderedType):
|
||||||
|
|
||||||
def __init__(self, type, description=None, default=None,
|
def __init__(self, type, description=None, default=None,
|
||||||
name=None, _creation_counter=None, required=False):
|
name=None, _creation_counter=None, required=False):
|
||||||
|
@ -130,8 +131,6 @@ class InputField(OrderedType):
|
||||||
assert issubclass(
|
assert issubclass(
|
||||||
cls, (InputObjectType)), 'InputField {} cannot be mounted in {}'.format(
|
cls, (InputObjectType)), 'InputField {} cannot be mounted in {}'.format(
|
||||||
self, cls)
|
self, cls)
|
||||||
if not self.name:
|
|
||||||
self.name = to_camel_case(attname)
|
|
||||||
self.attname = attname
|
self.attname = attname
|
||||||
self.object_type = cls
|
self.object_type = cls
|
||||||
self.mount(cls)
|
self.mount(cls)
|
||||||
|
@ -143,3 +142,14 @@ class InputField(OrderedType):
|
||||||
return GraphQLInputObjectField(
|
return GraphQLInputObjectField(
|
||||||
schema.T(self.type),
|
schema.T(self.type),
|
||||||
default_value=self.default, description=self.description)
|
default_value=self.default, description=self.description)
|
||||||
|
|
||||||
|
|
||||||
|
class FieldsGroupType(GroupNamedType):
|
||||||
|
def internal_type(self, schema):
|
||||||
|
fields = []
|
||||||
|
for field in sorted(self.types):
|
||||||
|
try:
|
||||||
|
fields.append(self.get_named_type(schema, field))
|
||||||
|
except SkipField:
|
||||||
|
continue
|
||||||
|
return OrderedDict(fields)
|
||||||
|
|
|
@ -27,8 +27,8 @@ def test_to_arguments():
|
||||||
other_kwarg=String(),
|
other_kwarg=String(),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert [a.name for a in arguments] == [
|
assert [a.name or a.attname for a in arguments] == [
|
||||||
'myArg', 'otherArg', 'myKwarg', 'otherKwarg']
|
'myArg', 'otherArg', 'my_kwarg', 'other_kwarg']
|
||||||
|
|
||||||
|
|
||||||
def test_to_arguments_no_name():
|
def test_to_arguments_no_name():
|
||||||
|
|
|
@ -20,7 +20,7 @@ def test_field_internal_type():
|
||||||
schema = Schema(query=Query)
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
type = schema.T(field)
|
type = schema.T(field)
|
||||||
assert field.name == 'myField'
|
assert field.name is None
|
||||||
assert field.attname == 'my_field'
|
assert field.attname == 'my_field'
|
||||||
assert isinstance(type, GraphQLField)
|
assert isinstance(type, GraphQLField)
|
||||||
assert type.description == 'My argument'
|
assert type.description == 'My argument'
|
||||||
|
@ -98,9 +98,10 @@ def test_field_string_reference():
|
||||||
|
|
||||||
def test_field_custom_arguments():
|
def test_field_custom_arguments():
|
||||||
field = Field(None, name='my_customName', p=String())
|
field = Field(None, name='my_customName', p=String())
|
||||||
|
schema = Schema()
|
||||||
|
|
||||||
args = field.arguments
|
args = field.arguments
|
||||||
assert 'p' in args
|
assert 'p' in schema.T(args)
|
||||||
|
|
||||||
|
|
||||||
def test_inputfield_internal_type():
|
def test_inputfield_internal_type():
|
||||||
|
@ -115,7 +116,7 @@ def test_inputfield_internal_type():
|
||||||
schema = Schema(query=MyObjectType)
|
schema = Schema(query=MyObjectType)
|
||||||
|
|
||||||
type = schema.T(field)
|
type = schema.T(field)
|
||||||
assert field.name == 'myField'
|
assert field.name is None
|
||||||
assert field.attname == 'my_field'
|
assert field.attname == 'my_field'
|
||||||
assert isinstance(type, GraphQLInputObjectField)
|
assert isinstance(type, GraphQLInputObjectField)
|
||||||
assert type.description == 'My input field'
|
assert type.description == 'My input field'
|
||||||
|
|
6
graphene/plugins/__init__.py
Normal file
6
graphene/plugins/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from .base import Plugin
|
||||||
|
from .camel_case import CamelCase
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'Plugin', 'CamelCase'
|
||||||
|
]
|
6
graphene/plugins/base.py
Normal file
6
graphene/plugins/base.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
class Plugin(object):
|
||||||
|
def contribute_to_schema(self, schema):
|
||||||
|
self.schema = schema
|
||||||
|
|
||||||
|
def transform_type(self, objecttype):
|
||||||
|
return objecttype
|
22
graphene/plugins/camel_case.py
Normal file
22
graphene/plugins/camel_case.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from .base import Plugin
|
||||||
|
|
||||||
|
from ..core.types.base import GroupNamedType
|
||||||
|
from ..utils import memoize, to_camel_case
|
||||||
|
|
||||||
|
|
||||||
|
def camelcase_named_type(schema, type):
|
||||||
|
name = type.name or to_camel_case(type.attname)
|
||||||
|
return name, schema.T(type)
|
||||||
|
|
||||||
|
|
||||||
|
class CamelCase(Plugin):
|
||||||
|
@memoize
|
||||||
|
def transform_group(self, _type):
|
||||||
|
new_type = _type.__class__(*_type.types)
|
||||||
|
setattr(new_type, 'get_named_type', camelcase_named_type)
|
||||||
|
return new_type
|
||||||
|
|
||||||
|
def transform_type(self, _type):
|
||||||
|
if isinstance(_type, GroupNamedType):
|
||||||
|
return self.transform_group(_type)
|
||||||
|
return _type
|
|
@ -34,8 +34,7 @@ schema = Schema(query=Query, mutation=MyResultMutation)
|
||||||
|
|
||||||
def test_mutation_arguments():
|
def test_mutation_arguments():
|
||||||
assert ChangeNumber.arguments
|
assert ChangeNumber.arguments
|
||||||
assert list(ChangeNumber.arguments) == ['input']
|
assert 'input' in schema.T(ChangeNumber.arguments)
|
||||||
assert 'input' in ChangeNumber.arguments
|
|
||||||
inner_type = ChangeNumber.input_type
|
inner_type = ChangeNumber.input_type
|
||||||
client_mutation_id_field = inner_type._meta.fields_map[
|
client_mutation_id_field = inner_type._meta.fields_map[
|
||||||
'client_mutation_id']
|
'client_mutation_id']
|
||||||
|
|
|
@ -3,8 +3,9 @@ from .proxy_snake_dict import ProxySnakeDict
|
||||||
from .caching import cached_property, memoize
|
from .caching import cached_property, memoize
|
||||||
from .misc import enum_to_graphql_enum
|
from .misc import enum_to_graphql_enum
|
||||||
from .resolve_only_args import resolve_only_args
|
from .resolve_only_args import resolve_only_args
|
||||||
|
from .lazylist import LazyList
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['to_camel_case', 'to_snake_case', 'ProxySnakeDict',
|
__all__ = ['to_camel_case', 'to_snake_case', 'ProxySnakeDict',
|
||||||
'cached_property', 'memoize', 'enum_to_graphql_enum',
|
'cached_property', 'memoize', 'enum_to_graphql_enum',
|
||||||
'resolve_only_args']
|
'resolve_only_args', 'LazyList']
|
||||||
|
|
43
graphene/utils/lazylist.py
Normal file
43
graphene/utils/lazylist.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
class LazyList(object):
|
||||||
|
|
||||||
|
def __init__(self, origin, state=None):
|
||||||
|
self._origin = origin
|
||||||
|
self._state = state or []
|
||||||
|
self._origin_iter = None
|
||||||
|
self._finished = False
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self if not self._finished else iter(self._state)
|
||||||
|
|
||||||
|
def iter(self):
|
||||||
|
return self.__iter__()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self._origin.__len__()
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
try:
|
||||||
|
if not self._origin_iter:
|
||||||
|
self._origin_iter = self._origin.__iter__()
|
||||||
|
n = next(self._origin_iter)
|
||||||
|
except StopIteration as e:
|
||||||
|
self._finished = True
|
||||||
|
raise e
|
||||||
|
else:
|
||||||
|
self._state.append(n)
|
||||||
|
return n
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
return self.__next__()
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
item = self._origin[key]
|
||||||
|
if isinstance(key, slice):
|
||||||
|
return self.__class__(item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self._origin, name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<{} {}>".format(self.__class__.__name__, repr(self._origin))
|
23
graphene/utils/tests/test_lazylist.py
Normal file
23
graphene/utils/tests/test_lazylist.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from py.test import raises
|
||||||
|
|
||||||
|
from ..lazylist import LazyList
|
||||||
|
|
||||||
|
|
||||||
|
def test_lazymap():
|
||||||
|
data = list(range(10))
|
||||||
|
lm = LazyList(data)
|
||||||
|
assert len(lm) == 10
|
||||||
|
assert lm[1] == 1
|
||||||
|
assert isinstance(lm[1:4], LazyList)
|
||||||
|
assert lm.append == data.append
|
||||||
|
assert repr(lm) == '<LazyList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>'
|
||||||
|
|
||||||
|
|
||||||
|
def test_lazymap_iter():
|
||||||
|
data = list(range(2))
|
||||||
|
lm = LazyList(data)
|
||||||
|
iter_lm = iter(lm)
|
||||||
|
assert iter_lm.next() == 0
|
||||||
|
assert iter_lm.next() == 1
|
||||||
|
with raises(StopIteration):
|
||||||
|
iter_lm.next()
|
2
setup.py
2
setup.py
|
@ -24,7 +24,7 @@ class PyTest(TestCommand):
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='graphene',
|
name='graphene',
|
||||||
version='0.4.2',
|
version='0.4.3',
|
||||||
|
|
||||||
description='Graphene: Python DSL for GraphQL',
|
description='Graphene: Python DSL for GraphQL',
|
||||||
long_description=open('README.rst').read(),
|
long_description=open('README.rst').read(),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user