mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 17:46:57 +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 .schema import Schema
|
||||
from .structures import List, NonNull
|
||||
from .enum import Enum
|
||||
from .field import Field, InputField
|
||||
from .field import Field
|
||||
from .inputfield import InputField
|
||||
from .argument import Argument
|
||||
from .inputobjecttype import InputObjectType
|
||||
|
||||
__all__ = [
|
||||
'AbstractType',
|
||||
'ObjectType',
|
||||
'InputObjectType',
|
||||
'Interface',
|
||||
|
|
|
@ -1,77 +1,33 @@
|
|||
import inspect
|
||||
from collections import OrderedDict
|
||||
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.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):
|
||||
super(Argument, self).__init__(_creation_counter=_creation_counter)
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.default_value = default_value
|
||||
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
|
||||
args = list(filter(None, args)) + [extra]
|
||||
arguments = []
|
||||
iter_arguments = chain(*[arg.items() for arg in args])
|
||||
arguments_names = set()
|
||||
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, GraphQLArgument):
|
||||
if not isinstance(arg, Argument):
|
||||
raise ValueError('Unknown argument "{}".'.format(default_name))
|
||||
|
||||
arg = Argument.copy_from(arg)
|
||||
arg.name = arg.name or default_name and to_camel_case(default_name)
|
||||
assert arg.name, 'All arguments must have a name.'
|
||||
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)
|
||||
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 OrderedDict([(a.name, a) for a in sorted(arguments)])
|
||||
return arguments
|
||||
|
|
|
@ -16,6 +16,11 @@ except:
|
|||
|
||||
|
||||
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):
|
||||
|
|
|
@ -16,31 +16,22 @@ except ImportError:
|
|||
class EnumTypeMeta(type):
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
super_new = type.__new__
|
||||
|
||||
# Also ensure initialization is only performed for subclasses of Model
|
||||
# (excluding Model class itself).
|
||||
if not is_base_type(bases, EnumTypeMeta):
|
||||
return super_new(cls, name, bases, attrs)
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
options = Options(
|
||||
attrs.pop('Meta', None),
|
||||
name=None,
|
||||
description=None,
|
||||
name=name,
|
||||
description=attrs.get('__doc__'),
|
||||
enum=None,
|
||||
graphql_type=None
|
||||
)
|
||||
if not options.enum:
|
||||
options.enum = PyEnum(cls.__name__, attrs)
|
||||
|
||||
new_attrs = OrderedDict(attrs, _meta=options, **options.enum.__members__)
|
||||
|
||||
cls = super_new(cls, name, bases, new_attrs)
|
||||
|
||||
if not options.graphql_type:
|
||||
options.graphql_type = generate_enum(cls)
|
||||
|
||||
return cls
|
||||
return type.__new__(cls, name, bases, new_attrs)
|
||||
|
||||
def __prepare__(name, bases, **kwargs): # noqa: N805
|
||||
return OrderedDict()
|
||||
|
@ -51,10 +42,13 @@ class EnumTypeMeta(type):
|
|||
return cls.from_enum(PyEnum(*args, **kwargs), description=description)
|
||||
return super(EnumTypeMeta, cls).__call__(*args, **kwargs)
|
||||
|
||||
|
||||
class Enum(six.with_metaclass(EnumTypeMeta, UnmountedType)):
|
||||
|
||||
@classmethod
|
||||
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,221 +1,46 @@
|
|||
import inspect
|
||||
from collections import OrderedDict
|
||||
|
||||
from graphql.type import (GraphQLField, GraphQLFieldDefinition,
|
||||
GraphQLInputObjectField)
|
||||
from graphql.utils.assert_valid_name import assert_valid_name
|
||||
from functools import partial
|
||||
from collections import OrderedDict, Mapping
|
||||
|
||||
from ..utils.orderedtype import OrderedType
|
||||
from ..utils.str_converters import to_camel_case
|
||||
from .structures import NonNull
|
||||
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
|
||||
def name(self, name):
|
||||
if name is not None:
|
||||
assert_valid_name(name)
|
||||
self._name = name
|
||||
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):
|
||||
from ..utils.get_graphql_type import get_graphql_type
|
||||
from .structures import NonNull
|
||||
if inspect.isfunction(self._type):
|
||||
_type = self._type()
|
||||
else:
|
||||
_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
|
||||
return self._type()
|
||||
return self._type
|
||||
|
|
|
@ -1,49 +1,36 @@
|
|||
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 .field import InputField
|
||||
from .objecttype import attrs_without_fields
|
||||
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):
|
||||
super_new = super(InputObjectTypeMeta, cls).__new__
|
||||
|
||||
# Also ensure initialization is only performed for subclasses of Model
|
||||
# (excluding Model class itself).
|
||||
# Also ensure initialization is only performed for subclasses of
|
||||
# InputObjectType
|
||||
if not is_base_type(bases, InputObjectTypeMeta):
|
||||
return super_new(cls, name, bases, attrs)
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
options = Options(
|
||||
attrs.pop('Meta', None),
|
||||
name=None,
|
||||
description=None,
|
||||
graphql_type=None,
|
||||
name=name,
|
||||
description=attrs.get('__doc__'),
|
||||
)
|
||||
|
||||
fields = get_fields(InputObjectType, attrs, bases)
|
||||
attrs = attrs_without_fields(attrs, fields)
|
||||
cls = super_new(cls, name, bases, dict(attrs, _meta=options))
|
||||
attrs = merge_fields_in_attrs(bases, attrs)
|
||||
options.fields = get_fields_in_type(InputObjectType, attrs)
|
||||
yank_fields_from_attrs(attrs, options.fields)
|
||||
|
||||
if not options.graphql_type:
|
||||
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)
|
||||
return type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
||||
|
||||
for name, field in fields.items():
|
||||
setattr(cls, field.attname or name, field)
|
||||
|
||||
return cls
|
||||
def __str__(cls):
|
||||
return cls._meta.name
|
||||
|
||||
|
||||
class InputObjectType(six.with_metaclass(InputObjectTypeMeta, UnmountedType)):
|
||||
pass
|
||||
class InputObjectType(six.with_metaclass(InputObjectTypeMeta)):
|
||||
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)
|
||||
|
||||
cls = cls._create_objecttype(cls, name, bases, attrs)
|
||||
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'
|
||||
|
|
|
@ -1,132 +1,45 @@
|
|||
import inspect
|
||||
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 .field import Field
|
||||
from .options import Options
|
||||
|
||||
|
||||
from ..generators import generate_interface, generate_objecttype
|
||||
from .abstracttype import AbstractTypeMeta
|
||||
from .utils import get_fields_in_type, yank_fields_from_attrs, merge_fields_in_attrs
|
||||
|
||||
|
||||
def get_interfaces(interfaces):
|
||||
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):
|
||||
class ObjectTypeMeta(AbstractTypeMeta):
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
# Also ensure initialization is only performed for subclasses of
|
||||
# ObjectType,or Interfaces
|
||||
# ObjectType
|
||||
if not is_base_type(bases, ObjectTypeMeta):
|
||||
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(
|
||||
attrs.pop('Meta', None),
|
||||
name=None,
|
||||
description=None,
|
||||
graphql_type=None,
|
||||
name=name,
|
||||
description=attrs.get('__doc__'),
|
||||
interfaces=(),
|
||||
)
|
||||
|
||||
interfaces = tuple(options.interfaces)
|
||||
fields = get_fields(ObjectType, attrs, bases, interfaces)
|
||||
attrs = attrs_without_fields(attrs, fields)
|
||||
cls = type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
||||
attrs = merge_fields_in_attrs(bases, attrs)
|
||||
options.fields = get_fields_in_type(ObjectType, attrs)
|
||||
yank_fields_from_attrs(attrs, options.fields)
|
||||
|
||||
if not options.graphql_type:
|
||||
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)
|
||||
return type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
||||
|
||||
options.get_fields = lambda: fields
|
||||
|
||||
for name, field in fields.items():
|
||||
setattr(cls, field.attname or name, field)
|
||||
|
||||
return cls
|
||||
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.get_fields().items()
|
||||
for name, f in fields:
|
||||
setattr(self, getattr(f, 'attname', name), None)
|
||||
|
||||
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")
|
||||
|
@ -134,19 +47,16 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
|||
|
||||
if not kwargs:
|
||||
for val, (name, field) in zip(args, fields_iter):
|
||||
attname = getattr(field, 'attname', name)
|
||||
setattr(self, attname, val)
|
||||
setattr(self, name, val)
|
||||
else:
|
||||
for val, (name, field) in zip(args, fields_iter):
|
||||
attname = getattr(field, 'attname', name)
|
||||
setattr(self, attname, val)
|
||||
kwargs.pop(attname, None)
|
||||
setattr(self, name, val)
|
||||
kwargs.pop(name, None)
|
||||
|
||||
for name, field in fields_iter:
|
||||
try:
|
||||
attname = getattr(field, 'attname', name)
|
||||
val = kwargs.pop(attname)
|
||||
setattr(self, attname, val)
|
||||
val = kwargs.pop(name)
|
||||
setattr(self, name, val)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
@ -159,28 +69,8 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
|||
pass
|
||||
if kwargs:
|
||||
raise TypeError(
|
||||
"'%s' is an invalid keyword argument for this function" %
|
||||
list(kwargs)[0])
|
||||
|
||||
@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
|
||||
"'{}' is an invalid keyword argument for {}".format(
|
||||
list(kwargs)[0],
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import inspect
|
||||
from ..utils.props import props
|
||||
|
||||
|
||||
|
@ -8,9 +9,11 @@ class Options(object):
|
|||
'''
|
||||
|
||||
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 {}
|
||||
for attr_name, value in defaults.items():
|
||||
if attr_name in meta_attrs:
|
||||
|
@ -27,3 +30,6 @@ class Options(object):
|
|||
','.join(meta_attrs.keys())
|
||||
)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Meta \n{} >'.format(props(self))
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import six
|
||||
|
||||
from graphql import (GraphQLBoolean, GraphQLFloat, GraphQLID, GraphQLInt,
|
||||
GraphQLString)
|
||||
from graphql.language.ast import BooleanValue, FloatValue, IntValue, StringValue
|
||||
|
||||
from ..generators import generate_scalar
|
||||
from ..utils.is_base_type import is_base_type
|
||||
from .options import Options
|
||||
from .unmountedtype import UnmountedType
|
||||
|
@ -21,34 +19,137 @@ class ScalarTypeMeta(type):
|
|||
|
||||
options = Options(
|
||||
attrs.pop('Meta', None),
|
||||
name=None,
|
||||
description=None,
|
||||
graphql_type=None
|
||||
name=name,
|
||||
description=attrs.get('__doc__'),
|
||||
)
|
||||
|
||||
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:
|
||||
options.graphql_type = generate_scalar(cls)
|
||||
|
||||
return cls
|
||||
def __str__(cls):
|
||||
return cls._meta.name
|
||||
|
||||
|
||||
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):
|
||||
# This is equivalent to
|
||||
# class String(Scalar):
|
||||
# class Meta:
|
||||
# graphql_type = graphql_type
|
||||
meta_class = type('Meta', (object,), {'graphql_type': graphql_type})
|
||||
return type(graphql_type.name, (Scalar, ), {'Meta': meta_class})
|
||||
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
|
||||
|
||||
|
||||
String = construct_scalar_class(GraphQLString)
|
||||
Int = construct_scalar_class(GraphQLInt)
|
||||
Float = construct_scalar_class(GraphQLFloat)
|
||||
Boolean = construct_scalar_class(GraphQLBoolean)
|
||||
ID = construct_scalar_class(GraphQLID)
|
||||
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,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.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
|
||||
|
@ -10,32 +16,60 @@ from ..utils.get_graphql_type import get_graphql_type
|
|||
# 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):
|
||||
if query:
|
||||
query = get_graphql_type(query)
|
||||
if mutation:
|
||||
mutation = get_graphql_type(mutation)
|
||||
if subscription:
|
||||
subscription = get_graphql_type(subscription)
|
||||
self._query = query
|
||||
self._mutation = mutation
|
||||
self._subscription = subscription
|
||||
self.types = types
|
||||
self._executor = executor
|
||||
super(Schema, self).__init__(
|
||||
query=query,
|
||||
mutation=mutation,
|
||||
subscription=subscription,
|
||||
directives=directives,
|
||||
types=self.types
|
||||
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
|
||||
)
|
||||
|
||||
@property
|
||||
def types(self):
|
||||
return map(get_graphql_type, self._types or [])
|
||||
self._directives = directives
|
||||
initial_types = [
|
||||
query,
|
||||
mutation,
|
||||
subscription,
|
||||
IntrospectionSchema
|
||||
]
|
||||
if types:
|
||||
initial_types += types
|
||||
self._type_map = TypeMap(initial_types)
|
||||
|
||||
@types.setter
|
||||
def types(self, value):
|
||||
self._types = value
|
||||
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):
|
||||
|
@ -50,7 +84,7 @@ class Schema(GraphQLSchema):
|
|||
)
|
||||
|
||||
def register(self, object_type):
|
||||
self._types.append(object_type)
|
||||
self.types.append(object_type)
|
||||
|
||||
def introspect(self):
|
||||
return self.execute(introspection_query).data
|
||||
|
@ -58,9 +92,6 @@ class Schema(GraphQLSchema):
|
|||
def __str__(self):
|
||||
return print_schema(self)
|
||||
|
||||
def get_type(self, _type):
|
||||
return self._type_map[_type]
|
||||
|
||||
def lazy(self, _type):
|
||||
return lambda: self.get_type(_type)
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
import inspect
|
||||
|
||||
from graphql import GraphQLList, GraphQLNonNull
|
||||
|
||||
from .unmountedtype import UnmountedType
|
||||
|
||||
|
||||
|
@ -18,21 +14,12 @@ class Structure(UnmountedType):
|
|||
def get_type(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
|
||||
def of_type(self, value):
|
||||
self._of_type = value
|
||||
class List(Structure):
|
||||
def __str__(self):
|
||||
return '[{}]'.format(self.of_type)
|
||||
|
||||
|
||||
class List(Structure, GraphQLList):
|
||||
pass
|
||||
|
||||
|
||||
class NonNull(Structure, GraphQLNonNull):
|
||||
pass
|
||||
class NonNull(Structure):
|
||||
def __str__(self):
|
||||
return '{}!'.format(self.of_type)
|
||||
|
|
|
@ -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 ..field import Field
|
||||
|
||||
|
||||
def test_enum_construction():
|
||||
class RGB(Enum):
|
||||
'''Description'''
|
||||
RED = 1
|
||||
GREEN = 2
|
||||
BLUE = 3
|
||||
|
@ -15,8 +12,10 @@ def test_enum_construction():
|
|||
def description(self):
|
||||
return "Description {}".format(self.name)
|
||||
|
||||
assert isinstance(RGB._meta.graphql_type, GraphQLEnumType)
|
||||
values = RGB._meta.graphql_type.get_values()
|
||||
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',
|
||||
|
@ -27,37 +26,40 @@ def test_enum_construction():
|
|||
'Description GREEN',
|
||||
'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():
|
||||
RGB = Enum('RGB', 'RED,GREEN,BLUE')
|
||||
|
||||
assert isinstance(RGB._meta.graphql_type, GraphQLEnumType)
|
||||
values = RGB._meta.graphql_type.get_values()
|
||||
values = RGB._meta.enum.__members__.values()
|
||||
assert sorted([v.name for v in values]) == [
|
||||
'BLUE',
|
||||
'GREEN',
|
||||
'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():
|
||||
PyRGB = PyEnum('RGB', 'RED,GREEN,BLUE')
|
||||
|
||||
RGB = Enum.from_enum(PyRGB)
|
||||
assert isinstance(RGB._meta.graphql_type, GraphQLEnumType)
|
||||
values = RGB._meta.graphql_type.get_values()
|
||||
assert sorted([v.name for v in values]) == [
|
||||
'BLUE',
|
||||
'GREEN',
|
||||
'RED'
|
||||
]
|
||||
assert isinstance(RGB(name='field_name').as_field(), Field)
|
||||
assert isinstance(RGB(name='field_name').as_argument(), Argument)
|
||||
assert RGB._meta.enum == PyRGB
|
||||
assert RGB.RED
|
||||
assert RGB.GREEN
|
||||
assert RGB.BLUE
|
||||
|
||||
|
||||
def test_enum_value_from_class():
|
||||
|
@ -67,3 +69,5 @@ def test_enum_value_from_class():
|
|||
BLUE = 3
|
||||
|
||||
assert RGB.RED.value == 1
|
||||
assert RGB.GREEN.value == 2
|
||||
assert RGB.BLUE.value == 3
|
||||
|
|
|
@ -1,67 +1,77 @@
|
|||
import copy
|
||||
|
||||
import pytest
|
||||
|
||||
from graphql import GraphQLField, GraphQLInt, GraphQLNonNull, GraphQLString
|
||||
|
||||
from ..argument import Argument
|
||||
from ..field import Field
|
||||
from ..scalars import Int, String
|
||||
from ..structures import NonNull
|
||||
from ..argument import Argument
|
||||
|
||||
|
||||
def test_field():
|
||||
field = Field(GraphQLString, name="name", description="description")
|
||||
assert isinstance(field, GraphQLField)
|
||||
assert field.name == "name"
|
||||
assert field.description == "description"
|
||||
assert field.type == GraphQLString
|
||||
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():
|
||||
field = Field(GraphQLString, required=True)
|
||||
assert isinstance(field, GraphQLField)
|
||||
assert isinstance(field.type, GraphQLNonNull)
|
||||
assert field.type.of_type == GraphQLString
|
||||
MyType = object()
|
||||
field = Field(MyType, required=True)
|
||||
assert isinstance(field.type, NonNull)
|
||||
assert field.type.of_type == MyType
|
||||
|
||||
|
||||
def test_field_wrong_name():
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
Field(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_field_source():
|
||||
MyType = object()
|
||||
field = Field(MyType, source='value')
|
||||
assert field.resolver(MyInstance, {}, None, None) == MyInstance.value
|
||||
|
||||
|
||||
def test_not_source_and_resolver():
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
Field(GraphQLString, source="a", resolver=lambda *_: None)
|
||||
|
||||
assert "You cannot have a source and a resolver at the same time" == str(excinfo.value)
|
||||
def test_field_with_lazy_type():
|
||||
MyType = object()
|
||||
field = Field(lambda: MyType)
|
||||
assert field.type == MyType
|
||||
|
||||
|
||||
def test_copy_field_works():
|
||||
field = Field(GraphQLString)
|
||||
copy.copy(field)
|
||||
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_callable_type():
|
||||
field = Field(lambda: GraphQLString)
|
||||
assert field.type == GraphQLString
|
||||
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_with_arguments():
|
||||
field = Field(GraphQLString, name="name", description="description", input=Argument(GraphQLString))
|
||||
assert isinstance(field, GraphQLField)
|
||||
assert field.name == "name"
|
||||
assert field.description == "description"
|
||||
assert 'input' in field.args
|
||||
assert field.args['input'].type == GraphQLString
|
||||
|
||||
|
||||
def test_field_with_argument_proxies():
|
||||
field = Field(GraphQLString, name="name", description="description", int=Int(), string=String())
|
||||
assert isinstance(field, GraphQLField)
|
||||
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
|
||||
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,57 +1,83 @@
|
|||
import pytest
|
||||
|
||||
from graphql import GraphQLInputObjectType, GraphQLString
|
||||
from graphql.type.definition import GraphQLInputFieldDefinition
|
||||
|
||||
from ..field import InputField
|
||||
from ..field import Field
|
||||
from ..inputfield import InputField
|
||||
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():
|
||||
class MyObjectType(InputObjectType):
|
||||
class MyInputObjectType(InputObjectType):
|
||||
'''Documentation'''
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLInputObjectType)
|
||||
assert graphql_type.name == "MyObjectType"
|
||||
assert graphql_type.description == "Documentation"
|
||||
assert MyInputObjectType._meta.name == "MyInputObjectType"
|
||||
assert MyInputObjectType._meta.description == "Documentation"
|
||||
assert MyInputObjectType._meta.fields == {}
|
||||
|
||||
|
||||
def test_generate_inputobjecttype_with_meta():
|
||||
class MyObjectType(InputObjectType):
|
||||
class MyInputObjectType(InputObjectType):
|
||||
|
||||
class Meta:
|
||||
name = 'MyOtherObjectType'
|
||||
name = 'MyOtherInputObjectType'
|
||||
description = 'Documentation'
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLInputObjectType)
|
||||
assert graphql_type.name == "MyOtherObjectType"
|
||||
assert graphql_type.description == "Documentation"
|
||||
assert MyInputObjectType._meta.name == "MyOtherInputObjectType"
|
||||
assert MyInputObjectType._meta.description == "Documentation"
|
||||
|
||||
|
||||
def test_empty_inputobjecttype_has_meta():
|
||||
class MyObjectType(InputObjectType):
|
||||
pass
|
||||
def test_generate_inputobjecttype_with_fields():
|
||||
class MyInputObjectType(InputObjectType):
|
||||
field = Field(MyType)
|
||||
|
||||
assert MyObjectType._meta
|
||||
assert 'field' in MyInputObjectType._meta.fields
|
||||
|
||||
|
||||
def test_generate_objecttype_with_fields():
|
||||
class MyObjectType(InputObjectType):
|
||||
field = InputField(GraphQLString)
|
||||
def test_ordered_fields_in_inputobjecttype():
|
||||
class MyInputObjectType(InputObjectType):
|
||||
b = InputField(MyType)
|
||||
a = InputField(MyType)
|
||||
field = MyScalar()
|
||||
asa = InputField(MyType)
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
fields = graphql_type.get_fields()
|
||||
assert 'field' in fields
|
||||
assert isinstance(fields['field'], GraphQLInputFieldDefinition)
|
||||
assert list(MyInputObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
|
||||
|
||||
|
||||
def test_generate_objecttype_with_graphene_fields():
|
||||
class MyObjectType(InputObjectType):
|
||||
field = String()
|
||||
def test_generate_inputobjecttype_unmountedtype():
|
||||
class MyInputObjectType(InputObjectType):
|
||||
field = MyScalar(MyType)
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
fields = graphql_type.get_fields()
|
||||
assert 'field' in fields
|
||||
assert isinstance(fields['field'], GraphQLInputFieldDefinition)
|
||||
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,19 +1,27 @@
|
|||
import pytest
|
||||
|
||||
from graphql import GraphQLField, GraphQLInterfaceType, GraphQLString
|
||||
|
||||
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'''
|
||||
|
||||
graphql_type = MyInterface._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLInterfaceType)
|
||||
assert graphql_type.name == "MyInterface"
|
||||
assert graphql_type.description == "Documentation"
|
||||
assert MyInterface._meta.name == "MyInterface"
|
||||
assert MyInterface._meta.description == "Documentation"
|
||||
assert MyInterface._meta.fields == {}
|
||||
|
||||
|
||||
def test_generate_interface_with_meta():
|
||||
|
@ -23,62 +31,52 @@ def test_generate_interface_with_meta():
|
|||
name = 'MyOtherInterface'
|
||||
description = 'Documentation'
|
||||
|
||||
graphql_type = MyInterface._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLInterfaceType)
|
||||
assert graphql_type.name == "MyOtherInterface"
|
||||
assert graphql_type.description == "Documentation"
|
||||
|
||||
|
||||
def test_empty_interface_has_meta():
|
||||
class MyInterface(Interface):
|
||||
pass
|
||||
|
||||
assert MyInterface._meta
|
||||
assert MyInterface._meta.name == "MyOtherInterface"
|
||||
assert MyInterface._meta.description == "Documentation"
|
||||
|
||||
|
||||
def test_generate_interface_with_fields():
|
||||
class MyInterface(Interface):
|
||||
field = Field(GraphQLString)
|
||||
field = Field(MyType)
|
||||
|
||||
graphql_type = MyInterface._meta.graphql_type
|
||||
fields = graphql_type.get_fields()
|
||||
assert 'field' in fields
|
||||
assert 'field' in MyInterface._meta.fields
|
||||
|
||||
|
||||
def test_interface_inheritance():
|
||||
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():
|
||||
def test_ordered_fields_in_interface():
|
||||
class MyInterface(Interface):
|
||||
inherited = Field(GraphQLString)
|
||||
b = Field(MyType)
|
||||
a = Field(MyType)
|
||||
field = MyScalar()
|
||||
asa = Field(MyType)
|
||||
|
||||
with pytest.raises(Exception) as excinfo:
|
||||
MyInterface()
|
||||
|
||||
assert "An interface cannot be intitialized" in str(excinfo.value)
|
||||
assert list(MyInterface._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
|
||||
|
||||
|
||||
def test_interface_add_fields_in_reused_graphql_type():
|
||||
MyGraphQLType = GraphQLInterfaceType('MyGraphQLType', fields={
|
||||
'field': GraphQLField(GraphQLString)
|
||||
})
|
||||
def test_generate_interface_unmountedtype():
|
||||
class MyInterface(Interface):
|
||||
field = MyScalar()
|
||||
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
class GrapheneInterface(Interface):
|
||||
field = Field(GraphQLString)
|
||||
assert 'field' in MyInterface._meta.fields
|
||||
assert isinstance(MyInterface._meta.fields['field'], Field)
|
||||
|
||||
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
|
||||
|
||||
from graphql import GraphQLObjectType, GraphQLString
|
||||
|
||||
from ..field import Field
|
||||
from ..mutation import Mutation
|
||||
from ..objecttype import ObjectType
|
||||
|
@ -16,31 +14,29 @@ def test_generate_mutation_no_args():
|
|||
pass
|
||||
|
||||
assert issubclass(MyMutation, ObjectType)
|
||||
graphql_type = MyMutation._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLObjectType)
|
||||
assert graphql_type.name == "MyMutation"
|
||||
assert graphql_type.description == "Documentation"
|
||||
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()
|
||||
# def test_generate_mutation_with_args():
|
||||
# class MyMutation(Mutation):
|
||||
# '''Documentation'''
|
||||
# class Input:
|
||||
# s = String()
|
||||
|
||||
@classmethod
|
||||
def mutate(cls, *args, **kwargs):
|
||||
pass
|
||||
# @classmethod
|
||||
# def mutate(cls, *args, **kwargs):
|
||||
# pass
|
||||
|
||||
graphql_type = MyMutation._meta.graphql_type
|
||||
field = MyMutation.Field()
|
||||
assert isinstance(graphql_type, GraphQLObjectType)
|
||||
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 == GraphQLString
|
||||
# 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():
|
||||
|
@ -54,20 +50,9 @@ def test_generate_mutation_with_meta():
|
|||
def mutate(cls, *args, **kwargs):
|
||||
pass
|
||||
|
||||
graphql_type = MyMutation._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLObjectType)
|
||||
assert graphql_type.name == "MyOtherMutation"
|
||||
assert graphql_type.description == "Documentation"
|
||||
|
||||
|
||||
def test_empty_mutation_has_meta():
|
||||
class MyMutation(Mutation):
|
||||
|
||||
@classmethod
|
||||
def mutate(cls, *args, **kwargs):
|
||||
pass
|
||||
|
||||
assert MyMutation._meta
|
||||
assert MyMutation._meta.name == "MyOtherMutation"
|
||||
assert MyMutation._meta.description == "Documentation"
|
||||
assert MyMutation.Field().resolver == MyMutation.mutate
|
||||
|
||||
|
||||
def test_mutation_raises_exception_if_no_mutate():
|
||||
|
|
|
@ -1,26 +1,33 @@
|
|||
import pytest
|
||||
|
||||
from graphql import (GraphQLField, GraphQLInterfaceType, GraphQLObjectType,
|
||||
GraphQLString)
|
||||
|
||||
from ..field import Field
|
||||
from ..interface import Interface
|
||||
from ..objecttype import ObjectType
|
||||
from ..unmountedtype import UnmountedType
|
||||
from ..abstracttype import AbstractType
|
||||
|
||||
|
||||
class MyType(object):
|
||||
pass
|
||||
|
||||
|
||||
class Container(ObjectType):
|
||||
field1 = Field(GraphQLString, name='field1')
|
||||
field2 = Field(GraphQLString, name='field2')
|
||||
field1 = Field(MyType)
|
||||
field2 = Field(MyType)
|
||||
|
||||
|
||||
class MyScalar(UnmountedType):
|
||||
def get_type(self):
|
||||
return MyType
|
||||
|
||||
|
||||
def test_generate_objecttype():
|
||||
class MyObjectType(ObjectType):
|
||||
'''Documentation'''
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLObjectType)
|
||||
assert graphql_type.name == "MyObjectType"
|
||||
assert graphql_type.description == "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():
|
||||
|
@ -29,66 +36,62 @@ def test_generate_objecttype_with_meta():
|
|||
class Meta:
|
||||
name = 'MyOtherObjectType'
|
||||
description = 'Documentation'
|
||||
interfaces = (MyType, )
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
assert isinstance(graphql_type, GraphQLObjectType)
|
||||
assert graphql_type.name == "MyOtherObjectType"
|
||||
assert graphql_type.description == "Documentation"
|
||||
|
||||
|
||||
def test_empty_objecttype_has_meta():
|
||||
class MyObjectType(ObjectType):
|
||||
pass
|
||||
|
||||
assert MyObjectType._meta
|
||||
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(GraphQLString)
|
||||
field = Field(MyType)
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
fields = graphql_type.get_fields()
|
||||
assert 'field' in fields
|
||||
assert 'field' in MyObjectType._meta.fields
|
||||
|
||||
|
||||
def test_ordered_fields_in_objecttype():
|
||||
class MyObjectType(ObjectType):
|
||||
b = Field(GraphQLString)
|
||||
a = Field(GraphQLString)
|
||||
field = Field(GraphQLString)
|
||||
asa = Field(GraphQLString)
|
||||
b = Field(MyType)
|
||||
a = Field(MyType)
|
||||
field = MyScalar()
|
||||
asa = Field(MyType)
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
fields = graphql_type.get_fields()
|
||||
assert list(fields.keys()) == ['b', 'a', 'field', 'asa']
|
||||
assert list(MyObjectType._meta.fields.keys()) == ['b', 'a', 'field', 'asa']
|
||||
|
||||
|
||||
def test_objecttype_inheritance():
|
||||
class MyInheritedObjectType(ObjectType):
|
||||
inherited = Field(GraphQLString)
|
||||
def test_generate_objecttype_inherit_abstracttype():
|
||||
class MyAbstractType(AbstractType):
|
||||
field1 = MyScalar()
|
||||
|
||||
class MyObjectType(MyInheritedObjectType):
|
||||
field1 = Field(GraphQLString)
|
||||
field2 = Field(GraphQLString)
|
||||
class MyObjectType(ObjectType, MyAbstractType):
|
||||
field2 = MyScalar()
|
||||
|
||||
graphql_type = MyObjectType._meta.graphql_type
|
||||
fields = graphql_type.get_fields()
|
||||
assert list(fields.keys()) == ['inherited', 'field1', 'field2']
|
||||
assert MyObjectType._meta.fields.keys() == ['field1', 'field2']
|
||||
assert [type(x) for x in MyObjectType._meta.fields.values()] == [Field, Field]
|
||||
|
||||
|
||||
def test_objecttype_as_container_get_fields():
|
||||
def test_generate_objecttype_inherit_abstracttype_reversed():
|
||||
class MyAbstractType(AbstractType):
|
||||
field1 = MyScalar()
|
||||
|
||||
class Container(ObjectType):
|
||||
field1 = Field(GraphQLString)
|
||||
field2 = Field(GraphQLString)
|
||||
class MyObjectType(MyAbstractType, ObjectType):
|
||||
field2 = MyScalar()
|
||||
|
||||
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():
|
||||
fields = Container._meta.graphql_type.get_fields()
|
||||
assert list(fields.keys()) == ['field1', 'field2']
|
||||
assert list(Container._meta.fields.keys()) == ['field1', 'field2']
|
||||
|
||||
|
||||
def test_objecttype_as_container_only_args():
|
||||
|
@ -125,130 +128,4 @@ 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 this function" == 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()
|
||||
assert "'unexisting_field' is an invalid keyword argument for Container" == str(excinfo.value)
|
||||
|
|
|
@ -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 .argument import Argument
|
||||
from .field import Field, InputField
|
||||
|
||||
|
||||
class UnmountedType(OrderedType):
|
||||
|
@ -18,17 +16,18 @@ class UnmountedType(OrderedType):
|
|||
'''
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UnmountedType, self).__init__()
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
super(UnmountedType, self).__init__()
|
||||
|
||||
def get_type(self):
|
||||
return self._meta.graphql_type
|
||||
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,
|
||||
|
@ -40,6 +39,7 @@ class UnmountedType(OrderedType):
|
|||
'''
|
||||
Mount the UnmountedType as InputField
|
||||
'''
|
||||
from .inputfield import InputField
|
||||
return InputField(
|
||||
self.get_type(),
|
||||
*self.args,
|
||||
|
@ -51,9 +51,20 @@ class UnmountedType(OrderedType):
|
|||
'''
|
||||
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
|
||||
)
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ from .inputfield import InputField
|
|||
|
||||
|
||||
def merge_fields_in_attrs(bases, attrs):
|
||||
from ..new_types.abstracttype import AbstractType
|
||||
from ..types.abstracttype import AbstractType
|
||||
for base in bases:
|
||||
if base == AbstractType or not issubclass(base, AbstractType):
|
||||
continue
|
||||
|
@ -26,10 +26,10 @@ def unmounted_field_in_type(attname, unmounted_field, type):
|
|||
InputObjectType -> InputField
|
||||
'''
|
||||
# from ..types.inputobjecttype import InputObjectType
|
||||
from ..new_types.objecttype import ObjectType
|
||||
from ..new_types.interface import Interface
|
||||
from ..new_types.abstracttype import AbstractType
|
||||
from ..new_types.inputobjecttype import InputObjectType
|
||||
from ..types.objecttype import ObjectType
|
||||
from ..types.interface import Interface
|
||||
from ..types.abstracttype import AbstractType
|
||||
from ..types.inputobjecttype import InputObjectType
|
||||
|
||||
if issubclass(type, (ObjectType, Interface)):
|
||||
return unmounted_field.as_field()
|
Loading…
Reference in New Issue
Block a user