Simplified Interface code

This commit is contained in:
Syrus Akbary 2017-07-11 20:33:03 -07:00
parent 3e62fcf0cc
commit c98d91ba1c
10 changed files with 141 additions and 95 deletions

25
UPGRADE-v2.0.md Normal file
View 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
```

View File

@ -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',

View 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)

View File

@ -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',

View File

@ -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
View 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__()

View File

@ -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):

View File

@ -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):

View File

@ -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])

View 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"""