From f53c26c7ab36cf6cecced1fb649d8e2a5759dcb2 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 16 Aug 2016 21:09:12 -0700 Subject: [PATCH 1/5] Simplified fields implementation --- graphene/types/abstracttype.py | 9 ++--- graphene/types/inputobjecttype.py | 9 ++--- graphene/types/interface.py | 9 ++--- graphene/types/objecttype.py | 9 ++--- graphene/types/utils.py | 67 +++++++++++-------------------- 5 files changed, 40 insertions(+), 63 deletions(-) diff --git a/graphene/types/abstracttype.py b/graphene/types/abstracttype.py index 94ed303e..d969e731 100644 --- a/graphene/types/abstracttype.py +++ b/graphene/types/abstracttype.py @@ -2,8 +2,8 @@ import six from ..utils.is_base_type import is_base_type from .options import Options -from .utils import (get_fields_in_type, get_base_fields, - yank_fields_from_attrs, merge) +from .utils import (yank_fields_from_attrs, get_base_fields, + merge) class AbstractTypeMeta(type): @@ -19,10 +19,9 @@ class AbstractTypeMeta(type): # raise Exception('You can only extend AbstractTypes after the base definition.') 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) - yank_fields_from_attrs(attrs, fields) + fields = yank_fields_from_attrs(attrs, _as=None) options = Options( fields=merge(base_fields, fields) diff --git a/graphene/types/inputobjecttype.py b/graphene/types/inputobjecttype.py index c850fa3f..460628ad 100644 --- a/graphene/types/inputobjecttype.py +++ b/graphene/types/inputobjecttype.py @@ -4,8 +4,8 @@ from ..utils.is_base_type import is_base_type from .abstracttype import AbstractTypeMeta from .options import Options from .unmountedtype import UnmountedType -from .utils import (get_fields_in_type, yank_fields_from_attrs, - get_base_fields, merge) +from .utils import yank_fields_from_attrs, get_base_fields, merge +from .inputfield import InputField class InputObjectTypeMeta(AbstractTypeMeta): @@ -23,11 +23,10 @@ class InputObjectTypeMeta(AbstractTypeMeta): 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: - options.local_fields = get_fields_in_type(InputObjectType, attrs) - yank_fields_from_attrs(attrs, options.local_fields) + options.local_fields = yank_fields_from_attrs(attrs, _as=InputField) options.fields = merge( options.base_fields, diff --git a/graphene/types/interface.py b/graphene/types/interface.py index a4cd9703..7790dd5d 100644 --- a/graphene/types/interface.py +++ b/graphene/types/interface.py @@ -3,8 +3,8 @@ import six from ..utils.is_base_type import is_base_type from .abstracttype import AbstractTypeMeta from .options import Options -from .utils import (get_fields_in_type, yank_fields_from_attrs, - get_base_fields, merge) +from .utils import yank_fields_from_attrs, get_base_fields, merge +from .field import Field class InterfaceMeta(AbstractTypeMeta): @@ -22,11 +22,10 @@ class InterfaceMeta(AbstractTypeMeta): 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: - options.local_fields = get_fields_in_type(Interface, attrs) - yank_fields_from_attrs(attrs, options.local_fields) + options.local_fields = yank_fields_from_attrs(attrs, _as=Field) options.fields = merge( options.base_fields, diff --git a/graphene/types/objecttype.py b/graphene/types/objecttype.py index d9361204..1d4749f9 100644 --- a/graphene/types/objecttype.py +++ b/graphene/types/objecttype.py @@ -6,8 +6,8 @@ from ..utils.is_base_type import is_base_type from .abstracttype import AbstractTypeMeta from .interface import Interface from .options import Options -from .utils import (get_fields_in_type, yank_fields_from_attrs, - get_base_fields, merge) +from .utils import yank_fields_from_attrs, get_base_fields, merge +from .field import Field class ObjectTypeMeta(AbstractTypeMeta): @@ -26,11 +26,10 @@ class ObjectTypeMeta(AbstractTypeMeta): interfaces=(), 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: - options.local_fields = get_fields_in_type(ObjectType, attrs) - yank_fields_from_attrs(attrs, options.local_fields) + options.local_fields = yank_fields_from_attrs(attrs=attrs, _as=Field) options.interface_fields = OrderedDict() for interface in options.interfaces: diff --git a/graphene/types/utils.py b/graphene/types/utils.py index 9f950678..aa8969a5 100644 --- a/graphene/types/utils.py +++ b/graphene/types/utils.py @@ -6,19 +6,6 @@ from .inputfield import InputField 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): merged = OrderedDict() for _dict in dicts: @@ -26,62 +13,56 @@ def merge(*dicts): return merged -def get_base_fields(in_type, bases): +def get_base_fields(bases, _as=None): fields = OrderedDict() - fields = merge_fields_in_attrs(bases, fields) - return get_fields_in_type(in_type, fields, order=False) + from ..types import AbstractType, Interface + # 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 - depending on where mounted in. - - ObjectType -> Field - InputObjectType -> InputField ''' - # from ..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 _as is None: + return unmounted_field - if issubclass(type, (ObjectType, Interface)): + elif _as is Field: return unmounted_field.Field() - elif issubclass(type, (AbstractType)): - return unmounted_field - elif issubclass(type, (InputObjectType)): + elif _as is InputField: return unmounted_field.InputField() raise Exception( '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): if isinstance(value, (Field, InputField, Dynamic)): return value 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): fields_with_names = [] for attname, value in list(attrs.items()): - field = get_field(in_type, value) + field = get_field_as(value, _as) if not field: continue fields_with_names.append((attname, field)) - - if not order: - return OrderedDict(fields_with_names) + del attrs[attname] 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] From af9e51e58d7cf06dead03b61ab6c3b72f94e0541 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 16 Aug 2016 21:29:59 -0700 Subject: [PATCH 2/5] Added documentation --- graphene/types/abstracttype.py | 8 ++++++++ graphene/types/dynamic.py | 4 ++++ graphene/types/enum.py | 7 +++++++ graphene/types/inputobjecttype.py | 8 ++++++++ graphene/types/interface.py | 9 +++++++++ graphene/types/objecttype.py | 6 ++++++ graphene/types/scalars.py | 14 ++++++++++---- graphene/types/schema.py | 15 +++++++-------- graphene/types/structures.py | 18 ++++++++++++++++++ graphene/types/union.py | 8 ++++++++ graphene/types/unmountedtype.py | 2 +- graphene/types/utils.py | 13 +++++++++++++ 12 files changed, 99 insertions(+), 13 deletions(-) diff --git a/graphene/types/abstracttype.py b/graphene/types/abstracttype.py index d969e731..4930bbf8 100644 --- a/graphene/types/abstracttype.py +++ b/graphene/types/abstracttype.py @@ -7,6 +7,14 @@ from .utils import (yank_fields_from_attrs, get_base_fields, 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): # Also ensure initialization is only performed for subclasses of diff --git a/graphene/types/dynamic.py b/graphene/types/dynamic.py index d02d68e9..b7e2aaa1 100644 --- a/graphene/types/dynamic.py +++ b/graphene/types/dynamic.py @@ -4,6 +4,10 @@ from ..utils.orderedtype import 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): super(Dynamic, self).__init__(_creation_counter=_creation_counter) diff --git a/graphene/types/enum.py b/graphene/types/enum.py index 4494c6b7..356f542a 100644 --- a/graphene/types/enum.py +++ b/graphene/types/enum.py @@ -50,6 +50,13 @@ class EnumTypeMeta(type): 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): return type(self) diff --git a/graphene/types/inputobjecttype.py b/graphene/types/inputobjecttype.py index 460628ad..67982c8b 100644 --- a/graphene/types/inputobjecttype.py +++ b/graphene/types/inputobjecttype.py @@ -39,6 +39,14 @@ class InputObjectTypeMeta(AbstractTypeMeta): 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 def get_type(cls): diff --git a/graphene/types/interface.py b/graphene/types/interface.py index 7790dd5d..a67ce914 100644 --- a/graphene/types/interface.py +++ b/graphene/types/interface.py @@ -39,6 +39,15 @@ class InterfaceMeta(AbstractTypeMeta): 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 def __init__(self, *args, **kwargs): diff --git a/graphene/types/objecttype.py b/graphene/types/objecttype.py index 1d4749f9..273049ba 100644 --- a/graphene/types/objecttype.py +++ b/graphene/types/objecttype.py @@ -56,6 +56,12 @@ class ObjectTypeMeta(AbstractTypeMeta): 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 def is_type_of(cls, root, context, info): diff --git a/graphene/types/scalars.py b/graphene/types/scalars.py index afedbd22..d6060d33 100644 --- a/graphene/types/scalars.py +++ b/graphene/types/scalars.py @@ -11,12 +11,10 @@ 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) + return type.__new__(cls, name, bases, attrs) options = Options( attrs.pop('Meta', None), @@ -24,13 +22,21 @@ class ScalarTypeMeta(type): 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 return cls._meta.name 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 parse_value = None parse_literal = None diff --git a/graphene/types/schema.py b/graphene/types/schema.py index 1e75aed0..e8830f09 100644 --- a/graphene/types/schema.py +++ b/graphene/types/schema.py @@ -9,15 +9,14 @@ from graphql.utils.schema_printer import print_schema 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): + ''' + 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, directives=None, types=None, executor=None, middlewares=None): diff --git a/graphene/types/structures.py b/graphene/types/structures.py index 10c0715f..fbb78a36 100644 --- a/graphene/types/structures.py +++ b/graphene/types/structures.py @@ -16,12 +16,30 @@ class Structure(UnmountedType): 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): return '[{}]'.format(self.of_type) 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): return '{}!'.format(self.of_type) diff --git a/graphene/types/union.py b/graphene/types/union.py index 30ddbbfb..fa178594 100644 --- a/graphene/types/union.py +++ b/graphene/types/union.py @@ -31,6 +31,14 @@ class UnionMeta(type): 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 def __init__(self, *args, **kwargs): diff --git a/graphene/types/unmountedtype.py b/graphene/types/unmountedtype.py index 36412f3b..c9b36631 100644 --- a/graphene/types/unmountedtype.py +++ b/graphene/types/unmountedtype.py @@ -4,7 +4,7 @@ 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. + dynamically as Field, InputField or Argument. Instead of writing >>> class MyObjectType(ObjectType): diff --git a/graphene/types/utils.py b/graphene/types/utils.py index aa8969a5..7f7a19f5 100644 --- a/graphene/types/utils.py +++ b/graphene/types/utils.py @@ -7,6 +7,9 @@ from .unmountedtype import UnmountedType def merge(*dicts): + ''' + Merge the dicts into one + ''' merged = OrderedDict() for _dict in dicts: merged.update(_dict) @@ -14,6 +17,9 @@ def merge(*dicts): def get_base_fields(bases, _as=None): + ''' + Get all the fields in the given bases + ''' fields = OrderedDict() from ..types import AbstractType, Interface # We allow inheritance in AbstractTypes and Interfaces but not ObjectTypes @@ -50,6 +56,9 @@ def mount_as(unmounted_field, _as): def get_field_as(value, _as=None): + ''' + Get type mounted + ''' if isinstance(value, (Field, InputField, Dynamic)): return value elif isinstance(value, UnmountedType): @@ -57,6 +66,10 @@ def get_field_as(value, _as=None): def yank_fields_from_attrs(attrs, _as=None): + ''' + Extract all the fields in given attributes (dict) + and return them ordered + ''' fields_with_names = [] for attname, value in list(attrs.items()): field = get_field_as(value, _as) From 37872698120a365c810cffaac504d10c757e1844 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 16 Aug 2016 22:58:03 -0700 Subject: [PATCH 3/5] Moved enum to pyutils --- graphene/{utils => pyutils}/enum.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename graphene/{utils => pyutils}/enum.py (100%) diff --git a/graphene/utils/enum.py b/graphene/pyutils/enum.py similarity index 100% rename from graphene/utils/enum.py rename to graphene/pyutils/enum.py From 100e03ec0c5f0588d044fa057b065ab0d068f886 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 16 Aug 2016 22:58:40 -0700 Subject: [PATCH 4/5] Add documentation to is_node --- graphene/relay/node.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/graphene/relay/node.py b/graphene/relay/node.py index 0f9fc49b..84011230 100644 --- a/graphene/relay/node.py +++ b/graphene/relay/node.py @@ -9,6 +9,12 @@ from ..types.interface import InterfaceMeta 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: if issubclass(i, Node): return True From 657810aef76a5824c76153ee5793e4d64428e456 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Tue, 16 Aug 2016 22:59:44 -0700 Subject: [PATCH 5/5] Fixed enum import --- graphene/types/enum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphene/types/enum.py b/graphene/types/enum.py index 356f542a..7895f258 100644 --- a/graphene/types/enum.py +++ b/graphene/types/enum.py @@ -9,7 +9,7 @@ from .unmountedtype import UnmountedType try: from enum import Enum as PyEnum except ImportError: - from ..utils.enum import Enum as PyEnum + from ..pyutils.enum import Enum as PyEnum class EnumTypeMeta(type):