Dual mode enums - legacy and new style

This commit is contained in:
Aviv Eyal 2019-04-12 16:23:50 -07:00
parent 40ba414bd5
commit 9f3d386b89
7 changed files with 87 additions and 10 deletions

View File

@ -37,8 +37,9 @@ class GrapheneScalarType(GrapheneGraphQLType, GraphQLScalarType):
class GrapheneEnumType(GrapheneGraphQLType, GraphQLEnumType):
def serialize(self, value):
if value in self.graphene_type._meta.enum:
return value.name
if not self.graphene_type._meta.legacy_enum_resolver:
if value in self.graphene_type._meta.enum:
return value.name
return super(GrapheneEnumType, self).serialize(value)

View File

@ -36,7 +36,15 @@ def _filter_magic_members(classdict):
class EnumMeta(SubclassWithMeta_Meta):
def __new__(cls, name, bases, classdict, **options):
meta_class = classdict.get("Meta")
if meta_class is None or not hasattr(meta_class, "legacy_enum_resolver"):
is_legacy = True
else:
is_legacy = meta_class.legacy_enum_resolver
enum_members = _filter_magic_members(classdict)
if is_legacy:
enum_members["__eq__"] = eq_enum
enum = PyEnum(name, enum_members)
return SubclassWithMeta_Meta.__new__(
cls, name, bases, OrderedDict(classdict, __enum__=enum), **options
@ -57,12 +65,13 @@ class EnumMeta(SubclassWithMeta_Meta):
return cls.from_enum(PyEnum(*args, **kwargs), description=description)
return super(EnumMeta, cls).__call__(*args, **kwargs)
def from_enum(cls, enum, description=None, deprecation_reason=None): # noqa: N805
def from_enum(cls, enum, description=None, deprecation_reason=None, legacy_enum_resolver=True): # noqa: N805
description = description or enum.__doc__
meta_dict = {
"enum": enum,
"description": description,
"deprecation_reason": deprecation_reason,
"legacy_enum_resolver": legacy_enum_resolver,
}
meta_class = type("Meta", (object,), meta_dict)
return type(meta_class.enum.__name__, (Enum,), {"Meta": meta_class})
@ -75,6 +84,7 @@ class Enum(six.with_metaclass(EnumMeta, UnmountedType, BaseType)):
_meta = EnumOptions(cls)
_meta.enum = enum or cls.__enum__
_meta.deprecation_reason = options.pop("deprecation_reason", None)
_meta.legacy_enum_resolver = options.pop("legacy_enum_resolver", True)
for key, value in _meta.enum.__members__.items():
setattr(cls, key, value)

View File

@ -182,6 +182,29 @@ def test_enum_value_as_unmounted_argument():
assert isinstance(unmounted_field, Argument)
assert unmounted_field.type == RGB
def test_legacy_enum_can_be_compared():
class RGB(Enum):
RED = 1
GREEN = 2
BLUE = 3
assert RGB.RED == 1
assert RGB.GREEN == 2
assert RGB.BLUE == 3
def test_new_enum_only_compare_to_enum_instances():
class RGBBase(PyEnum):
RED = 1
GREEN = 2
BLUE = 3
RGB = Enum.from_enum(RGBBase, legacy_enum_resolver=False)
assert RGB.RED == RGBBase.RED
assert RGB.GREEN == RGBBase.GREEN
assert RGB.BLUE == RGBBase.BLUE
assert RGB.RED != 1
assert RGB.GREEN != 2
assert RGB.BLUE != 3
def test_enum_can_be_initialzied():
class RGB(Enum):

View File

@ -46,8 +46,10 @@ def _call_and_get_arg(mocker, resolver_name, query):
def test_resolve_enum_python(mocker):
arg = _call_and_get_arg(mocker, "resolve_python", "{python(v:P2)}")
assert arg == PythonBaseEnum.P2
assert arg == PythonEnum.P2
assert arg is PythonBaseEnum.P2
assert arg is not PythonBaseEnum.P2.value
assert arg is PythonEnum.P2
assert arg is not PythonEnum.P2.value
def test_resolve_enum_default_value_python(mocker):

View File

@ -69,7 +69,7 @@ def _call_and_get_arg(mocker, resolver_name, query):
def test_resolve_simple_enum(mocker):
arg = _call_and_get_arg(mocker, "resolve_simple", "{simple(v:S2)}")
assert arg == SimpleEnum.S2
assert arg == SimpleEnum.S2.value
def test_resolve_enum_python(mocker):

View File

@ -13,6 +13,7 @@ from graphql.type import (
from ..dynamic import Dynamic
from ..enum import Enum
from enum import Enum as PyEnum
from ..field import Field
from ..inputfield import InputField
from ..inputobjecttype import InputObjectType
@ -23,7 +24,7 @@ from ..structures import List, NonNull
from ..typemap import TypeMap, resolve_type
def test_enum():
def test_enum_legacy():
class MyEnum(Enum):
"""Description"""
@ -49,11 +50,46 @@ def test_enum():
assert values == [
GraphQLEnumValue(
name="foo",
value=MyEnum.foo,
value=1,
description="Description foo=1",
deprecation_reason="Is deprecated",
),
GraphQLEnumValue(name="bar", value=MyEnum.bar, description="Description bar=2"),
GraphQLEnumValue(name="bar", value=2, description="Description bar=2"),
]
def test_enum_new():
class MyEnumBase(PyEnum):
"""Description"""
foo = 1
bar = 2
@property
def description(self):
return "Description {}={}".format(self.name, self.value)
@property
def deprecation_reason(self):
if self == MyEnum.foo:
return "Is deprecated"
MyEnum = Enum.from_enum(MyEnumBase, legacy_enum_resolver=False)
typemap = TypeMap([MyEnum])
assert "MyEnumBase" in typemap
graphql_enum = typemap["MyEnumBase"]
assert isinstance(graphql_enum, GraphQLEnumType)
assert graphql_enum.name == "MyEnumBase"
assert graphql_enum.description == "Description"
values = graphql_enum.values
assert values == [
GraphQLEnumValue(
name="foo",
value=MyEnumBase.foo,
description="Description foo=1",
deprecation_reason="Is deprecated",
),
GraphQLEnumValue(name="bar", value=MyEnumBase.bar, description="Description bar=2"),
]

View File

@ -149,9 +149,14 @@ class TypeMap(GraphQLTypeMap):
if not deprecation_reason and callable(type._meta.deprecation_reason):
deprecation_reason = type._meta.deprecation_reason(value)
if type._meta.legacy_enum_resolver:
gql_value = value.value
else:
gql_value = value
values[name] = GraphQLEnumValue(
name=name,
value=value,
value=gql_value,
description=description,
deprecation_reason=deprecation_reason,
)