mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 09:36:44 +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,
|
||||
Union,
|
||||
)
|
||||
from .relay import (
|
||||
Node,
|
||||
is_node,
|
||||
GlobalID,
|
||||
ClientIDMutation,
|
||||
Connection,
|
||||
ConnectionField,
|
||||
PageInfo
|
||||
)
|
||||
# from .relay import (
|
||||
# Node,
|
||||
# is_node,
|
||||
# GlobalID,
|
||||
# ClientIDMutation,
|
||||
# Connection,
|
||||
# ConnectionField,
|
||||
# PageInfo
|
||||
# )
|
||||
from .utils.resolve_only_args import resolve_only_args
|
||||
from .utils.module_loading import lazy_import
|
||||
|
||||
__all__ = [
|
||||
'AbstractType',
|
||||
'ObjectType',
|
||||
'InputObjectType',
|
||||
'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
|
||||
|
||||
from .objecttype import ObjectType
|
||||
from .abstracttype import AbstractType
|
||||
from .interface import Interface
|
||||
from .mutation import Mutation
|
||||
from .scalars import Scalar, String, ID, Int, Float, Boolean
|
||||
|
@ -15,9 +14,11 @@ from .inputobjecttype import InputObjectType
|
|||
from .dynamic import Dynamic
|
||||
from .union import Union
|
||||
|
||||
# Deprecated
|
||||
from .abstracttype import AbstractType
|
||||
|
||||
|
||||
__all__ = [
|
||||
'AbstractType',
|
||||
'ObjectType',
|
||||
'InputObjectType',
|
||||
'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):
|
||||
'''
|
||||
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
|
||||
|
||||
|
||||
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 .options import Options
|
||||
from .utils import get_base_fields, merge, yank_fields_from_attrs
|
||||
from .utils import yank_fields_from_attrs
|
||||
from collections import OrderedDict
|
||||
|
||||
from .base import BaseOptions, BaseType
|
||||
|
||||
|
||||
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=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 InterfaceOptions(BaseOptions):
|
||||
fields = None # type: Dict[str, Field]
|
||||
|
||||
|
||||
class Interface(six.with_metaclass(InterfaceMeta)):
|
||||
class Interface(BaseType):
|
||||
'''
|
||||
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
|
||||
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
|
||||
def resolve_type(cls, instance, context, info):
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
from ..abstracttype import AbstractType
|
||||
from ..field import Field
|
||||
from ..interface import Interface
|
||||
from ..unmountedtype import UnmountedType
|
||||
|
@ -61,7 +59,7 @@ def test_generate_interface_unmountedtype():
|
|||
|
||||
|
||||
def test_generate_interface_inherit_abstracttype():
|
||||
class MyAbstractType(AbstractType):
|
||||
class MyAbstractType(object):
|
||||
field1 = MyScalar()
|
||||
|
||||
class MyInterface(Interface, MyAbstractType):
|
||||
|
@ -84,7 +82,7 @@ def test_generate_interface_inherit_interface():
|
|||
|
||||
|
||||
def test_generate_interface_inherit_abstracttype_reversed():
|
||||
class MyAbstractType(AbstractType):
|
||||
class MyAbstractType(object):
|
||||
field1 = MyScalar()
|
||||
|
||||
class MyInterface(MyAbstractType, Interface):
|
||||
|
|
|
@ -49,7 +49,7 @@ def get_field_as(value, _as=None):
|
|||
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)
|
||||
and return them ordered
|
||||
|
@ -60,8 +60,6 @@ def yank_fields_from_attrs(attrs, _as=None, delete=True, sort=True):
|
|||
if not field:
|
||||
continue
|
||||
fields_with_names.append((attname, field))
|
||||
if delete:
|
||||
del attrs[attname]
|
||||
|
||||
if sort:
|
||||
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