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={