1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
from __future__ import unicode_literals
import sys
import warnings
from enum import Enum
from marshmallow import ValidationError
from marshmallow.fields import Field
PY2 = sys.version_info.major == 2
# ugh Python 2
if PY2:
string_types = (str, unicode) # noqa: F821
text_type = unicode # noqa: F821
else:
string_types = (str, )
text_type = str
class LoadDumpOptions(Enum):
value = 1
name = 0
class EnumField(Field):
VALUE = LoadDumpOptions.value
NAME = LoadDumpOptions.name
default_error_messages = {
'by_name': 'Invalid enum member {input}',
'by_value': 'Invalid enum value {input}',
'must_be_string': 'Enum name must be string'
}
def __init__(
self, enum, by_value=False, load_by=None, dump_by=None, error='', *args, **kwargs
):
self.enum = enum
self.by_value = by_value
if error and any(old in error for old in ('name}', 'value}', 'choices}')):
warnings.warn(
"'name', 'value', and 'choices' fail inputs are deprecated,"
"use input, names and values instead",
DeprecationWarning,
stacklevel=2
)
self.error = error
if load_by is None:
load_by = LoadDumpOptions.value if by_value else LoadDumpOptions.name
if not isinstance(load_by, Enum) or load_by not in LoadDumpOptions:
raise ValueError(
'Invalid selection for load_by must be EnumField.VALUE or EnumField.NAME, got {}'.
format(load_by)
)
if dump_by is None:
dump_by = LoadDumpOptions.value if by_value else LoadDumpOptions.name
if not isinstance(dump_by, Enum) or dump_by not in LoadDumpOptions:
raise ValueError(
'Invalid selection for load_by must be EnumField.VALUE or EnumField.NAME, got {}'.
format(dump_by)
)
self.load_by = load_by
self.dump_by = dump_by
super(EnumField, self).__init__(*args, **kwargs)
def _serialize(self, value, attr, obj):
if value is None:
return None
elif self.dump_by == LoadDumpOptions.value:
return value.value
else:
return value.name
def _deserialize(self, value, attr, data, **kwargs):
if value is None:
return None
elif self.load_by == LoadDumpOptions.value:
return self._deserialize_by_value(value, attr, data)
else:
return self._deserialize_by_name(value, attr, data)
def _deserialize_by_value(self, value, attr, data):
try:
return self.enum(value)
except ValueError:
self.fail('by_value', input=value, value=value)
def _deserialize_by_name(self, value, attr, data):
if not isinstance(value, string_types):
self.fail('must_be_string', input=value, name=value)
try:
return getattr(self.enum, value)
except AttributeError:
self.fail('by_name', input=value, name=value)
def fail(self, key, **kwargs):
kwargs['values'] = ', '.join([text_type(mem.value) for mem in self.enum])
kwargs['names'] = ', '.join([mem.name for mem in self.enum])
if self.error:
if self.by_value:
kwargs['choices'] = kwargs['values']
else:
kwargs['choices'] = kwargs['names']
msg = self.error.format(**kwargs)
raise ValidationError(msg)
else:
super(EnumField, self).fail(key, **kwargs)
|