Merged in schesis/aenum (pull request #1)
Add detailed docs link to README
diff --git a/.hgtags b/.hgtags
index 9d863c4..62a298e 100644
--- a/.hgtags
+++ b/.hgtags
@@ -17,3 +17,8 @@
9a637f3a1603b915b0555a55a0128d4031d94f74 1.4.7
25863a30e91fa7518cf48847a55b8692cedbbab0 2.0.0
28564353661b271d4a080daccabbef5a720fee1e 2.0.1
+e31a83fe494524746c11f5ea1fa6934c8781b5a8 2.0.2
+2f9765ca16315b852442ef0b0bf6e5be9e038b2c 2.0.3
+5d74a19813c7b19c11cb02558fe81037358b2f58 2.0.4
+039f81808a365ee496f3e36b083bd89e42d68f2a 2.0.5
+3d59c21500e43a52c4145c54f7f6ae73c93f7202 2.0.6
diff --git a/aenum/CHANGES b/aenum/CHANGES
index 54b98a3..58b0a63 100644
--- a/aenum/CHANGES
+++ b/aenum/CHANGES
@@ -1,3 +1,37 @@
+2.0.6
+=====
+
+constants cannot be deleted (they already couldn't be changed)
+constants can be used to define other constants
+
+
+2.0.5
+=====
+
+_init_ and MultiValue can now work together
+
+
+2.0.4
+=====
+
+_init_ and AutoValue (and _generate_next_value_) can now work together to
+supply missing values even when some of the required values per member are
+absent
+
+
+2.0.3
+=====
+
+add _missing_value_ and _missing_name_ methods, deprecate _missing_
+make enum instances comparable
+
+
+2.0.2
+=====
+
+both EnumMeta.__getattr__ and Enum.__new__ fall back to _missing_
+
+
2.0.1
=====
diff --git a/aenum/__init__.py b/aenum/__init__.py
index 9abaf3c..65bc168 100644
--- a/aenum/__init__.py
+++ b/aenum/__init__.py
@@ -24,6 +24,8 @@
if pyver < 3:
from operator import div as _div_
+import inspect
+
__all__ = [
'NamedConstant', 'constant', 'skip'
'Enum', 'IntEnum', 'AutoNumberEnum', 'OrderedEnum', 'UniqueEnum',
@@ -34,7 +36,7 @@
if sqlite3 is None:
__all__.remove('SqliteEnum')
-version = 2, 0, 1
+version = 2, 0, 6
try:
any
@@ -152,6 +154,27 @@
cls.__reduce_ex__ = _break_on_call_reduce
cls.__module__ = '<unknown>'
+def _check_auto_args(method):
+ """check if new generate method supports *args and **kwds"""
+ if isinstance(method, staticmethod):
+ method = method.__get__(type)
+ method = getattr(method, 'im_func', method)
+ args, varargs, keywords, defaults = inspect.getargspec(method)
+ return varargs is not None and keywords is not None
+
+def _get_attr_from_chain(cls, attr):
+ sentinel = object()
+ for basecls in cls.mro():
+ obj = basecls.__dict__.get(attr, sentinel)
+ if obj is not sentinel:
+ return obj
+
+def _value(obj):
+ if isinstance(obj, (auto, constant)):
+ return obj.value
+ else:
+ return obj
+
################
# Constant stuff
################
@@ -172,6 +195,98 @@
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
+ def __and__(self, other):
+ return _and_(self.value, _value(other))
+
+ def __rand__(self, other):
+ return _and_(_value(other), self.value)
+
+ def __invert__(self):
+ return _inv_(self.value)
+
+ def __or__(self, other):
+ return _or_(self.value, _value(other))
+
+ def __ror__(self, other):
+ return _or_(_value(other), self.value)
+
+ def __xor__(self, other):
+ return _xor_(self.value, _value(other))
+
+ def __rxor__(self, other):
+ return _xor_(_value(other), self.value)
+
+ def __abs__(self):
+ return _abs_(self.value)
+
+ def __add__(self, other):
+ return _add_(self.value, _value(other))
+
+ def __radd__(self, other):
+ return _add_(_value(other), self.value)
+
+ def __neg__(self):
+ return _neg_(self.value)
+
+ def __pos__(self):
+ return _pos_(self.value)
+
+ if pyver < 3:
+ def __div__(self, other):
+ return _div_(self.value, _value(other))
+
+ def __rdiv__(self, other):
+ return _div_(_value(other), (self.value))
+
+ def __floordiv__(self, other):
+ return _floordiv_(self.value, _value(other))
+
+ def __rfloordiv__(self, other):
+ return _floordiv_(_value(other), self.value)
+
+ def __truediv__(self, other):
+ return _truediv_(self.value, _value(other))
+
+ def __rtruediv__(self, other):
+ return _truediv_(_value(other), self.value)
+
+ def __lshift__(self, other):
+ return _lshift_(self.value, _value(other))
+
+ def __rlshift__(self, other):
+ return _lshift_(_value(other), self.value)
+
+ def __rshift__(self, other):
+ return _rshift_(self.value, _value(other))
+
+ def __rrshift__(self, other):
+ return _rshift_(_value(other), self.value)
+
+ def __mod__(self, other):
+ return _mod_(self.value, _value(other))
+
+ def __rmod__(self, other):
+ return _mod_(_value(other), self.value)
+
+ def __mul__(self, other):
+ return _mul_(self.value, _value(other))
+
+ def __rmul__(self, other):
+ return _mul_(_value(other), self.value)
+
+ def __pow__(self, other):
+ return _pow_(self.value, _value(other))
+
+ def __rpow__(self, other):
+ return _pow_(_value(other), self.value)
+
+ def __sub__(self, other):
+ return _sub_(self.value, _value(other))
+
+ def __rsub__(self, other):
+ return _sub_(_value(other), self.value)
+
+
NamedConstant = None
@@ -350,8 +465,30 @@
Helper class to track args, kwds.
"""
def __init__(self, *args, **kwds):
- self.args = args
- self.kwds = kwds
+ self._args = args
+ self._kwds = kwds.items()
+ self._hash = hash(args)
+
+ @property
+ def args(self):
+ return self._args
+
+ @property
+ def kwds(self):
+ return dict([(k, v) for k, v in self._kwds])
+
+ def __hash__(self):
+ return self._hash
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ return NotImplemented
+ return self.args == other.args and self.kwds == other.kwds
+
+ def __ne__(self, other):
+ if not isinstance(other, self.__class__):
+ return NotImplemented
+ return self.args != other.args or self.kwds != other.kwds
def __repr__(self):
final = []
@@ -364,7 +501,7 @@
return 'enum(%s)' % ', '.join(final)
_auto_null = object()
-class auto(object):
+class auto(enum):
"""
Instances are replaced with an appropriate value in Enum class suites.
"""
@@ -667,10 +804,13 @@
if _is_sunder(key):
if key not in (
'_init_', '_settings_', '_order_', '_ignore_', '_start_',
- '_create_pseudo_member_', '_generate_next_value_', '_missing_',
+ '_create_pseudo_member_', '_generate_next_value_',
+ '_missing_', '_missing_value_', '_missing_name_',
):
raise ValueError('_names_ are reserved for Enum use')
- elif not self._allow_init and key not in ('create_pseudo_member_', '_missing_'):
+ elif not self._allow_init and key not in (
+ 'create_pseudo_member_', '_missing_', '_missing_value_', '_missing_name_',
+ ):
# sunder is used during creation, must be specified first
raise ValueError('cannot set %r after init phase' % (key,))
elif key == '_ignore_':
@@ -700,8 +840,6 @@
self._settings |= set(value)
if NoAlias in value and Unique in value:
raise TypeError('cannot specify both NoAlias and Unique')
- elif MultiValue in value and self._init:
- raise TypeError('cannot specify both MultiValue and _init_ fields')
elif MultiValue in value and NoAlias in value:
raise TypeError('cannot specify both MultiValue and NoAlias')
elif AutoValue in value and AutoNumber in value:
@@ -720,14 +858,13 @@
elif key == '_init_':
if self._constructor_init:
raise TypeError('init specified in constructor and in class body')
- if value and self._multivalue:
- raise TypeError('cannot specify both MultiValue and _init_ fields')
_init_ = value
if isinstance(_init_, basestring):
_init_ = _init_.replace(',',' ').split()
self._init = _init_
elif key == '_generate_next_value_':
setattr(self, '_generate_next_value', value)
+ self._auto_args = _check_auto_args(value)
elif _is_dunder(key):
if key == '__order__':
key = '_order_'
@@ -749,8 +886,29 @@
if self._multivalue and isinstance(value, tuple):
if self._autonumber:
self._value = value[0]
- elif self._autovalue:
- pass
+ elif self._autovalue and self._init and not isinstance(value, auto):
+ # call generate iff init is specified and calls for more values than are present
+ target_values = len(self._init)
+ if not isinstance(value, tuple):
+ value = (value, )
+ source_values = len(value)
+ if target_values != source_values:
+ if self._auto_args:
+ value = self._generate_next_value(
+ key, 1,
+ len(self._member_names),
+ self._last_values[:],
+ *value
+ )
+ else:
+ value = self._generate_next_value(
+ key,
+ 1,
+ len(self._member_names),
+ self._last_values[:],
+ )
+
+
elif self._autonumber and not self._locked:
# convert any auto instances to integers
if isinstance(value, auto):
@@ -807,7 +965,22 @@
self._value = value
else:
if value.value == _auto_null:
- value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:])
+ if self._auto_args:
+ value.value = self._generate_next_value(
+ key,
+ 1,
+ len(self._member_names),
+ self._last_values[:],
+ *value.args,
+ **value.kwds
+ )
+ else:
+ value.value = self._generate_next_value(
+ key,
+ 1,
+ len(self._member_names),
+ self._last_values[:],
+ )
value = value.value
else:
pass
@@ -847,8 +1020,6 @@
# check for custom settings
if NoAlias in settings and Unique in settings:
raise TypeError('cannot specify both NoAlias and Unique')
- elif MultiValue in settings and init is not None:
- raise TypeError('cannot specify both MultiValue and INIT fields')
elif MultiValue in settings and NoAlias in settings:
raise TypeError('cannot specify both MultiValue and NoAlias')
elif AutoValue in settings and AutoNumber in settings:
@@ -884,6 +1055,8 @@
_create_pseudo_member_ = clsdict.pop('_create_pseudo_member_', None)
_generate_next_value_ = clsdict.pop('_generate_next_value_', None)
_missing_ = clsdict.pop('_missing_', None)
+ _missing_value_ = clsdict.pop('_missing_value_', None)
+ _missing_name_ = clsdict.pop('_missing_name_', None)
enum_members = dict([
(k, v) for (k, v) in clsdict.items()
if not (_is_sunder(k) or _is_dunder(k) or _is_descriptor(v))
@@ -898,7 +1071,10 @@
calced_order = _order_
original_dict = clsdict
clsdict = metacls.__prepare__(cls, bases, init=init, start=start, settings=settings)
- for name in ('_ignore_', '_create_pseudo_member_', '_generate_next_value_', '_missing_', '_order_'):
+ for name in (
+ '_ignore_', '_create_pseudo_member_', '_generate_next_value_', '_order_'
+ , '_missing_', '_missing_value_', '_missing_name_',
+ ):
attr = locals()[name]
if attr is not None:
clsdict[name] = attr
@@ -908,7 +1084,8 @@
for k, v in original_dict.items():
if k not in calced_order:
clsdict[k] = v
- del _order_, _ignore_, _create_pseudo_member_, _generate_next_value_, _missing_
+ del _order_, _ignore_, _create_pseudo_member_, _generate_next_value_
+ del _missing_, _missing_value_, _missing_name_
# resume normal path
clsdict._locked = True
member_type, first_enum = metacls._get_mixins_(bases)
@@ -1007,7 +1184,7 @@
else:
value, args = args[0], args[1:]
args, more_args = (value, ), args
- elif multivalue:
+ elif multivalue and not creating_init:
args, more_values = args[0:1], args[1:]
value = args[0]
if member_type is tuple: # special case for tuple enums
@@ -1057,9 +1234,12 @@
setattr(enum_class, member_name, enum_member)
# now add to _member_map_
enum_class._member_map_[member_name] = enum_member
- values = (value, ) + more_values
+ if multivalue and creating_init:
+ values = args
+ else:
+ values = (value, ) + more_values
enum_member._values_ = values
- for value in (value, ) + more_values:
+ for value in values:
# first check if value has already been used
if multivalue and (
value in enum_class._value2member_map_
@@ -1178,7 +1358,12 @@
# (see issue19025).
if attr in cls._member_map_:
raise AttributeError(
- "%s: cannot delete Enum member." % cls.__name__)
+ "%s: cannot delete Enum member %r." % (cls.__name__, attr),
+ )
+ if isinstance(_get_attr_from_chain(cls, attr), constant):
+ raise AttributeError(
+ "%s: cannot delete constant %r" % (cls.__name__, attr),
+ )
super(EnumMeta, cls).__delattr__(attr)
def __dir__(self):
@@ -1207,10 +1392,18 @@
try:
return cls._member_map_[name]
except KeyError:
- raise AttributeError(name)
+ raise AttributeError
def __getitem__(cls, name):
- return cls._member_map_[name]
+ try:
+ return cls._member_map_[name]
+ except KeyError:
+ exc = _sys.exc_info()[1]
+ result = cls._missing_name_(name)
+ if isinstance(result, cls):
+ return result
+ else:
+ raise exc
def __iter__(cls):
return (cls._member_map_[name] for name in cls._member_names_)
@@ -1235,10 +1428,14 @@
"""
member_map = cls.__dict__.get('_member_map_', {})
if name in member_map:
- raise AttributeError('Cannot rebind %s.' % name)
+ raise AttributeError(
+ '%s: cannot rebind member %r.' % (cls.__name__, name),
+ )
cur_obj = cls.__dict__.get(name)
if isinstance(cur_obj, constant):
- raise AttributeError('Cannot rebind %r' % (cur_obj,))
+ raise AttributeError(
+ '%s: cannot rebind constant %r' % (cls.__name__, name),
+ )
super(EnumMeta, cls).__setattr__(name, value)
def _create_(cls, class_name, names=None, module=None, type=None, start=1):
@@ -1517,12 +1714,16 @@
if name == value:
return member
# still not found -- try _missing_ hook
- return cls._missing_(value)
+ result = cls._missing_value_(value)
+ if isinstance(result, cls):
+ return result
+ else:
+ raise ValueError("%r is not a valid %s" % (value, cls.__name__))
temp_enum_dict['__new__'] = __new__
del __new__
@staticmethod
-def _generate_next_value_(name, start, count, last_values):
+def _generate_next_value_(name, start, count, last_values, *args, **kwds):
for last_value in reversed(last_values):
try:
return last_value + 1
@@ -1535,10 +1736,25 @@
@classmethod
def _missing_(cls, value):
- raise ValueError("%s is not a valid %s" % (value, cls.__name__))
+ "deprecated, use _missing_value_ instead"
+ return None
temp_enum_dict['_missing_'] = _missing_
del _missing_
+@classmethod
+def _missing_value_(cls, value):
+ "used for failed value access"
+ return cls._missing_(value)
+temp_enum_dict['_missing_value_'] = _missing_value_
+del _missing_value_
+
+@classmethod
+def _missing_name_(cls, name):
+ "used for failed item access"
+ return None
+temp_enum_dict['_missing_name_'] = _missing_name_
+del _missing_name_
+
def __repr__(self):
return "<%s.%s: %r>" % (
self.__class__.__name__, self._name_, self._value_)
diff --git a/aenum/doc/aenum.rst b/aenum/doc/aenum.rst
index f6a8615..d45cd99 100644
--- a/aenum/doc/aenum.rst
+++ b/aenum/doc/aenum.rst
@@ -256,6 +256,8 @@
The various settings enable special behavior:
- ``AutoNumber`` is the same as specifying ``start=1``
+- ``AutoValue`` calls a user supplied ``_generate_next_value_`` to provide
+ missing/auto() values
- ``MultiValue`` allows multiple values per member instead of the usual 1
- ``NoAlias`` allows different members to have the same value
- ``Unique`` disallows different members to have the same value
@@ -857,7 +859,7 @@
>>> Planet.G = 9
Traceback (most recent call last):
...
- AttributeError: Cannot rebind constant(6.673e-11)
+ AttributeError: Planet: cannot rebind constant 'G'
skip
^^^^
@@ -878,8 +880,8 @@
>>> Color.opacity
0.77
-start (py3 only)
-^^^^^^^^^^^^^^^^
+start
+^^^^^
When using Python 3 you have the option of turning on auto-numbering
(useful for when you don't care which numbers are assigned as long as
@@ -891,10 +893,21 @@
>>> Color.blue
<Color.blue: 3>
+This can also be done in Python 2, albeit not as elegantly::
+
+ >>> class Color(Enum): # doctest: +SKIP
+ ... _start_ = 1
+ ... red = auto()
+ ... green = auto()
+ ... blue = auto()
+ ...
+ >>> Color.blue
+ <Color.blue: 3>
+
.. note:: auto-numbering turns off when a non-member is defined
-init (py3 only)
-^^^^^^^^^^^^^^^
+init
+^^^^
If you need an ``__init__`` method that does nothing besides save its
arguments, ``init`` is for you::
@@ -915,6 +928,40 @@
>>> Planet.JUPITER.mass
1.9e+27
+combining init and AutoValue
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When a member will have multiple values, and some of them have an easy to
+calculate default value, ``init`` and ``AutoValue`` can be combined. Here
+is the Python 2 version::
+
+ >>> from aenum import AutoValue
+ >>> class SelectionEnum(Enum):
+ ... _init_ = 'db user'
+ ... _settings_ = AutoValue
+ ... def __new__(cls, *args, **kwds):
+ ... count = len(cls.__members__)
+ ... obj = object.__new__(cls)
+ ... obj._count = count
+ ... obj._value_ = args
+ ... return obj
+ ... @staticmethod
+ ... def _generate_next_value_(name, start, count, values, *args, **kwds):
+ ... return (name, ) + args
+ ...
+ >>> class NotificationType(SelectionEnum):
+ ... # usually, name is the same as db
+ ... # but not for blanks
+ ... blank = '', ''
+ ... C = 'Catalog'
+ ... S = 'Sheet'
+ ... B = 'Both'
+ ...
+ >>> NotificationType.blank
+ <NotificationType.blank: ('', '')>
+ >>> NotificationType.B
+ <NotificationType.B: ('B', 'Both')>
+
Decorators
----------
diff --git a/aenum/test.py b/aenum/test.py
index c5799f0..28dd82d 100644
--- a/aenum/test.py
+++ b/aenum/test.py
@@ -5,8 +5,8 @@
import doctest
import unittest
from aenum import EnumMeta, Enum, IntEnum, AutoNumberEnum, OrderedEnum, UniqueEnum, Flag, IntFlag
-from aenum import NamedTuple, TupleSize, NamedConstant, constant, NoAlias, AutoNumber, Unique
-from aenum import _reduce_ex_by_name, unique, skip, extend_enum, auto
+from aenum import NamedTuple, TupleSize, NamedConstant, constant, NoAlias, AutoNumber, AutoValue, Unique
+from aenum import _reduce_ex_by_name, unique, skip, extend_enum, auto, enum, MultiValue
from collections import OrderedDict
from datetime import timedelta
from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
@@ -45,10 +45,10 @@
try:
from enum import EnumMeta as StdlibEnumMeta, Enum as StdlibEnum
- import enum
- if hasattr(enum, 'version'):
+ import enum as enum_module
+ if hasattr(enum_module, 'version'):
StdlibEnumMeta = StdlibEnum = None
- del enum
+ del enum_module
except ImportError:
StdlibEnumMeta = StdlibEnum = None
@@ -274,13 +274,75 @@
# operator.div is gone in 3
if pyver < 3:
tester(12, _div_, 12 // 5, 5)
- # strigs are a pain
+ # strings are a pain
left = auto()
right = 'eggs'
value = _mod_(left, right)
left.value = 'I see 17 %s!'
self.assertEqual(value.value, 'I see 17 %s!' % 'eggs')
+ def test_constant(self):
+ errors = []
+ def tester(first, op, final, second=None):
+ if second is None:
+ primary = constant(first)
+ secondary = constant(op(primary))
+ if secondary.value != final:
+ errors.append(
+ "%s %r -> %r != %r" % (op.__name__, first, secondary.value, final),
+ )
+ else:
+ left = constant(first)
+ right = second
+ value = op(left, right)
+ if value != final:
+ errors.append(
+ "forward: %r %s %r -> %r != %r" % (first, op.__name__, second, value, final),
+ )
+ left = first
+ right = constant(second)
+ value = op(left, right)
+ if value != final:
+ errors.append(
+ "reversed: %r %s %r -> %r != %r" % (second, op.__name__, first, value, final),
+ )
+ for args in (
+ (1, _abs_, abs(1)),
+ (-3, _abs_, abs(-3)),
+ (1, _add_, 1+2, 2),
+ (25, _floordiv_, 25 // 5, 5),
+ (49, _truediv_, 49 / 9, 9),
+ (6, _mod_, 6 % 9, 9),
+ (5, _lshift_, 5 << 2, 2),
+ (5, _rshift_, 5 >> 2, 2),
+ (3, _mul_, 3 * 6, 6),
+ (5, _neg_, -5),
+ (-4, _pos_, +(-4)),
+ (2, _pow_, 2**5, 5),
+ (7, _sub_, 7 - 10, 10),
+ (1, _or_, 1 | 2, 2),
+ (3, _xor_, 3 ^ 6, 6),
+ (3, _and_, 3 & 6, 6),
+ (7, _inv_, ~7),
+ ('a', _add_, 'a'+'b', 'b'),
+ ('a', _mul_, 'a' * 3, 3),
+ ):
+ tester(*args)
+ # operator.div is gone in 3
+ if pyver < 3:
+ tester(12, _div_, 12 // 5, 5)
+ # strings are a pain
+ left = constant('I see 17 %s!')
+ right = 'eggs'
+ value = _mod_(left, right)
+ if value != 'I see 17 %s!' % 'eggs':
+ errors.append("'I see 17 eggs!' != %r" % value)
+ if errors:
+ print()
+ for error in errors:
+ print(error)
+ self.assertTrue(False)
+
class TestEnum(TestCase):
@@ -367,6 +429,14 @@
repr(e),
'<Season.%s: %s>' % (season, i),
)
+ def test_enum_helper(self):
+ e1 = enum(1, 2, three=9)
+ e2 = enum(1, 2, three=9)
+ e3 = enum(1, 2, 9)
+ self.assertTrue(e1 is not e2)
+ self.assertEqual(e1, e2)
+ self.assertNotEqual(e1, e3)
+ self.assertNotEqual(e2, e3)
def test_value_name(self):
Season = self.Season
@@ -1633,6 +1703,67 @@
self.assertTrue(Grade.D < Grade.A)
self.assertTrue(Grade.B >= Grade.B)
+ def test_missing_deprecated(self):
+ class Label(Enum):
+ AnyApple = 0
+ RedApple = 1
+ GreenApple = 2
+ @classmethod
+ def _missing_(cls, name):
+ return cls.AnyApple
+
+ self.assertEqual(Label.AnyApple, Label(4))
+ with self.assertRaises(AttributeError):
+ Label.redapple
+ with self.assertRaises(KeyError):
+ Label['redapple']
+
+ def test_missing(self):
+ class Label(Enum):
+ AnyApple = 0
+ RedApple = 1
+ GreenApple = 2
+ @classmethod
+ def _missing_value_(cls, name):
+ return cls.AnyApple
+
+ self.assertEqual(Label.AnyApple, Label(4))
+ with self.assertRaises(AttributeError):
+ Label.redapple
+ with self.assertRaises(KeyError):
+ Label['redapple']
+
+ def test_missing_name(self):
+ class Label(Enum):
+ RedApple = 1
+ GreenApple = 2
+ @classmethod
+ def _missing_name_(cls, name):
+ for member in cls:
+ if member.name.lower() == name.lower():
+ return member
+
+ Label['redapple']
+ with self.assertRaises(AttributeError):
+ Label.redapple
+ with self.assertRaises(ValueError):
+ Label('redapple')
+
+ def test_missing_name_bad_return(self):
+ class Label(Enum):
+ RedApple = 1
+ GreenApple = 2
+ @classmethod
+ def _missing_name_(cls, name):
+ return None
+
+ with self.assertRaises(AttributeError):
+ Label.redapple
+ with self.assertRaises(ValueError):
+ Label('redapple')
+ with self.assertRaises(KeyError):
+ Label['redapple']
+
def test_extending2(self):
def bad_extension():
class Shade(Enum):
@@ -1684,6 +1815,23 @@
self.assertEqual(len(Color), 4)
self.assertEqual(Color.red.value, 1)
+ def test_extend_intenum(self):
+ class Index(Enum):
+ DeviceType = 0x1000
+ ErrorRegister = 0x1001
+
+ for name, value in (
+ ('ControlWord', 0x6040),
+ ('StatusWord', 0x6041),
+ ('OperationMode', 0x6060),
+ ):
+ extend_enum(Index, name, value)
+
+ self.assertEqual(len(Index), 5)
+ self.assertEqual(list(Index), [Index.DeviceType, Index.ErrorRegister, Index.ControlWord, Index.StatusWord, Index.OperationMode])
+ self.assertEqual(Index.DeviceType.value, 0x1000)
+ self.assertEqual(Index.StatusWord.value, 0x6041)
+
def test_no_duplicates(self):
def bad_duplicates():
class Color1(UniqueEnum):
@@ -1867,6 +2015,66 @@
third = auto()
self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
+ def test_auto_value_with_auto(self):
+
+ class SelectionEnum(Enum):
+ _init_ = 'db user'
+ def __new__(cls, *args, **kwds):
+ count = len(cls.__members__)
+ obj = object.__new__(cls)
+ obj._count = count
+ obj._value_ = args
+ obj.db, obj.user = args
+ return obj
+ @staticmethod
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (name, ) + args
+
+ class Test(SelectionEnum):
+ _order_ = 'this that'
+ this = auto('these')
+ that = auto('those')
+
+ self.assertEqual(list(Test), [Test.this, Test.that])
+ self.assertEqual(Test.this.name, 'this')
+ self.assertEqual(Test.this.value, ('this', 'these'))
+ self.assertEqual(Test.this.db, 'this')
+ self.assertEqual(Test.this.user, 'these')
+ self.assertEqual(Test.that.name, 'that')
+ self.assertEqual(Test.that.value, ('that', 'those'))
+ self.assertEqual(Test.that.db, 'that')
+ self.assertEqual(Test.that.user, 'those')
+
+ def test_auto_value_with_autovalue(self):
+
+ class SelectionEnum(Enum):
+ _init_ = 'db user'
+ _settings_ = AutoValue
+ def __new__(cls, *args, **kwds):
+ count = len(cls.__members__)
+ obj = object.__new__(cls)
+ obj._count = count
+ obj._value_ = args
+ return obj
+ @staticmethod
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (name, ) + args
+
+ class Test(SelectionEnum):
+ _order_ = 'this that'
+ this = 'these'
+ that = 'those'
+
+ self.assertEqual(list(Test), [Test.this, Test.that])
+ self.assertEqual(Test.this.name, 'this')
+ self.assertEqual(Test.this.value, ('this', 'these'))
+ self.assertEqual(Test.this.db, 'this')
+ self.assertEqual(Test.this.user, 'these')
+ self.assertEqual(Test.that.name, 'that')
+ self.assertEqual(Test.that.value, ('that', 'those'))
+ self.assertEqual(Test.that.db, 'that')
+ self.assertEqual(Test.that.user, 'those')
+
def test_empty_with_functional_api(self):
empty = aenum.IntEnum('Foo', {})
self.assertEqual(len(empty), 0)
@@ -2014,6 +2222,23 @@
self.assertEqual(Color.green.value, 2)
self.assertEqual(Color.blue.value, 3)
+ def test_MultiValue_with_init(self):
+ class Color(Enum):
+ _init_ = 'color r g b'
+ _settings_ = MultiValue
+ red = 'red', 1, 2, 3
+ green = 'green', 4, 5, 6
+ blue = 'blue', 7, 8, 9
+ self.assertEqual(Color.red.r, 1)
+ self.assertEqual(Color.red.g, 2)
+ self.assertEqual(Color.red.b, 3)
+ self.assertEqual(Color.green.r, 4)
+ self.assertEqual(Color.green.g, 5)
+ self.assertEqual(Color.green.b, 6)
+ self.assertEqual(Color.blue.r, 7)
+ self.assertEqual(Color.blue.g, 8)
+ self.assertEqual(Color.blue.b, 9)
+
def test_combine_new_settings_with_old_settings(self):
class Auto(Enum):
_settings_ = Unique
@@ -2058,6 +2283,21 @@
elementD = 'd'
self.assertIs(enumA.enumB, enumA.__dict__['enumB'])
+ def test_constantness_of_constants(self):
+ class Universe(Enum):
+ PI = constant(3.141596)
+ G = constant(6.67300E-11)
+ self.assertEqual(Universe.PI, 3.141596)
+ self.assertRaisesRegex(AttributeError, 'cannot rebind constant', setattr, Universe, 'PI', 9)
+ self.assertRaisesRegex(AttributeError, 'cannot delete constant', delattr, Universe, 'PI')
+
+ def test_math_and_stuff_with_constants(self):
+ class Universe(Enum):
+ PI = constant(3.141596)
+ TAU = constant(2 * PI)
+ self.assertEqual(Universe.PI, 3.141596)
+ self.assertEqual(Universe.TAU, 2 * Universe.PI)
+
if StdlibEnumMeta is not None:
def test_stdlib_inheritence(self):
self.assertTrue(isinstance(self.Season, StdlibEnumMeta))
@@ -3225,6 +3465,10 @@
CONVERT_TEST_NAME_A = 5 # This one should sort first.
CONVERT_TEST_NAME_E = 5
CONVERT_TEST_NAME_F = 5
+CONVERT_TEST_SIGABRT = 4 # and this one
+CONVERT_TEST_SIGIOT = 4
+CONVERT_TEST_EIO = 7
+CONVERT_TEST_EBUS = 7 # and this one
class TestIntEnumConvert(TestCase):
def test_convert_value_lookup_priority(self):
@@ -3236,6 +3480,16 @@
# multiple possible names for a given value. It should always
# report the first lexigraphical name in that case.
self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A')
+ self.assertEqual(test_type(4).name, 'CONVERT_TEST_SIGABRT')
+ self.assertEqual(test_type(7).name, 'CONVERT_TEST_EBUS')
+ self.assertEqual(
+ list(test_type),
+ [
+ test_type.CONVERT_TEST_SIGABRT,
+ test_type.CONVERT_TEST_NAME_A,
+ test_type.CONVERT_TEST_EBUS,
+ ],
+ )
def test_convert(self):
test_type = IntEnum._convert(
diff --git a/setup.py b/setup.py
index 2b4f6f4..31fd958 100644
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,7 @@
data = dict(
name='aenum',
- version='2.0.1',
+ version='2.0.6',
url='https://bitbucket.org/stoneleaf/aenum',
packages=['aenum'],
package_data={