mirror of
https://github.com/graphql-python/graphene.git
synced 2025-03-05 20:35:46 +03:00
Merge branch 'next' into next-allow-subclassing
This commit is contained in:
commit
8e5555a044
|
@ -9,6 +9,12 @@ from ..types.interface import InterfaceMeta
|
||||||
|
|
||||||
|
|
||||||
def is_node(objecttype):
|
def is_node(objecttype):
|
||||||
|
'''
|
||||||
|
Check if the given objecttype has Node as an interface
|
||||||
|
'''
|
||||||
|
assert issubclass(objecttype, ObjectType), (
|
||||||
|
'Only ObjectTypes can have a Node interface.'
|
||||||
|
)
|
||||||
for i in objecttype._meta.interfaces:
|
for i in objecttype._meta.interfaces:
|
||||||
if issubclass(i, Node):
|
if issubclass(i, Node):
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -2,11 +2,19 @@ import six
|
||||||
|
|
||||||
from ..utils.is_base_type import is_base_type
|
from ..utils.is_base_type import is_base_type
|
||||||
from .options import Options
|
from .options import Options
|
||||||
from .utils import (get_fields_in_type, get_base_fields,
|
from .utils import (yank_fields_from_attrs, get_base_fields,
|
||||||
yank_fields_from_attrs, merge)
|
merge)
|
||||||
|
|
||||||
|
|
||||||
class AbstractTypeMeta(type):
|
class AbstractTypeMeta(type):
|
||||||
|
'''
|
||||||
|
AbstractType Definition
|
||||||
|
|
||||||
|
When we want to share fields across multiple types, like a Interface,
|
||||||
|
a ObjectType and a Input ObjectType we can use AbstractTypes for defining
|
||||||
|
our fields that the other types will inherit from.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
# Also ensure initialization is only performed for subclasses of
|
# Also ensure initialization is only performed for subclasses of
|
||||||
|
@ -19,10 +27,9 @@ class AbstractTypeMeta(type):
|
||||||
# raise Exception('You can only extend AbstractTypes after the base definition.')
|
# raise Exception('You can only extend AbstractTypes after the base definition.')
|
||||||
return type.__new__(cls, name, bases, attrs)
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
base_fields = get_base_fields(AbstractType, bases)
|
base_fields = get_base_fields(bases, _as=None)
|
||||||
|
|
||||||
fields = get_fields_in_type(AbstractType, attrs)
|
fields = yank_fields_from_attrs(attrs, _as=None)
|
||||||
yank_fields_from_attrs(attrs, fields)
|
|
||||||
|
|
||||||
options = Options(
|
options = Options(
|
||||||
fields=merge(base_fields, fields)
|
fields=merge(base_fields, fields)
|
||||||
|
|
|
@ -4,6 +4,10 @@ from ..utils.orderedtype import OrderedType
|
||||||
|
|
||||||
|
|
||||||
class Dynamic(OrderedType):
|
class Dynamic(OrderedType):
|
||||||
|
'''
|
||||||
|
A Dynamic Type let us get the type in runtime when we generate
|
||||||
|
the schema. So we can have lazy fields.
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, type, _creation_counter=None):
|
def __init__(self, type, _creation_counter=None):
|
||||||
super(Dynamic, self).__init__(_creation_counter=_creation_counter)
|
super(Dynamic, self).__init__(_creation_counter=_creation_counter)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from .unmountedtype import UnmountedType
|
||||||
try:
|
try:
|
||||||
from enum import Enum as PyEnum
|
from enum import Enum as PyEnum
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from ..utils.enum import Enum as PyEnum
|
from ..pyutils.enum import Enum as PyEnum
|
||||||
|
|
||||||
|
|
||||||
class EnumTypeMeta(type):
|
class EnumTypeMeta(type):
|
||||||
|
@ -50,6 +50,13 @@ class EnumTypeMeta(type):
|
||||||
|
|
||||||
|
|
||||||
class Enum(six.with_metaclass(EnumTypeMeta, UnmountedType)):
|
class Enum(six.with_metaclass(EnumTypeMeta, UnmountedType)):
|
||||||
|
'''
|
||||||
|
Enum Type Definition
|
||||||
|
|
||||||
|
Some leaf values of requests and input values are Enums. GraphQL serializes
|
||||||
|
Enum values as strings, however internally Enums can be represented by any
|
||||||
|
kind of type, often integers.
|
||||||
|
'''
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
return type(self)
|
return type(self)
|
||||||
|
|
|
@ -4,8 +4,8 @@ from ..utils.is_base_type import is_base_type
|
||||||
from .abstracttype import AbstractTypeMeta
|
from .abstracttype import AbstractTypeMeta
|
||||||
from .options import Options
|
from .options import Options
|
||||||
from .unmountedtype import UnmountedType
|
from .unmountedtype import UnmountedType
|
||||||
from .utils import (get_fields_in_type, yank_fields_from_attrs,
|
from .utils import yank_fields_from_attrs, get_base_fields, merge
|
||||||
get_base_fields, merge)
|
from .inputfield import InputField
|
||||||
|
|
||||||
|
|
||||||
class InputObjectTypeMeta(AbstractTypeMeta):
|
class InputObjectTypeMeta(AbstractTypeMeta):
|
||||||
|
@ -23,11 +23,10 @@ class InputObjectTypeMeta(AbstractTypeMeta):
|
||||||
local_fields=None,
|
local_fields=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
options.base_fields = get_base_fields(InputObjectType, bases)
|
options.base_fields = get_base_fields(bases, _as=InputField)
|
||||||
|
|
||||||
if not options.local_fields:
|
if not options.local_fields:
|
||||||
options.local_fields = get_fields_in_type(InputObjectType, attrs)
|
options.local_fields = yank_fields_from_attrs(attrs, _as=InputField)
|
||||||
yank_fields_from_attrs(attrs, options.local_fields)
|
|
||||||
|
|
||||||
options.fields = merge(
|
options.fields = merge(
|
||||||
options.base_fields,
|
options.base_fields,
|
||||||
|
@ -40,6 +39,14 @@ class InputObjectTypeMeta(AbstractTypeMeta):
|
||||||
|
|
||||||
|
|
||||||
class InputObjectType(six.with_metaclass(InputObjectTypeMeta, UnmountedType)):
|
class InputObjectType(six.with_metaclass(InputObjectTypeMeta, UnmountedType)):
|
||||||
|
'''
|
||||||
|
Input Object Type Definition
|
||||||
|
|
||||||
|
An input object defines a structured collection of fields which may be
|
||||||
|
supplied to a field argument.
|
||||||
|
|
||||||
|
Using `NonNull` will ensure that a value must be provided by the query
|
||||||
|
'''
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_type(cls):
|
def get_type(cls):
|
||||||
|
|
|
@ -3,8 +3,8 @@ import six
|
||||||
from ..utils.is_base_type import is_base_type
|
from ..utils.is_base_type import is_base_type
|
||||||
from .abstracttype import AbstractTypeMeta
|
from .abstracttype import AbstractTypeMeta
|
||||||
from .options import Options
|
from .options import Options
|
||||||
from .utils import (get_fields_in_type, yank_fields_from_attrs,
|
from .utils import yank_fields_from_attrs, get_base_fields, merge
|
||||||
get_base_fields, merge)
|
from .field import Field
|
||||||
|
|
||||||
|
|
||||||
class InterfaceMeta(AbstractTypeMeta):
|
class InterfaceMeta(AbstractTypeMeta):
|
||||||
|
@ -22,11 +22,10 @@ class InterfaceMeta(AbstractTypeMeta):
|
||||||
local_fields=None,
|
local_fields=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
options.base_fields = get_base_fields(Interface, bases)
|
options.base_fields = get_base_fields(bases, _as=Field)
|
||||||
|
|
||||||
if not options.local_fields:
|
if not options.local_fields:
|
||||||
options.local_fields = get_fields_in_type(Interface, attrs)
|
options.local_fields = yank_fields_from_attrs(attrs, _as=Field)
|
||||||
yank_fields_from_attrs(attrs, options.local_fields)
|
|
||||||
|
|
||||||
options.fields = merge(
|
options.fields = merge(
|
||||||
options.base_fields,
|
options.base_fields,
|
||||||
|
@ -40,6 +39,15 @@ class InterfaceMeta(AbstractTypeMeta):
|
||||||
|
|
||||||
|
|
||||||
class Interface(six.with_metaclass(InterfaceMeta)):
|
class Interface(six.with_metaclass(InterfaceMeta)):
|
||||||
|
'''
|
||||||
|
Interface Type Definition
|
||||||
|
|
||||||
|
When a field can return one of a heterogeneous set of types, a Interface type
|
||||||
|
is used to describe what types are possible, what fields are in common across
|
||||||
|
all types, as well as a function to determine which type is actually used
|
||||||
|
when the field is resolved.
|
||||||
|
'''
|
||||||
|
|
||||||
resolve_type = None
|
resolve_type = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
|
@ -6,8 +6,8 @@ from ..utils.is_base_type import is_base_type
|
||||||
from .abstracttype import AbstractTypeMeta
|
from .abstracttype import AbstractTypeMeta
|
||||||
from .interface import Interface
|
from .interface import Interface
|
||||||
from .options import Options
|
from .options import Options
|
||||||
from .utils import (get_fields_in_type, yank_fields_from_attrs,
|
from .utils import yank_fields_from_attrs, get_base_fields, merge
|
||||||
get_base_fields, merge)
|
from .field import Field
|
||||||
|
|
||||||
|
|
||||||
class ObjectTypeMeta(AbstractTypeMeta):
|
class ObjectTypeMeta(AbstractTypeMeta):
|
||||||
|
@ -26,11 +26,10 @@ class ObjectTypeMeta(AbstractTypeMeta):
|
||||||
interfaces=(),
|
interfaces=(),
|
||||||
local_fields=OrderedDict(),
|
local_fields=OrderedDict(),
|
||||||
)
|
)
|
||||||
options.base_fields = get_base_fields(ObjectType, bases)
|
options.base_fields = get_base_fields(bases, _as=Field)
|
||||||
|
|
||||||
if not options.local_fields:
|
if not options.local_fields:
|
||||||
options.local_fields = get_fields_in_type(ObjectType, attrs)
|
options.local_fields = yank_fields_from_attrs(attrs=attrs, _as=Field)
|
||||||
yank_fields_from_attrs(attrs, options.local_fields)
|
|
||||||
|
|
||||||
options.interface_fields = OrderedDict()
|
options.interface_fields = OrderedDict()
|
||||||
for interface in options.interfaces:
|
for interface in options.interfaces:
|
||||||
|
@ -57,6 +56,12 @@ class ObjectTypeMeta(AbstractTypeMeta):
|
||||||
|
|
||||||
|
|
||||||
class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
class ObjectType(six.with_metaclass(ObjectTypeMeta)):
|
||||||
|
'''
|
||||||
|
Object Type Definition
|
||||||
|
|
||||||
|
Almost all of the GraphQL types you define will be object types. Object types
|
||||||
|
have a name, but most importantly describe their fields.
|
||||||
|
'''
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_type_of(cls, root, context, info):
|
def is_type_of(cls, root, context, info):
|
||||||
|
|
|
@ -11,12 +11,10 @@ from .unmountedtype import UnmountedType
|
||||||
class ScalarTypeMeta(type):
|
class ScalarTypeMeta(type):
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
super_new = super(ScalarTypeMeta, cls).__new__
|
|
||||||
|
|
||||||
# Also ensure initialization is only performed for subclasses of Model
|
# Also ensure initialization is only performed for subclasses of Model
|
||||||
# (excluding Model class itself).
|
# (excluding Model class itself).
|
||||||
if not is_base_type(bases, ScalarTypeMeta):
|
if not is_base_type(bases, ScalarTypeMeta):
|
||||||
return super_new(cls, name, bases, attrs)
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
options = Options(
|
options = Options(
|
||||||
attrs.pop('Meta', None),
|
attrs.pop('Meta', None),
|
||||||
|
@ -24,13 +22,21 @@ class ScalarTypeMeta(type):
|
||||||
description=attrs.get('__doc__'),
|
description=attrs.get('__doc__'),
|
||||||
)
|
)
|
||||||
|
|
||||||
return super_new(cls, name, bases, dict(attrs, _meta=options))
|
return type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
||||||
|
|
||||||
def __str__(cls): # noqa: N802
|
def __str__(cls): # noqa: N802
|
||||||
return cls._meta.name
|
return cls._meta.name
|
||||||
|
|
||||||
|
|
||||||
class Scalar(six.with_metaclass(ScalarTypeMeta, UnmountedType)):
|
class Scalar(six.with_metaclass(ScalarTypeMeta, UnmountedType)):
|
||||||
|
'''
|
||||||
|
Scalar Type Definition
|
||||||
|
|
||||||
|
The leaf values of any request and input values to arguments are
|
||||||
|
Scalars (or Enums) and are defined with a name and a series of functions
|
||||||
|
used to parse input from ast or variables and to ensure validity.
|
||||||
|
'''
|
||||||
|
|
||||||
serialize = None
|
serialize = None
|
||||||
parse_value = None
|
parse_value = None
|
||||||
parse_literal = None
|
parse_literal = None
|
||||||
|
|
|
@ -9,15 +9,14 @@ from graphql.utils.schema_printer import print_schema
|
||||||
from .typemap import TypeMap, is_graphene_type
|
from .typemap import TypeMap, is_graphene_type
|
||||||
|
|
||||||
|
|
||||||
# from ..utils.get_graphql_type import get_graphql_type
|
|
||||||
|
|
||||||
|
|
||||||
# from graphql.type.schema import assert_object_implements_interface
|
|
||||||
|
|
||||||
# from collections import defaultdict
|
|
||||||
|
|
||||||
|
|
||||||
class Schema(GraphQLSchema):
|
class Schema(GraphQLSchema):
|
||||||
|
'''
|
||||||
|
Schema Definition
|
||||||
|
|
||||||
|
A Schema is created by supplying the root types of each type of operation,
|
||||||
|
query and mutation (optional). A schema definition is then supplied to the
|
||||||
|
validator and executor.
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, query=None, mutation=None, subscription=None,
|
def __init__(self, query=None, mutation=None, subscription=None,
|
||||||
directives=None, types=None, executor=None, middlewares=None):
|
directives=None, types=None, executor=None, middlewares=None):
|
||||||
|
|
|
@ -16,12 +16,30 @@ class Structure(UnmountedType):
|
||||||
|
|
||||||
|
|
||||||
class List(Structure):
|
class List(Structure):
|
||||||
|
'''
|
||||||
|
List Modifier
|
||||||
|
|
||||||
|
A list is a kind of type marker, a wrapping type which points to another
|
||||||
|
type. Lists are often created within the context of defining the fields of
|
||||||
|
an object type.
|
||||||
|
'''
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '[{}]'.format(self.of_type)
|
return '[{}]'.format(self.of_type)
|
||||||
|
|
||||||
|
|
||||||
class NonNull(Structure):
|
class NonNull(Structure):
|
||||||
|
'''
|
||||||
|
Non-Null Modifier
|
||||||
|
|
||||||
|
A non-null is a kind of type marker, a wrapping type which points to another
|
||||||
|
type. Non-null types enforce that their values are never null and can ensure
|
||||||
|
an error is raised if this ever occurs during a request. It is useful for
|
||||||
|
fields which you can make a strong guarantee on non-nullability, for example
|
||||||
|
usually the id field of a database row will never be null.
|
||||||
|
|
||||||
|
Note: the enforcement of non-nullability occurs within the executor.
|
||||||
|
'''
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}!'.format(self.of_type)
|
return '{}!'.format(self.of_type)
|
||||||
|
|
|
@ -31,6 +31,14 @@ class UnionMeta(type):
|
||||||
|
|
||||||
|
|
||||||
class Union(six.with_metaclass(UnionMeta)):
|
class Union(six.with_metaclass(UnionMeta)):
|
||||||
|
'''
|
||||||
|
Union Type Definition
|
||||||
|
|
||||||
|
When a field can return one of a heterogeneous set of types, a Union type
|
||||||
|
is used to describe what types are possible as well as providing a function
|
||||||
|
to determine which type is actually used when the field is resolved.
|
||||||
|
'''
|
||||||
|
|
||||||
resolve_type = None
|
resolve_type = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
|
@ -4,7 +4,7 @@ from ..utils.orderedtype import OrderedType
|
||||||
class UnmountedType(OrderedType):
|
class UnmountedType(OrderedType):
|
||||||
'''
|
'''
|
||||||
This class acts a proxy for a Graphene Type, so it can be mounted
|
This class acts a proxy for a Graphene Type, so it can be mounted
|
||||||
as Field, InputField or Argument.
|
dynamically as Field, InputField or Argument.
|
||||||
|
|
||||||
Instead of writing
|
Instead of writing
|
||||||
>>> class MyObjectType(ObjectType):
|
>>> class MyObjectType(ObjectType):
|
||||||
|
|
|
@ -6,82 +6,76 @@ from .inputfield import InputField
|
||||||
from .unmountedtype import UnmountedType
|
from .unmountedtype import UnmountedType
|
||||||
|
|
||||||
|
|
||||||
def merge_fields_in_attrs(bases, attrs):
|
|
||||||
from ..types import AbstractType, Interface
|
|
||||||
inherited_bases = (AbstractType, Interface)
|
|
||||||
for base in bases:
|
|
||||||
if base in inherited_bases or not issubclass(base, inherited_bases):
|
|
||||||
continue
|
|
||||||
for name, field in base._meta.fields.items():
|
|
||||||
if name in attrs:
|
|
||||||
continue
|
|
||||||
attrs[name] = field
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
|
|
||||||
def merge(*dicts):
|
def merge(*dicts):
|
||||||
|
'''
|
||||||
|
Merge the dicts into one
|
||||||
|
'''
|
||||||
merged = OrderedDict()
|
merged = OrderedDict()
|
||||||
for _dict in dicts:
|
for _dict in dicts:
|
||||||
merged.update(_dict)
|
merged.update(_dict)
|
||||||
return merged
|
return merged
|
||||||
|
|
||||||
|
|
||||||
def get_base_fields(in_type, bases):
|
def get_base_fields(bases, _as=None):
|
||||||
|
'''
|
||||||
|
Get all the fields in the given bases
|
||||||
|
'''
|
||||||
fields = OrderedDict()
|
fields = OrderedDict()
|
||||||
fields = merge_fields_in_attrs(bases, fields)
|
from ..types import AbstractType, Interface
|
||||||
return get_fields_in_type(in_type, fields, order=False)
|
# We allow inheritance in AbstractTypes and Interfaces but not ObjectTypes
|
||||||
|
inherited_bases = (AbstractType, Interface)
|
||||||
|
for base in bases:
|
||||||
|
if base in inherited_bases or not issubclass(base, inherited_bases):
|
||||||
|
continue
|
||||||
|
for name, field in base._meta.fields.items():
|
||||||
|
if name in fields:
|
||||||
|
continue
|
||||||
|
fields[name] = get_field_as(field, _as=_as)
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
def unmounted_field_in_type(unmounted_field, type):
|
def mount_as(unmounted_field, _as):
|
||||||
'''
|
'''
|
||||||
Mount the UnmountedType dinamically as Field or InputField
|
Mount the UnmountedType dinamically as Field or InputField
|
||||||
depending on where mounted in.
|
|
||||||
|
|
||||||
ObjectType -> Field
|
|
||||||
InputObjectType -> InputField
|
|
||||||
'''
|
'''
|
||||||
# from ..types.inputobjecttype import InputObjectType
|
if _as is None:
|
||||||
from ..types.objecttype import ObjectType
|
return unmounted_field
|
||||||
from ..types.interface import Interface
|
|
||||||
from ..types.abstracttype import AbstractType
|
|
||||||
from ..types.inputobjecttype import InputObjectType
|
|
||||||
|
|
||||||
if issubclass(type, (ObjectType, Interface)):
|
elif _as is Field:
|
||||||
return unmounted_field.Field()
|
return unmounted_field.Field()
|
||||||
|
|
||||||
elif issubclass(type, (AbstractType)):
|
elif _as is InputField:
|
||||||
return unmounted_field
|
|
||||||
elif issubclass(type, (InputObjectType)):
|
|
||||||
return unmounted_field.InputField()
|
return unmounted_field.InputField()
|
||||||
|
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Unmounted field "{}" cannot be mounted in {}.'.format(
|
'Unmounted field "{}" cannot be mounted in {}.'.format(
|
||||||
unmounted_field, type
|
unmounted_field, _as
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_field(in_type, value):
|
def get_field_as(value, _as=None):
|
||||||
|
'''
|
||||||
|
Get type mounted
|
||||||
|
'''
|
||||||
if isinstance(value, (Field, InputField, Dynamic)):
|
if isinstance(value, (Field, InputField, Dynamic)):
|
||||||
return value
|
return value
|
||||||
elif isinstance(value, UnmountedType):
|
elif isinstance(value, UnmountedType):
|
||||||
return unmounted_field_in_type(value, in_type)
|
return mount_as(value, _as)
|
||||||
|
|
||||||
|
|
||||||
def get_fields_in_type(in_type, attrs, order=True):
|
def yank_fields_from_attrs(attrs, _as=None):
|
||||||
|
'''
|
||||||
|
Extract all the fields in given attributes (dict)
|
||||||
|
and return them ordered
|
||||||
|
'''
|
||||||
fields_with_names = []
|
fields_with_names = []
|
||||||
for attname, value in list(attrs.items()):
|
for attname, value in list(attrs.items()):
|
||||||
field = get_field(in_type, value)
|
field = get_field_as(value, _as)
|
||||||
if not field:
|
if not field:
|
||||||
continue
|
continue
|
||||||
fields_with_names.append((attname, field))
|
fields_with_names.append((attname, field))
|
||||||
|
del attrs[attname]
|
||||||
if not order:
|
|
||||||
return OrderedDict(fields_with_names)
|
|
||||||
|
|
||||||
return OrderedDict(sorted(fields_with_names, key=lambda f: f[1]))
|
return OrderedDict(sorted(fields_with_names, key=lambda f: f[1]))
|
||||||
|
|
||||||
|
|
||||||
def yank_fields_from_attrs(attrs, fields):
|
|
||||||
for name in fields.keys():
|
|
||||||
del attrs[name]
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user