mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-17 03:50:44 +03:00
Make new_types the deafult types
This commit is contained in:
parent
c339afc1ce
commit
2696ae9b73
|
@ -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
|
|
|
@ -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")
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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")
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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__
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -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 '<Meta \n{} >'.format(props(self))
|
|
|
@ -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
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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]
|
|
|
@ -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]
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -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 .scalars import Scalar, String, ID, Int, Float, Boolean
|
||||||
from .schema import Schema
|
from .schema import Schema
|
||||||
from .structures import List, NonNull
|
from .structures import List, NonNull
|
||||||
from .enum import Enum
|
from .enum import Enum
|
||||||
from .field import Field, InputField
|
from .field import Field
|
||||||
|
from .inputfield import InputField
|
||||||
from .argument import Argument
|
from .argument import Argument
|
||||||
from .inputobjecttype import InputObjectType
|
from .inputobjecttype import InputObjectType
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
'AbstractType',
|
||||||
'ObjectType',
|
'ObjectType',
|
||||||
'InputObjectType',
|
'InputObjectType',
|
||||||
'Interface',
|
'Interface',
|
||||||
|
|
|
@ -1,77 +1,33 @@
|
||||||
import inspect
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from itertools import chain
|
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.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):
|
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.name = name
|
||||||
self.type = type
|
self.type = type
|
||||||
self.default_value = default_value
|
self.default_value = default_value
|
||||||
self.description = description
|
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
|
from .unmountedtype import UnmountedType
|
||||||
args = list(filter(None, args)) + [extra]
|
extra_args = sorted(extra_args.items(), key=lambda f: f[1])
|
||||||
arguments = []
|
iter_arguments = chain(args.items() + extra_args)
|
||||||
iter_arguments = chain(*[arg.items() for arg in args])
|
arguments = OrderedDict()
|
||||||
arguments_names = set()
|
|
||||||
for default_name, arg in iter_arguments:
|
for default_name, arg in iter_arguments:
|
||||||
if isinstance(arg, UnmountedType):
|
if isinstance(arg, UnmountedType):
|
||||||
arg = arg.as_argument()
|
arg = arg.as_argument()
|
||||||
|
|
||||||
if not isinstance(arg, GraphQLArgument):
|
if not isinstance(arg, Argument):
|
||||||
raise ValueError('Unknown argument "{}".'.format(default_name))
|
raise ValueError('Unknown argument "{}".'.format(default_name))
|
||||||
|
|
||||||
arg = Argument.copy_from(arg)
|
arg_name = default_name or arg.name
|
||||||
arg.name = arg.name or default_name and to_camel_case(default_name)
|
assert arg_name not in arguments, 'More than one Argument have same name "{}".'.format(arg.name)
|
||||||
assert arg.name, 'All arguments must have a name.'
|
arguments[arg_name] = arg
|
||||||
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)
|
|
||||||
|
|
||||||
return OrderedDict([(a.name, a) for a in sorted(arguments)])
|
return arguments
|
||||||
|
|
|
@ -16,6 +16,11 @@ except:
|
||||||
|
|
||||||
|
|
||||||
class DateTime(Scalar):
|
class DateTime(Scalar):
|
||||||
|
'''
|
||||||
|
The `DateTime` scalar type represents a DateTime
|
||||||
|
value as specified by
|
||||||
|
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
|
||||||
|
'''
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(dt):
|
def serialize(dt):
|
||||||
|
|
|
@ -16,31 +16,22 @@ except ImportError:
|
||||||
class EnumTypeMeta(type):
|
class EnumTypeMeta(type):
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
super_new = type.__new__
|
|
||||||
|
|
||||||
# Also ensure initialization is only performed for subclasses of Model
|
# Also ensure initialization is only performed for subclasses of Model
|
||||||
# (excluding Model class itself).
|
# (excluding Model class itself).
|
||||||
if not is_base_type(bases, EnumTypeMeta):
|
if not is_base_type(bases, EnumTypeMeta):
|
||||||
return super_new(cls, name, bases, attrs)
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
options = Options(
|
options = Options(
|
||||||
attrs.pop('Meta', None),
|
attrs.pop('Meta', None),
|
||||||
name=None,
|
name=name,
|
||||||
description=None,
|
description=attrs.get('__doc__'),
|
||||||
enum=None,
|
enum=None,
|
||||||
graphql_type=None
|
|
||||||
)
|
)
|
||||||
if not options.enum:
|
if not options.enum:
|
||||||
options.enum = PyEnum(cls.__name__, attrs)
|
options.enum = PyEnum(cls.__name__, attrs)
|
||||||
|
|
||||||
new_attrs = OrderedDict(attrs, _meta=options, **options.enum.__members__)
|
new_attrs = OrderedDict(attrs, _meta=options, **options.enum.__members__)
|
||||||
|
return type.__new__(cls, name, bases, new_attrs)
|
||||||
cls = super_new(cls, name, bases, new_attrs)
|
|
||||||
|
|
||||||
if not options.graphql_type:
|
|
||||||
options.graphql_type = generate_enum(cls)
|
|
||||||
|
|
||||||
return cls
|
|
||||||
|
|
||||||
def __prepare__(name, bases, **kwargs): # noqa: N805
|
def __prepare__(name, bases, **kwargs): # noqa: N805
|
||||||
return OrderedDict()
|
return OrderedDict()
|
||||||
|
@ -51,10 +42,13 @@ class EnumTypeMeta(type):
|
||||||
return cls.from_enum(PyEnum(*args, **kwargs), description=description)
|
return cls.from_enum(PyEnum(*args, **kwargs), description=description)
|
||||||
return super(EnumTypeMeta, cls).__call__(*args, **kwargs)
|
return super(EnumTypeMeta, cls).__call__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Enum(six.with_metaclass(EnumTypeMeta, UnmountedType)):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_enum(cls, enum, description=None):
|
def from_enum(cls, enum, description=None):
|
||||||
meta_class = type('Meta', (object,), {'enum': enum, 'description': description})
|
meta_class = type('Meta', (object,), {'enum': enum, 'description': description})
|
||||||
return type(meta_class.enum.__name__, (Enum,), {'Meta': meta_class})
|
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
|
||||||
|
|
|
@ -1,221 +1,46 @@
|
||||||
import inspect
|
import inspect
|
||||||
from collections import OrderedDict
|
from functools import partial
|
||||||
|
from collections import OrderedDict, Mapping
|
||||||
from graphql.type import (GraphQLField, GraphQLFieldDefinition,
|
|
||||||
GraphQLInputObjectField)
|
|
||||||
from graphql.utils.assert_valid_name import assert_valid_name
|
|
||||||
|
|
||||||
from ..utils.orderedtype import OrderedType
|
from ..utils.orderedtype import OrderedType
|
||||||
from ..utils.str_converters import to_camel_case
|
from .structures import NonNull
|
||||||
from .argument import to_arguments
|
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
|
class Field(OrderedType):
|
||||||
def name(self, name):
|
|
||||||
if name is not None:
|
def __init__(self, type, args=None, resolver=None, source=None,
|
||||||
assert_valid_name(name)
|
deprecation_reason=None, name=None, description=None,
|
||||||
self._name = name
|
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
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
from ..utils.get_graphql_type import get_graphql_type
|
|
||||||
from .structures import NonNull
|
|
||||||
if inspect.isfunction(self._type):
|
if inspect.isfunction(self._type):
|
||||||
_type = self._type()
|
return self._type()
|
||||||
else:
|
return self._type
|
||||||
_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
|
|
||||||
|
|
|
@ -1,49 +1,36 @@
|
||||||
import six
|
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 ..utils.is_base_type import is_base_type
|
||||||
from .field import InputField
|
|
||||||
from .objecttype import attrs_without_fields
|
|
||||||
from .options import Options
|
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):
|
def __new__(cls, name, bases, attrs):
|
||||||
super_new = super(InputObjectTypeMeta, cls).__new__
|
# Also ensure initialization is only performed for subclasses of
|
||||||
|
# InputObjectType
|
||||||
# Also ensure initialization is only performed for subclasses of Model
|
|
||||||
# (excluding Model class itself).
|
|
||||||
if not is_base_type(bases, InputObjectTypeMeta):
|
if not is_base_type(bases, InputObjectTypeMeta):
|
||||||
return super_new(cls, name, bases, attrs)
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
options = Options(
|
options = Options(
|
||||||
attrs.pop('Meta', None),
|
attrs.pop('Meta', None),
|
||||||
name=None,
|
name=name,
|
||||||
description=None,
|
description=attrs.get('__doc__'),
|
||||||
graphql_type=None,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fields = get_fields(InputObjectType, attrs, bases)
|
attrs = merge_fields_in_attrs(bases, attrs)
|
||||||
attrs = attrs_without_fields(attrs, fields)
|
options.fields = get_fields_in_type(InputObjectType, attrs)
|
||||||
cls = super_new(cls, name, bases, dict(attrs, _meta=options))
|
yank_fields_from_attrs(attrs, options.fields)
|
||||||
|
|
||||||
if not options.graphql_type:
|
return type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
||||||
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)
|
|
||||||
|
|
||||||
for name, field in fields.items():
|
def __str__(cls):
|
||||||
setattr(cls, field.attname or name, field)
|
return cls._meta.name
|
||||||
|
|
||||||
return cls
|
|
||||||
|
|
||||||
|
|
||||||
class InputObjectType(six.with_metaclass(InputObjectTypeMeta, UnmountedType)):
|
class InputObjectType(six.with_metaclass(InputObjectTypeMeta)):
|
||||||
pass
|
def __init__(self, *args, **kwargs):
|
||||||
|
raise Exception("An InputObjectType cannot be intitialized")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -18,7 +18,7 @@ class MutationMeta(ObjectTypeMeta):
|
||||||
|
|
||||||
input_class = attrs.pop('Input', None)
|
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 {}
|
field_args = props(input_class) if input_class else {}
|
||||||
resolver = getattr(cls, 'mutate', None)
|
resolver = getattr(cls, 'mutate', None)
|
||||||
assert resolver, 'All mutations must define a mutate method in it'
|
assert resolver, 'All mutations must define a mutate method in it'
|
||||||
|
|
|
@ -1,132 +1,45 @@
|
||||||
import inspect
|
|
||||||
import six
|
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 ..utils.is_base_type import is_base_type
|
||||||
from .field import Field
|
|
||||||
from .options import Options
|
from .options import Options
|
||||||
|
|
||||||
|
from .abstracttype import AbstractTypeMeta
|
||||||
from ..generators import generate_interface, generate_objecttype
|
from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs
|
||||||
|
|
||||||
|
|
||||||
def get_interfaces(interfaces):
|
class ObjectTypeMeta(AbstractTypeMeta):
|
||||||
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):
|
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
# Also ensure initialization is only performed for subclasses of
|
# Also ensure initialization is only performed for subclasses of
|
||||||
# ObjectType,or Interfaces
|
# ObjectType
|
||||||
if not is_base_type(bases, ObjectTypeMeta):
|
if not is_base_type(bases, ObjectTypeMeta):
|
||||||
return type.__new__(cls, name, bases, attrs)
|
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(
|
options = Options(
|
||||||
attrs.pop('Meta', None),
|
attrs.pop('Meta', None),
|
||||||
name=None,
|
name=name,
|
||||||
description=None,
|
description=attrs.get('__doc__'),
|
||||||
graphql_type=None,
|
|
||||||
interfaces=(),
|
interfaces=(),
|
||||||
)
|
)
|
||||||
|
|
||||||
interfaces = tuple(options.interfaces)
|
attrs = merge_fields_in_attrs(bases, attrs)
|
||||||
fields = get_fields(ObjectType, attrs, bases, interfaces)
|
options.fields = get_fields_in_type(ObjectType, attrs)
|
||||||
attrs = attrs_without_fields(attrs, fields)
|
yank_fields_from_attrs(attrs, options.fields)
|
||||||
cls = type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
|
||||||
|
|
||||||
if not options.graphql_type:
|
return type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
||||||
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)
|
|
||||||
|
|
||||||
options.get_fields = lambda: fields
|
def __str__(cls):
|
||||||
|
return cls._meta.name
|
||||||
for name, field in fields.items():
|
|
||||||
setattr(cls, field.attname or name, field)
|
|
||||||
|
|
||||||
return cls
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
||||||
|
|
||||||
|
is_type_of = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# GraphQL ObjectType acting as container
|
# GraphQL ObjectType acting as container
|
||||||
args_len = len(args)
|
args_len = len(args)
|
||||||
fields = self._meta.get_fields().items()
|
fields = self._meta.fields.items()
|
||||||
for name, f in fields:
|
|
||||||
setattr(self, getattr(f, 'attname', name), None)
|
|
||||||
|
|
||||||
if args_len > len(fields):
|
if args_len > len(fields):
|
||||||
# Daft, but matches old exception sans the err msg.
|
# Daft, but matches old exception sans the err msg.
|
||||||
raise IndexError("Number of args exceeds number of fields")
|
raise IndexError("Number of args exceeds number of fields")
|
||||||
|
@ -134,19 +47,16 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
||||||
|
|
||||||
if not kwargs:
|
if not kwargs:
|
||||||
for val, (name, field) in zip(args, fields_iter):
|
for val, (name, field) in zip(args, fields_iter):
|
||||||
attname = getattr(field, 'attname', name)
|
setattr(self, name, val)
|
||||||
setattr(self, attname, val)
|
|
||||||
else:
|
else:
|
||||||
for val, (name, field) in zip(args, fields_iter):
|
for val, (name, field) in zip(args, fields_iter):
|
||||||
attname = getattr(field, 'attname', name)
|
setattr(self, name, val)
|
||||||
setattr(self, attname, val)
|
kwargs.pop(name, None)
|
||||||
kwargs.pop(attname, None)
|
|
||||||
|
|
||||||
for name, field in fields_iter:
|
for name, field in fields_iter:
|
||||||
try:
|
try:
|
||||||
attname = getattr(field, 'attname', name)
|
val = kwargs.pop(name)
|
||||||
val = kwargs.pop(attname)
|
setattr(self, name, val)
|
||||||
setattr(self, attname, val)
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -159,28 +69,8 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
||||||
pass
|
pass
|
||||||
if kwargs:
|
if kwargs:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"'%s' is an invalid keyword argument for this function" %
|
"'{}' is an invalid keyword argument for {}".format(
|
||||||
list(kwargs)[0])
|
list(kwargs)[0],
|
||||||
|
self.__class__.__name__
|
||||||
@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
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import inspect
|
||||||
from ..utils.props import props
|
from ..utils.props import props
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,9 +9,11 @@ class Options(object):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, meta=None, **defaults):
|
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 {}
|
meta_attrs = props(meta) if meta else {}
|
||||||
for attr_name, value in defaults.items():
|
for attr_name, value in defaults.items():
|
||||||
if attr_name in meta_attrs:
|
if attr_name in meta_attrs:
|
||||||
|
@ -27,3 +30,6 @@ class Options(object):
|
||||||
','.join(meta_attrs.keys())
|
','.join(meta_attrs.keys())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Meta \n{} >'.format(props(self))
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from graphql import (GraphQLBoolean, GraphQLFloat, GraphQLID, GraphQLInt,
|
from graphql.language.ast import BooleanValue, FloatValue, IntValue, StringValue
|
||||||
GraphQLString)
|
|
||||||
|
|
||||||
from ..generators import generate_scalar
|
|
||||||
from ..utils.is_base_type import is_base_type
|
from ..utils.is_base_type import is_base_type
|
||||||
from .options import Options
|
from .options import Options
|
||||||
from .unmountedtype import UnmountedType
|
from .unmountedtype import UnmountedType
|
||||||
|
@ -21,34 +19,137 @@ class ScalarTypeMeta(type):
|
||||||
|
|
||||||
options = Options(
|
options = Options(
|
||||||
attrs.pop('Meta', None),
|
attrs.pop('Meta', None),
|
||||||
name=None,
|
name=name,
|
||||||
description=None,
|
description=attrs.get('__doc__'),
|
||||||
graphql_type=None
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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:
|
def __str__(cls):
|
||||||
options.graphql_type = generate_scalar(cls)
|
return cls._meta.name
|
||||||
|
|
||||||
return cls
|
|
||||||
|
|
||||||
|
|
||||||
class Scalar(six.with_metaclass(ScalarTypeMeta, UnmountedType)):
|
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):
|
class Int(Scalar):
|
||||||
# This is equivalent to
|
'''
|
||||||
# class String(Scalar):
|
The `Int` scalar type represents non-fractional signed whole numeric
|
||||||
# class Meta:
|
values. Int can represent values between -(2^53 - 1) and 2^53 - 1 since
|
||||||
# graphql_type = graphql_type
|
represented in JSON as double-precision floating point numbers specified
|
||||||
meta_class = type('Meta', (object,), {'graphql_type': graphql_type})
|
by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).
|
||||||
return type(graphql_type.name, (Scalar, ), {'Meta': meta_class})
|
'''
|
||||||
|
|
||||||
|
@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)
|
class Float(Scalar):
|
||||||
Int = construct_scalar_class(GraphQLInt)
|
'''
|
||||||
Float = construct_scalar_class(GraphQLFloat)
|
The `Float` scalar type represents signed double-precision fractional
|
||||||
Boolean = construct_scalar_class(GraphQLBoolean)
|
values as specified by
|
||||||
ID = construct_scalar_class(GraphQLID)
|
[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
|
||||||
|
|
|
@ -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.introspection_query import introspection_query
|
||||||
from graphql.utils.schema_printer import print_schema
|
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
|
# 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 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):
|
class Schema(GraphQLSchema):
|
||||||
|
|
||||||
def __init__(self, query=None, mutation=None, subscription=None, directives=None, types=None, executor=None):
|
def __init__(self, query=None, mutation=None, subscription=None, directives=None, types=None, executor=None):
|
||||||
if query:
|
self._query = query
|
||||||
query = get_graphql_type(query)
|
self._mutation = mutation
|
||||||
if mutation:
|
self._subscription = subscription
|
||||||
mutation = get_graphql_type(mutation)
|
|
||||||
if subscription:
|
|
||||||
subscription = get_graphql_type(subscription)
|
|
||||||
self.types = types
|
self.types = types
|
||||||
self._executor = executor
|
self._executor = executor
|
||||||
super(Schema, self).__init__(
|
if directives is None:
|
||||||
query=query,
|
directives = [
|
||||||
mutation=mutation,
|
GraphQLIncludeDirective,
|
||||||
subscription=subscription,
|
GraphQLSkipDirective
|
||||||
directives=directives,
|
]
|
||||||
types=self.types
|
|
||||||
|
assert all(isinstance(d, GraphQLDirective) for d in directives), \
|
||||||
|
'Schema directives must be List[GraphQLDirective] if provided but got: {}.'.format(
|
||||||
|
directives
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
self._directives = directives
|
||||||
def types(self):
|
initial_types = [
|
||||||
return map(get_graphql_type, self._types or [])
|
query,
|
||||||
|
mutation,
|
||||||
|
subscription,
|
||||||
|
IntrospectionSchema
|
||||||
|
]
|
||||||
|
if types:
|
||||||
|
initial_types += types
|
||||||
|
self._type_map = TypeMap(initial_types)
|
||||||
|
|
||||||
@types.setter
|
def get_query_type(self):
|
||||||
def types(self, value):
|
return self.get_graphql_type(self._query)
|
||||||
self._types = value
|
|
||||||
|
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,
|
def execute(self, request_string='', root_value=None, variable_values=None,
|
||||||
context_value=None, operation_name=None, executor=None):
|
context_value=None, operation_name=None, executor=None):
|
||||||
|
@ -50,7 +84,7 @@ class Schema(GraphQLSchema):
|
||||||
)
|
)
|
||||||
|
|
||||||
def register(self, object_type):
|
def register(self, object_type):
|
||||||
self._types.append(object_type)
|
self.types.append(object_type)
|
||||||
|
|
||||||
def introspect(self):
|
def introspect(self):
|
||||||
return self.execute(introspection_query).data
|
return self.execute(introspection_query).data
|
||||||
|
@ -58,9 +92,6 @@ class Schema(GraphQLSchema):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return print_schema(self)
|
return print_schema(self)
|
||||||
|
|
||||||
def get_type(self, _type):
|
|
||||||
return self._type_map[_type]
|
|
||||||
|
|
||||||
def lazy(self, _type):
|
def lazy(self, _type):
|
||||||
return lambda: self.get_type(_type)
|
return lambda: self.get_type(_type)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
import inspect
|
|
||||||
|
|
||||||
from graphql import GraphQLList, GraphQLNonNull
|
|
||||||
|
|
||||||
from .unmountedtype import UnmountedType
|
from .unmountedtype import UnmountedType
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,21 +14,12 @@ class Structure(UnmountedType):
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
return 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
|
class List(Structure):
|
||||||
def of_type(self, value):
|
def __str__(self):
|
||||||
self._of_type = value
|
return '[{}]'.format(self.of_type)
|
||||||
|
|
||||||
|
|
||||||
class List(Structure, GraphQLList):
|
class NonNull(Structure):
|
||||||
pass
|
def __str__(self):
|
||||||
|
return '{}!'.format(self.of_type)
|
||||||
|
|
||||||
class NonNull(Structure, GraphQLNonNull):
|
|
||||||
pass
|
|
||||||
|
|
|
@ -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)
|
|
|
@ -1,12 +1,9 @@
|
||||||
from graphql.type import GraphQLEnumType
|
|
||||||
|
|
||||||
from ..argument import Argument
|
|
||||||
from ..enum import Enum, PyEnum
|
from ..enum import Enum, PyEnum
|
||||||
from ..field import Field
|
|
||||||
|
|
||||||
|
|
||||||
def test_enum_construction():
|
def test_enum_construction():
|
||||||
class RGB(Enum):
|
class RGB(Enum):
|
||||||
|
'''Description'''
|
||||||
RED = 1
|
RED = 1
|
||||||
GREEN = 2
|
GREEN = 2
|
||||||
BLUE = 3
|
BLUE = 3
|
||||||
|
@ -15,8 +12,10 @@ def test_enum_construction():
|
||||||
def description(self):
|
def description(self):
|
||||||
return "Description {}".format(self.name)
|
return "Description {}".format(self.name)
|
||||||
|
|
||||||
assert isinstance(RGB._meta.graphql_type, GraphQLEnumType)
|
assert RGB._meta.name == 'RGB'
|
||||||
values = RGB._meta.graphql_type.get_values()
|
assert RGB._meta.description == 'Description'
|
||||||
|
|
||||||
|
values = RGB._meta.enum.__members__.values()
|
||||||
assert sorted([v.name for v in values]) == [
|
assert sorted([v.name for v in values]) == [
|
||||||
'BLUE',
|
'BLUE',
|
||||||
'GREEN',
|
'GREEN',
|
||||||
|
@ -27,37 +26,40 @@ def test_enum_construction():
|
||||||
'Description GREEN',
|
'Description GREEN',
|
||||||
'Description RED'
|
'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():
|
def test_enum_instance_construction():
|
||||||
RGB = Enum('RGB', 'RED,GREEN,BLUE')
|
RGB = Enum('RGB', 'RED,GREEN,BLUE')
|
||||||
|
|
||||||
assert isinstance(RGB._meta.graphql_type, GraphQLEnumType)
|
values = RGB._meta.enum.__members__.values()
|
||||||
values = RGB._meta.graphql_type.get_values()
|
|
||||||
assert sorted([v.name for v in values]) == [
|
assert sorted([v.name for v in values]) == [
|
||||||
'BLUE',
|
'BLUE',
|
||||||
'GREEN',
|
'GREEN',
|
||||||
'RED'
|
'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():
|
def test_enum_from_builtin_enum():
|
||||||
PyRGB = PyEnum('RGB', 'RED,GREEN,BLUE')
|
PyRGB = PyEnum('RGB', 'RED,GREEN,BLUE')
|
||||||
|
|
||||||
RGB = Enum.from_enum(PyRGB)
|
RGB = Enum.from_enum(PyRGB)
|
||||||
assert isinstance(RGB._meta.graphql_type, GraphQLEnumType)
|
assert RGB._meta.enum == PyRGB
|
||||||
values = RGB._meta.graphql_type.get_values()
|
assert RGB.RED
|
||||||
assert sorted([v.name for v in values]) == [
|
assert RGB.GREEN
|
||||||
'BLUE',
|
assert RGB.BLUE
|
||||||
'GREEN',
|
|
||||||
'RED'
|
|
||||||
]
|
|
||||||
assert isinstance(RGB(name='field_name').as_field(), Field)
|
|
||||||
assert isinstance(RGB(name='field_name').as_argument(), Argument)
|
|
||||||
|
|
||||||
|
|
||||||
def test_enum_value_from_class():
|
def test_enum_value_from_class():
|
||||||
|
@ -67,3 +69,5 @@ def test_enum_value_from_class():
|
||||||
BLUE = 3
|
BLUE = 3
|
||||||
|
|
||||||
assert RGB.RED.value == 1
|
assert RGB.RED.value == 1
|
||||||
|
assert RGB.GREEN.value == 2
|
||||||
|
assert RGB.BLUE.value == 3
|
||||||
|
|
|
@ -1,67 +1,77 @@
|
||||||
import copy
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from graphql import GraphQLField, GraphQLInt, GraphQLNonNull, GraphQLString
|
|
||||||
|
|
||||||
from ..argument import Argument
|
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
from ..scalars import Int, String
|
from ..structures import NonNull
|
||||||
|
from ..argument import Argument
|
||||||
|
|
||||||
|
|
||||||
def test_field():
|
class MyInstance(object):
|
||||||
field = Field(GraphQLString, name="name", description="description")
|
value = 'value'
|
||||||
assert isinstance(field, GraphQLField)
|
value_func = staticmethod(lambda: 'value_func')
|
||||||
assert field.name == "name"
|
|
||||||
assert field.description == "description"
|
|
||||||
assert field.type == GraphQLString
|
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():
|
def test_field_required():
|
||||||
field = Field(GraphQLString, required=True)
|
MyType = object()
|
||||||
assert isinstance(field, GraphQLField)
|
field = Field(MyType, required=True)
|
||||||
assert isinstance(field.type, GraphQLNonNull)
|
assert isinstance(field.type, NonNull)
|
||||||
assert field.type.of_type == GraphQLString
|
assert field.type.of_type == MyType
|
||||||
|
|
||||||
|
|
||||||
def test_field_wrong_name():
|
def test_field_source():
|
||||||
with pytest.raises(AssertionError) as excinfo:
|
MyType = object()
|
||||||
Field(GraphQLString, name="a field")
|
field = Field(MyType, source='value')
|
||||||
|
assert field.resolver(MyInstance, {}, None, None) == MyInstance.value
|
||||||
assert """Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "a field" does not.""" == str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_source_and_resolver():
|
def test_field_with_lazy_type():
|
||||||
with pytest.raises(AssertionError) as excinfo:
|
MyType = object()
|
||||||
Field(GraphQLString, source="a", resolver=lambda *_: None)
|
field = Field(lambda: MyType)
|
||||||
|
assert field.type == MyType
|
||||||
assert "You cannot have a source and a resolver at the same time" == str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_copy_field_works():
|
def test_field_not_source_and_resolver():
|
||||||
field = Field(GraphQLString)
|
MyType = object()
|
||||||
copy.copy(field)
|
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():
|
def test_field_source_func():
|
||||||
field = Field(lambda: GraphQLString)
|
MyType = object()
|
||||||
assert field.type == GraphQLString
|
field = Field(MyType, source='value_func')
|
||||||
|
assert field.resolver(MyInstance(), {}, None, None) == MyInstance.value_func()
|
||||||
|
|
||||||
|
|
||||||
def test_field_with_arguments():
|
def test_field_source_argument_as_kw():
|
||||||
field = Field(GraphQLString, name="name", description="description", input=Argument(GraphQLString))
|
MyType = object()
|
||||||
assert isinstance(field, GraphQLField)
|
field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False))
|
||||||
assert field.name == "name"
|
assert field.args.keys() == ['b', 'c', 'a']
|
||||||
assert field.description == "description"
|
assert isinstance(field.args['b'], Argument)
|
||||||
assert 'input' in field.args
|
assert isinstance(field.args['b'].type, NonNull)
|
||||||
assert field.args['input'].type == GraphQLString
|
assert field.args['b'].type.of_type is True
|
||||||
|
assert isinstance(field.args['c'], Argument)
|
||||||
|
assert field.args['c'].type is None
|
||||||
def test_field_with_argument_proxies():
|
assert isinstance(field.args['a'], Argument)
|
||||||
field = Field(GraphQLString, name="name", description="description", int=Int(), string=String())
|
assert isinstance(field.args['a'].type, NonNull)
|
||||||
assert isinstance(field, GraphQLField)
|
assert field.args['a'].type.of_type is False
|
||||||
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
|
|
||||||
|
|
|
@ -1,57 +1,83 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
from graphql import GraphQLInputObjectType, GraphQLString
|
from ..field import Field
|
||||||
from graphql.type.definition import GraphQLInputFieldDefinition
|
from ..inputfield import InputField
|
||||||
|
|
||||||
from ..field import InputField
|
|
||||||
from ..inputobjecttype import InputObjectType
|
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():
|
def test_generate_inputobjecttype():
|
||||||
class MyObjectType(InputObjectType):
|
class MyInputObjectType(InputObjectType):
|
||||||
'''Documentation'''
|
'''Documentation'''
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert MyInputObjectType._meta.name == "MyInputObjectType"
|
||||||
assert isinstance(graphql_type, GraphQLInputObjectType)
|
assert MyInputObjectType._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyObjectType"
|
assert MyInputObjectType._meta.fields == {}
|
||||||
assert graphql_type.description == "Documentation"
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_inputobjecttype_with_meta():
|
def test_generate_inputobjecttype_with_meta():
|
||||||
class MyObjectType(InputObjectType):
|
class MyInputObjectType(InputObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
name = 'MyOtherObjectType'
|
name = 'MyOtherInputObjectType'
|
||||||
description = 'Documentation'
|
description = 'Documentation'
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert MyInputObjectType._meta.name == "MyOtherInputObjectType"
|
||||||
assert isinstance(graphql_type, GraphQLInputObjectType)
|
assert MyInputObjectType._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyOtherObjectType"
|
|
||||||
assert graphql_type.description == "Documentation"
|
|
||||||
|
|
||||||
|
|
||||||
def test_empty_inputobjecttype_has_meta():
|
def test_generate_inputobjecttype_with_fields():
|
||||||
class MyObjectType(InputObjectType):
|
class MyInputObjectType(InputObjectType):
|
||||||
pass
|
field = Field(MyType)
|
||||||
|
|
||||||
assert MyObjectType._meta
|
assert 'field' in MyInputObjectType._meta.fields
|
||||||
|
|
||||||
|
|
||||||
def test_generate_objecttype_with_fields():
|
def test_ordered_fields_in_inputobjecttype():
|
||||||
class MyObjectType(InputObjectType):
|
class MyInputObjectType(InputObjectType):
|
||||||
field = InputField(GraphQLString)
|
b = InputField(MyType)
|
||||||
|
a = InputField(MyType)
|
||||||
|
field = MyScalar()
|
||||||
|
asa = InputField(MyType)
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert list(MyInputObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
|
||||||
fields = graphql_type.get_fields()
|
|
||||||
assert 'field' in fields
|
|
||||||
assert isinstance(fields['field'], GraphQLInputFieldDefinition)
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_objecttype_with_graphene_fields():
|
def test_generate_inputobjecttype_unmountedtype():
|
||||||
class MyObjectType(InputObjectType):
|
class MyInputObjectType(InputObjectType):
|
||||||
field = String()
|
field = MyScalar(MyType)
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert 'field' in MyInputObjectType._meta.fields
|
||||||
fields = graphql_type.get_fields()
|
assert isinstance(MyInputObjectType._meta.fields['field'], InputField)
|
||||||
assert 'field' in fields
|
|
||||||
assert isinstance(fields['field'], GraphQLInputFieldDefinition)
|
|
||||||
|
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]
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from graphql import GraphQLField, GraphQLInterfaceType, GraphQLString
|
|
||||||
|
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
from ..interface import Interface
|
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():
|
def test_generate_interface():
|
||||||
class MyInterface(Interface):
|
class MyInterface(Interface):
|
||||||
'''Documentation'''
|
'''Documentation'''
|
||||||
|
|
||||||
graphql_type = MyInterface._meta.graphql_type
|
assert MyInterface._meta.name == "MyInterface"
|
||||||
assert isinstance(graphql_type, GraphQLInterfaceType)
|
assert MyInterface._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyInterface"
|
assert MyInterface._meta.fields == {}
|
||||||
assert graphql_type.description == "Documentation"
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_interface_with_meta():
|
def test_generate_interface_with_meta():
|
||||||
|
@ -23,62 +31,52 @@ def test_generate_interface_with_meta():
|
||||||
name = 'MyOtherInterface'
|
name = 'MyOtherInterface'
|
||||||
description = 'Documentation'
|
description = 'Documentation'
|
||||||
|
|
||||||
graphql_type = MyInterface._meta.graphql_type
|
assert MyInterface._meta.name == "MyOtherInterface"
|
||||||
assert isinstance(graphql_type, GraphQLInterfaceType)
|
assert MyInterface._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyOtherInterface"
|
|
||||||
assert graphql_type.description == "Documentation"
|
|
||||||
|
|
||||||
|
|
||||||
def test_empty_interface_has_meta():
|
|
||||||
class MyInterface(Interface):
|
|
||||||
pass
|
|
||||||
|
|
||||||
assert MyInterface._meta
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_interface_with_fields():
|
def test_generate_interface_with_fields():
|
||||||
class MyInterface(Interface):
|
class MyInterface(Interface):
|
||||||
field = Field(GraphQLString)
|
field = Field(MyType)
|
||||||
|
|
||||||
graphql_type = MyInterface._meta.graphql_type
|
assert 'field' in MyInterface._meta.fields
|
||||||
fields = graphql_type.get_fields()
|
|
||||||
assert 'field' in fields
|
|
||||||
|
|
||||||
|
|
||||||
def test_interface_inheritance():
|
def test_ordered_fields_in_interface():
|
||||||
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():
|
|
||||||
class MyInterface(Interface):
|
class MyInterface(Interface):
|
||||||
inherited = Field(GraphQLString)
|
b = Field(MyType)
|
||||||
|
a = Field(MyType)
|
||||||
|
field = MyScalar()
|
||||||
|
asa = Field(MyType)
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
assert list(MyInterface._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
|
||||||
MyInterface()
|
|
||||||
|
|
||||||
assert "An interface cannot be intitialized" in str(excinfo.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_interface_add_fields_in_reused_graphql_type():
|
def test_generate_interface_unmountedtype():
|
||||||
MyGraphQLType = GraphQLInterfaceType('MyGraphQLType', fields={
|
class MyInterface(Interface):
|
||||||
'field': GraphQLField(GraphQLString)
|
field = MyScalar()
|
||||||
})
|
|
||||||
|
|
||||||
with pytest.raises(AssertionError) as excinfo:
|
assert 'field' in MyInterface._meta.fields
|
||||||
class GrapheneInterface(Interface):
|
assert isinstance(MyInterface._meta.fields['field'], Field)
|
||||||
field = Field(GraphQLString)
|
|
||||||
|
|
||||||
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]
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from graphql import GraphQLObjectType, GraphQLString
|
|
||||||
|
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
from ..mutation import Mutation
|
from ..mutation import Mutation
|
||||||
from ..objecttype import ObjectType
|
from ..objecttype import ObjectType
|
||||||
|
@ -16,31 +14,29 @@ def test_generate_mutation_no_args():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
assert issubclass(MyMutation, ObjectType)
|
assert issubclass(MyMutation, ObjectType)
|
||||||
graphql_type = MyMutation._meta.graphql_type
|
assert MyMutation._meta.name == "MyMutation"
|
||||||
assert isinstance(graphql_type, GraphQLObjectType)
|
assert MyMutation._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyMutation"
|
assert MyMutation.Field().resolver == MyMutation.mutate
|
||||||
assert graphql_type.description == "Documentation"
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_mutation_with_args():
|
# def test_generate_mutation_with_args():
|
||||||
class MyMutation(Mutation):
|
# class MyMutation(Mutation):
|
||||||
'''Documentation'''
|
# '''Documentation'''
|
||||||
class Input:
|
# class Input:
|
||||||
s = String()
|
# s = String()
|
||||||
|
|
||||||
@classmethod
|
# @classmethod
|
||||||
def mutate(cls, *args, **kwargs):
|
# def mutate(cls, *args, **kwargs):
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
graphql_type = MyMutation._meta.graphql_type
|
# graphql_type = MyMutation._meta.graphql_type
|
||||||
field = MyMutation.Field()
|
# field = MyMutation.Field()
|
||||||
assert isinstance(graphql_type, GraphQLObjectType)
|
# assert graphql_type.name == "MyMutation"
|
||||||
assert graphql_type.name == "MyMutation"
|
# assert graphql_type.description == "Documentation"
|
||||||
assert graphql_type.description == "Documentation"
|
# assert isinstance(field, Field)
|
||||||
assert isinstance(field, Field)
|
# assert field.type == MyMutation._meta.graphql_type
|
||||||
assert field.type == MyMutation._meta.graphql_type
|
# assert 's' in field.args
|
||||||
assert 's' in field.args
|
# assert field.args['s'].type == String
|
||||||
assert field.args['s'].type == GraphQLString
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_mutation_with_meta():
|
def test_generate_mutation_with_meta():
|
||||||
|
@ -54,20 +50,9 @@ def test_generate_mutation_with_meta():
|
||||||
def mutate(cls, *args, **kwargs):
|
def mutate(cls, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
graphql_type = MyMutation._meta.graphql_type
|
assert MyMutation._meta.name == "MyOtherMutation"
|
||||||
assert isinstance(graphql_type, GraphQLObjectType)
|
assert MyMutation._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyOtherMutation"
|
assert MyMutation.Field().resolver == MyMutation.mutate
|
||||||
assert graphql_type.description == "Documentation"
|
|
||||||
|
|
||||||
|
|
||||||
def test_empty_mutation_has_meta():
|
|
||||||
class MyMutation(Mutation):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def mutate(cls, *args, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
assert MyMutation._meta
|
|
||||||
|
|
||||||
|
|
||||||
def test_mutation_raises_exception_if_no_mutate():
|
def test_mutation_raises_exception_if_no_mutate():
|
||||||
|
|
|
@ -1,26 +1,33 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from graphql import (GraphQLField, GraphQLInterfaceType, GraphQLObjectType,
|
|
||||||
GraphQLString)
|
|
||||||
|
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
from ..interface import Interface
|
|
||||||
from ..objecttype import ObjectType
|
from ..objecttype import ObjectType
|
||||||
|
from ..unmountedtype import UnmountedType
|
||||||
|
from ..abstracttype import AbstractType
|
||||||
|
|
||||||
|
|
||||||
|
class MyType(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Container(ObjectType):
|
class Container(ObjectType):
|
||||||
field1 = Field(GraphQLString, name='field1')
|
field1 = Field(MyType)
|
||||||
field2 = Field(GraphQLString, name='field2')
|
field2 = Field(MyType)
|
||||||
|
|
||||||
|
|
||||||
|
class MyScalar(UnmountedType):
|
||||||
|
def get_type(self):
|
||||||
|
return MyType
|
||||||
|
|
||||||
|
|
||||||
def test_generate_objecttype():
|
def test_generate_objecttype():
|
||||||
class MyObjectType(ObjectType):
|
class MyObjectType(ObjectType):
|
||||||
'''Documentation'''
|
'''Documentation'''
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert MyObjectType._meta.name == "MyObjectType"
|
||||||
assert isinstance(graphql_type, GraphQLObjectType)
|
assert MyObjectType._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyObjectType"
|
assert MyObjectType._meta.interfaces == tuple()
|
||||||
assert graphql_type.description == "Documentation"
|
assert MyObjectType._meta.fields == {}
|
||||||
|
|
||||||
|
|
||||||
def test_generate_objecttype_with_meta():
|
def test_generate_objecttype_with_meta():
|
||||||
|
@ -29,66 +36,62 @@ def test_generate_objecttype_with_meta():
|
||||||
class Meta:
|
class Meta:
|
||||||
name = 'MyOtherObjectType'
|
name = 'MyOtherObjectType'
|
||||||
description = 'Documentation'
|
description = 'Documentation'
|
||||||
|
interfaces = (MyType, )
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert MyObjectType._meta.name == "MyOtherObjectType"
|
||||||
assert isinstance(graphql_type, GraphQLObjectType)
|
assert MyObjectType._meta.description == "Documentation"
|
||||||
assert graphql_type.name == "MyOtherObjectType"
|
assert MyObjectType._meta.interfaces == (MyType, )
|
||||||
assert graphql_type.description == "Documentation"
|
|
||||||
|
|
||||||
|
|
||||||
def test_empty_objecttype_has_meta():
|
|
||||||
class MyObjectType(ObjectType):
|
|
||||||
pass
|
|
||||||
|
|
||||||
assert MyObjectType._meta
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_objecttype_with_fields():
|
def test_generate_objecttype_with_fields():
|
||||||
class MyObjectType(ObjectType):
|
class MyObjectType(ObjectType):
|
||||||
field = Field(GraphQLString)
|
field = Field(MyType)
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert 'field' in MyObjectType._meta.fields
|
||||||
fields = graphql_type.get_fields()
|
|
||||||
assert 'field' in fields
|
|
||||||
|
|
||||||
|
|
||||||
def test_ordered_fields_in_objecttype():
|
def test_ordered_fields_in_objecttype():
|
||||||
class MyObjectType(ObjectType):
|
class MyObjectType(ObjectType):
|
||||||
b = Field(GraphQLString)
|
b = Field(MyType)
|
||||||
a = Field(GraphQLString)
|
a = Field(MyType)
|
||||||
field = Field(GraphQLString)
|
field = MyScalar()
|
||||||
asa = Field(GraphQLString)
|
asa = Field(MyType)
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert list(MyObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
|
||||||
fields = graphql_type.get_fields()
|
|
||||||
assert list(fields.keys()) == ['b', 'a', 'field', 'asa']
|
|
||||||
|
|
||||||
|
|
||||||
def test_objecttype_inheritance():
|
def test_generate_objecttype_inherit_abstracttype():
|
||||||
class MyInheritedObjectType(ObjectType):
|
class MyAbstractType(AbstractType):
|
||||||
inherited = Field(GraphQLString)
|
field1 = MyScalar()
|
||||||
|
|
||||||
class MyObjectType(MyInheritedObjectType):
|
class MyObjectType(ObjectType, MyAbstractType):
|
||||||
field1 = Field(GraphQLString)
|
field2 = MyScalar()
|
||||||
field2 = Field(GraphQLString)
|
|
||||||
|
|
||||||
graphql_type = MyObjectType._meta.graphql_type
|
assert MyObjectType._meta.fields.keys() == ['field1', 'field2']
|
||||||
fields = graphql_type.get_fields()
|
assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field]
|
||||||
assert list(fields.keys()) == ['inherited', 'field1', 'field2']
|
|
||||||
|
|
||||||
|
|
||||||
def test_objecttype_as_container_get_fields():
|
def test_generate_objecttype_inherit_abstracttype_reversed():
|
||||||
|
class MyAbstractType(AbstractType):
|
||||||
|
field1 = MyScalar()
|
||||||
|
|
||||||
class Container(ObjectType):
|
class MyObjectType(MyAbstractType, ObjectType):
|
||||||
field1 = Field(GraphQLString)
|
field2 = MyScalar()
|
||||||
field2 = Field(GraphQLString)
|
|
||||||
|
|
||||||
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():
|
def test_parent_container_get_fields():
|
||||||
fields = Container._meta.graphql_type.get_fields()
|
assert list(Container._meta.fields.keys()) == ['field1', 'field2']
|
||||||
assert list(fields.keys()) == ['field1', 'field2']
|
|
||||||
|
|
||||||
|
|
||||||
def test_objecttype_as_container_only_args():
|
def test_objecttype_as_container_only_args():
|
||||||
|
@ -125,130 +128,4 @@ def test_objecttype_as_container_invalid_kwargs():
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
Container(unexisting_field="3")
|
Container(unexisting_field="3")
|
||||||
|
|
||||||
assert "'unexisting_field' is an invalid keyword argument for this function" == str(excinfo.value)
|
assert "'unexisting_field' is an invalid keyword argument for Container" == 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()
|
|
||||||
|
|
|
@ -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)
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,6 +1,4 @@
|
||||||
from ..utils.orderedtype import OrderedType
|
from ..utils.orderedtype import OrderedType
|
||||||
from .argument import Argument
|
|
||||||
from .field import Field, InputField
|
|
||||||
|
|
||||||
|
|
||||||
class UnmountedType(OrderedType):
|
class UnmountedType(OrderedType):
|
||||||
|
@ -18,17 +16,18 @@ class UnmountedType(OrderedType):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(UnmountedType, self).__init__()
|
||||||
self.args = args
|
self.args = args
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
super(UnmountedType, self).__init__()
|
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
return self._meta.graphql_type
|
raise NotImplementedError("get_type not implemented in {}".format(self))
|
||||||
|
|
||||||
def as_field(self):
|
def as_field(self):
|
||||||
'''
|
'''
|
||||||
Mount the UnmountedType as Field
|
Mount the UnmountedType as Field
|
||||||
'''
|
'''
|
||||||
|
from .field import Field
|
||||||
return Field(
|
return Field(
|
||||||
self.get_type(),
|
self.get_type(),
|
||||||
*self.args,
|
*self.args,
|
||||||
|
@ -40,6 +39,7 @@ class UnmountedType(OrderedType):
|
||||||
'''
|
'''
|
||||||
Mount the UnmountedType as InputField
|
Mount the UnmountedType as InputField
|
||||||
'''
|
'''
|
||||||
|
from .inputfield import InputField
|
||||||
return InputField(
|
return InputField(
|
||||||
self.get_type(),
|
self.get_type(),
|
||||||
*self.args,
|
*self.args,
|
||||||
|
@ -51,9 +51,20 @@ class UnmountedType(OrderedType):
|
||||||
'''
|
'''
|
||||||
Mount the UnmountedType as Argument
|
Mount the UnmountedType as Argument
|
||||||
'''
|
'''
|
||||||
|
from .argument import Argument
|
||||||
return Argument(
|
return Argument(
|
||||||
self.get_type(),
|
self.get_type(),
|
||||||
*self.args,
|
*self.args,
|
||||||
_creation_counter=self.creation_counter,
|
_creation_counter=self.creation_counter,
|
||||||
**self.kwargs
|
**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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -6,7 +6,7 @@ from .inputfield import InputField
|
||||||
|
|
||||||
|
|
||||||
def merge_fields_in_attrs(bases, attrs):
|
def merge_fields_in_attrs(bases, attrs):
|
||||||
from ..new_types.abstracttype import AbstractType
|
from ..types.abstracttype import AbstractType
|
||||||
for base in bases:
|
for base in bases:
|
||||||
if base == AbstractType or not issubclass(base, AbstractType):
|
if base == AbstractType or not issubclass(base, AbstractType):
|
||||||
continue
|
continue
|
||||||
|
@ -26,10 +26,10 @@ def unmounted_field_in_type(attname, unmounted_field, type):
|
||||||
InputObjectType -> InputField
|
InputObjectType -> InputField
|
||||||
'''
|
'''
|
||||||
# from ..types.inputobjecttype import InputObjectType
|
# from ..types.inputobjecttype import InputObjectType
|
||||||
from ..new_types.objecttype import ObjectType
|
from ..types.objecttype import ObjectType
|
||||||
from ..new_types.interface import Interface
|
from ..types.interface import Interface
|
||||||
from ..new_types.abstracttype import AbstractType
|
from ..types.abstracttype import AbstractType
|
||||||
from ..new_types.inputobjecttype import InputObjectType
|
from ..types.inputobjecttype import InputObjectType
|
||||||
|
|
||||||
if issubclass(type, (ObjectType, Interface)):
|
if issubclass(type, (ObjectType, Interface)):
|
||||||
return unmounted_field.as_field()
|
return unmounted_field.as_field()
|
Loading…
Reference in New Issue
Block a user