mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-26 03:23:55 +03:00
Simplified Interface code
This commit is contained in:
parent
3e62fcf0cc
commit
c98d91ba1c
25
UPGRADE-v2.0.md
Normal file
25
UPGRADE-v2.0.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# v1.0 Upgrade Guide
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
|
||||||
|
* AbstractType is deprecated, please use normal inheritance instead.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class CommonFields(AbstractType):
|
||||||
|
name = String()
|
||||||
|
|
||||||
|
class Pet(CommonFields, Interface):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
With 2.0:
|
||||||
|
```python
|
||||||
|
class CommonFields(object):
|
||||||
|
name = String()
|
||||||
|
|
||||||
|
class Pet(CommonFields, Interface):
|
||||||
|
pass
|
||||||
|
```
|
|
@ -33,20 +33,19 @@ if not __SETUP__:
|
||||||
Dynamic,
|
Dynamic,
|
||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
from .relay import (
|
# from .relay import (
|
||||||
Node,
|
# Node,
|
||||||
is_node,
|
# is_node,
|
||||||
GlobalID,
|
# GlobalID,
|
||||||
ClientIDMutation,
|
# ClientIDMutation,
|
||||||
Connection,
|
# Connection,
|
||||||
ConnectionField,
|
# ConnectionField,
|
||||||
PageInfo
|
# PageInfo
|
||||||
)
|
# )
|
||||||
from .utils.resolve_only_args import resolve_only_args
|
from .utils.resolve_only_args import resolve_only_args
|
||||||
from .utils.module_loading import lazy_import
|
from .utils.module_loading import lazy_import
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AbstractType',
|
|
||||||
'ObjectType',
|
'ObjectType',
|
||||||
'InputObjectType',
|
'InputObjectType',
|
||||||
'Interface',
|
'Interface',
|
||||||
|
|
15
graphene/pyutils/init_subclass.py
Normal file
15
graphene/pyutils/init_subclass.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
class InitSubclassMeta(type):
|
||||||
|
"""Metaclass that implements PEP 487 protocol"""
|
||||||
|
def __new__(cls, name, bases, ns):
|
||||||
|
__init_subclass__ = ns.pop('__init_subclass__', None)
|
||||||
|
if __init_subclass__:
|
||||||
|
__init_subclass__ = classmethod(__init_subclass__)
|
||||||
|
ns['__init_subclass__'] = __init_subclass__
|
||||||
|
return type.__new__(cls, name, bases, ns)
|
||||||
|
|
||||||
|
def __init__(cls, name, bases, ns):
|
||||||
|
super(InitSubclassMeta, cls).__init__(name, bases, ns)
|
||||||
|
super_class = super(cls, cls)
|
||||||
|
if hasattr(super_class, '__init_subclass__'):
|
||||||
|
super_class.__init_subclass__.__func__(cls)
|
|
@ -1,7 +1,6 @@
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
|
|
||||||
from .objecttype import ObjectType
|
from .objecttype import ObjectType
|
||||||
from .abstracttype import AbstractType
|
|
||||||
from .interface import Interface
|
from .interface import Interface
|
||||||
from .mutation import Mutation
|
from .mutation import Mutation
|
||||||
from .scalars import Scalar, String, ID, Int, Float, Boolean
|
from .scalars import Scalar, String, ID, Int, Float, Boolean
|
||||||
|
@ -15,9 +14,11 @@ from .inputobjecttype import InputObjectType
|
||||||
from .dynamic import Dynamic
|
from .dynamic import Dynamic
|
||||||
from .union import Union
|
from .union import Union
|
||||||
|
|
||||||
|
# Deprecated
|
||||||
|
from .abstracttype import AbstractType
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AbstractType',
|
|
||||||
'ObjectType',
|
'ObjectType',
|
||||||
'InputObjectType',
|
'InputObjectType',
|
||||||
'Interface',
|
'Interface',
|
||||||
|
|
|
@ -1,41 +1,8 @@
|
||||||
import six
|
|
||||||
|
|
||||||
from ..utils.is_base_type import is_base_type
|
|
||||||
from .options import Options
|
|
||||||
from .utils import get_base_fields, merge, yank_fields_from_attrs
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
|
||||||
# Also ensure initialization is only performed for subclasses of
|
|
||||||
# AbstractType
|
|
||||||
if not is_base_type(bases, AbstractTypeMeta):
|
|
||||||
return type.__new__(cls, name, bases, attrs)
|
|
||||||
|
|
||||||
for base in bases:
|
|
||||||
if not issubclass(base, AbstractType) and issubclass(type(base), AbstractTypeMeta):
|
|
||||||
# raise Exception('You can only extend AbstractTypes after the base definition.')
|
|
||||||
return type.__new__(cls, name, bases, attrs)
|
|
||||||
|
|
||||||
base_fields = get_base_fields(bases, _as=None)
|
|
||||||
|
|
||||||
fields = yank_fields_from_attrs(attrs, _as=None)
|
|
||||||
|
|
||||||
options = Options(
|
|
||||||
fields=merge(base_fields, fields)
|
|
||||||
)
|
|
||||||
cls = type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
|
||||||
|
|
||||||
return cls
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractType(six.with_metaclass(AbstractTypeMeta)):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractType(object):
|
||||||
|
def __init_subclass__(cls, *args, **kwargs):
|
||||||
|
print("Abstract type is deprecated")
|
||||||
|
super(AbstractType, cls).__init_subclass__(*args, **kwargs)
|
||||||
|
|
37
graphene/types/base.py
Normal file
37
graphene/types/base.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from ..utils.subclass_with_meta import SubclassWithMeta
|
||||||
|
from ..utils.trim_docstring import trim_docstring
|
||||||
|
|
||||||
|
|
||||||
|
class BaseOptions(object):
|
||||||
|
name = None
|
||||||
|
description = None
|
||||||
|
|
||||||
|
_frozen = False
|
||||||
|
|
||||||
|
def __init__(self, class_type):
|
||||||
|
self.class_type = class_type
|
||||||
|
|
||||||
|
def freeze(self):
|
||||||
|
self._frozen = True
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
if not self._frozen:
|
||||||
|
super(BaseOptions, self).__setattr__(name, value)
|
||||||
|
else:
|
||||||
|
raise Exception("Can't modify frozen Options {0}".format(self))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<{} type={}>".format(self.__class__.__name__, self.class_type.__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseType(SubclassWithMeta):
|
||||||
|
@classmethod
|
||||||
|
def __init_subclass_with_meta__(cls, name=None, description=None, _meta=None):
|
||||||
|
assert "_meta" not in cls.__dict__, "Can't assign directly meta"
|
||||||
|
if not _meta:
|
||||||
|
return
|
||||||
|
_meta.name = name or cls.__name__
|
||||||
|
_meta.description = description or trim_docstring(cls.__doc__)
|
||||||
|
_meta.freeze()
|
||||||
|
cls._meta = _meta
|
||||||
|
super(BaseType, cls).__init_subclass_with_meta__()
|
|
@ -1,45 +1,15 @@
|
||||||
import six
|
|
||||||
|
|
||||||
from ..utils.is_base_type import is_base_type
|
|
||||||
from ..utils.trim_docstring import trim_docstring
|
|
||||||
from .abstracttype import AbstractTypeMeta
|
|
||||||
from .field import Field
|
from .field import Field
|
||||||
from .options import Options
|
from .utils import yank_fields_from_attrs
|
||||||
from .utils import get_base_fields, merge, yank_fields_from_attrs
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from .base import BaseOptions, BaseType
|
||||||
|
|
||||||
|
|
||||||
class InterfaceMeta(AbstractTypeMeta):
|
class InterfaceOptions(BaseOptions):
|
||||||
|
fields = None # type: Dict[str, Field]
|
||||||
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=trim_docstring(attrs.get('__doc__')),
|
|
||||||
local_fields=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
options.base_fields = get_base_fields(bases, _as=Field)
|
|
||||||
|
|
||||||
if not options.local_fields:
|
|
||||||
options.local_fields = yank_fields_from_attrs(attrs, _as=Field)
|
|
||||||
|
|
||||||
options.fields = merge(
|
|
||||||
options.base_fields,
|
|
||||||
options.local_fields
|
|
||||||
)
|
|
||||||
|
|
||||||
return type.__new__(cls, name, bases, dict(attrs, _meta=options))
|
|
||||||
|
|
||||||
def __str__(cls): # noqa: N802
|
|
||||||
return cls._meta.name
|
|
||||||
|
|
||||||
|
|
||||||
class Interface(six.with_metaclass(InterfaceMeta)):
|
class Interface(BaseType):
|
||||||
'''
|
'''
|
||||||
Interface Type Definition
|
Interface Type Definition
|
||||||
|
|
||||||
|
@ -48,6 +18,18 @@ class Interface(six.with_metaclass(InterfaceMeta)):
|
||||||
all types, as well as a function to determine which type is actually used
|
all types, as well as a function to determine which type is actually used
|
||||||
when the field is resolved.
|
when the field is resolved.
|
||||||
'''
|
'''
|
||||||
|
@classmethod
|
||||||
|
def __init_subclass_with_meta__(cls, **options):
|
||||||
|
_meta = InterfaceOptions(cls)
|
||||||
|
|
||||||
|
fields = OrderedDict()
|
||||||
|
for base in reversed(cls.__mro__):
|
||||||
|
fields.update(
|
||||||
|
yank_fields_from_attrs(base.__dict__, _as=Field)
|
||||||
|
)
|
||||||
|
|
||||||
|
_meta.fields = fields
|
||||||
|
super(Interface, cls).__init_subclass_with_meta__(_meta=_meta, **options)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resolve_type(cls, instance, context, info):
|
def resolve_type(cls, instance, context, info):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
from ..abstracttype import AbstractType
|
|
||||||
from ..field import Field
|
from ..field import Field
|
||||||
from ..interface import Interface
|
from ..interface import Interface
|
||||||
from ..unmountedtype import UnmountedType
|
from ..unmountedtype import UnmountedType
|
||||||
|
@ -61,7 +59,7 @@ def test_generate_interface_unmountedtype():
|
||||||
|
|
||||||
|
|
||||||
def test_generate_interface_inherit_abstracttype():
|
def test_generate_interface_inherit_abstracttype():
|
||||||
class MyAbstractType(AbstractType):
|
class MyAbstractType(object):
|
||||||
field1 = MyScalar()
|
field1 = MyScalar()
|
||||||
|
|
||||||
class MyInterface(Interface, MyAbstractType):
|
class MyInterface(Interface, MyAbstractType):
|
||||||
|
@ -84,7 +82,7 @@ def test_generate_interface_inherit_interface():
|
||||||
|
|
||||||
|
|
||||||
def test_generate_interface_inherit_abstracttype_reversed():
|
def test_generate_interface_inherit_abstracttype_reversed():
|
||||||
class MyAbstractType(AbstractType):
|
class MyAbstractType(object):
|
||||||
field1 = MyScalar()
|
field1 = MyScalar()
|
||||||
|
|
||||||
class MyInterface(MyAbstractType, Interface):
|
class MyInterface(MyAbstractType, Interface):
|
||||||
|
|
|
@ -49,7 +49,7 @@ def get_field_as(value, _as=None):
|
||||||
return _as.mounted(value)
|
return _as.mounted(value)
|
||||||
|
|
||||||
|
|
||||||
def yank_fields_from_attrs(attrs, _as=None, delete=True, sort=True):
|
def yank_fields_from_attrs(attrs, _as=None, sort=True):
|
||||||
'''
|
'''
|
||||||
Extract all the fields in given attributes (dict)
|
Extract all the fields in given attributes (dict)
|
||||||
and return them ordered
|
and return them ordered
|
||||||
|
@ -60,8 +60,6 @@ def yank_fields_from_attrs(attrs, _as=None, delete=True, sort=True):
|
||||||
if not field:
|
if not field:
|
||||||
continue
|
continue
|
||||||
fields_with_names.append((attname, field))
|
fields_with_names.append((attname, field))
|
||||||
if delete:
|
|
||||||
del attrs[attname]
|
|
||||||
|
|
||||||
if sort:
|
if sort:
|
||||||
fields_with_names = sorted(fields_with_names, key=lambda f: f[1])
|
fields_with_names = sorted(fields_with_names, key=lambda f: f[1])
|
||||||
|
|
24
graphene/utils/subclass_with_meta.py
Normal file
24
graphene/utils/subclass_with_meta.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from .props import props
|
||||||
|
from ..pyutils.init_subclass import InitSubclassMeta
|
||||||
|
|
||||||
|
|
||||||
|
class SubclassWithMeta(object):
|
||||||
|
"""This class improves __init_subclass__ to receive automatically the options from meta"""
|
||||||
|
# We will only have the metaclass in Python 2
|
||||||
|
__metaclass__ = InitSubclassMeta
|
||||||
|
|
||||||
|
def __init_subclass__(cls, **meta_options):
|
||||||
|
"""This method just terminates the super() chain"""
|
||||||
|
_Meta = getattr(cls, "Meta", None)
|
||||||
|
_meta_props = {}
|
||||||
|
if _Meta:
|
||||||
|
_meta_props = props(_Meta)
|
||||||
|
delattr(cls, "Meta")
|
||||||
|
options = dict(meta_options, **_meta_props)
|
||||||
|
super_class = super(cls, cls)
|
||||||
|
if hasattr(super_class, '__init_subclass_with_meta__'):
|
||||||
|
super_class.__init_subclass_with_meta__(**options)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __init_subclass_with_meta__(cls, **meta_options):
|
||||||
|
"""This method just terminates the super() chain"""
|
Loading…
Reference in New Issue
Block a user