aboutsummaryrefslogtreecommitdiff
path: root/venv/Lib/site-packages
diff options
context:
space:
mode:
Diffstat (limited to 'venv/Lib/site-packages')
-rw-r--r--venv/Lib/site-packages/aenum/CHANGES489
-rw-r--r--venv/Lib/site-packages/aenum/LICENSE32
-rw-r--r--venv/Lib/site-packages/aenum/__init__.py4111
-rw-r--r--venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pycbin0 -> 104964 bytes
-rw-r--r--venv/Lib/site-packages/aenum/__pycache__/_py3.cpython-39.pycbin0 -> 686 bytes
-rw-r--r--venv/Lib/site-packages/aenum/_py2.py7
-rw-r--r--venv/Lib/site-packages/aenum/_py3.py12
-rw-r--r--venv/Lib/site-packages/aenum/doc/aenum.rst1568
-rw-r--r--venv/Lib/site-packages/aenum/test.py6832
-rw-r--r--venv/Lib/site-packages/aenum/test_v3.py1982
-rw-r--r--venv/Lib/site-packages/blendmodes/__init__.py2
-rw-r--r--venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pycbin0 -> 285 bytes
-rw-r--r--venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pycbin0 -> 13302 bytes
-rw-r--r--venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pycbin0 -> 2850 bytes
-rw-r--r--venv/Lib/site-packages/blendmodes/blend.py511
-rw-r--r--venv/Lib/site-packages/blendmodes/blendtype.py72
-rw-r--r--venv/Lib/site-packages/blendmodes/imagetools.py48
17 files changed, 15666 insertions, 0 deletions
diff --git a/venv/Lib/site-packages/aenum/CHANGES b/venv/Lib/site-packages/aenum/CHANGES
new file mode 100644
index 00000000..e8799f6d
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/CHANGES
@@ -0,0 +1,489 @@
+3.1.8
+=====
+
+recalculate bits used after all flags created (sometimes needed when a custom
+`__new__` is in place.
+
+
+3.1.7
+=====
+
+update flag creation to (possibly) add bitwise operator methods to newly
+created flags
+
+update extend_enum() to work with 3.11 flags
+
+
+3.1.6
+=====
+
+Update `dir()` on mixed enums to include mixed data type methods and
+attributes.
+
+Rename `enum_property` to `property` to match stdlib. Recommended usage is
+`aenum.property` (prefix with module name).
+
+Remove quadritic creation behavior.
+
+
+BREAKING CHANGE BUG FIX that won't affect most people
+
+Enums with a custom `__new__` that:
+
+- use the enum machinery to generate the values; AND
+- have keyword arguments set to a default (like `None`)
+
+will fail to generate a missing value. To fix: remove the default value and
+instead specify it on the member creation line.
+
+BREAKING CHANGE
+
+In Python 3.11 the `str()` of mixed enums will now match its `format()` which
+will be the normal `str()` of the data type -- so for an IntEnum you'll see
+`5` instead of `Perm.R|X`. This affects IntEnum, StrEnum, and IntFlag.
+
+
+3.1.5
+=====
+
+fix support of `auto()` kwds
+
+
+3.1.3
+=====
+
+rename `aenum.property` to `aenum.enum_property`
+
+fix `enum_property` to work with `_init_` attributes
+
+
+3.1.2
+=====
+
+fix `extend_enum()` for unhashable values
+
+
+3.1.1
+=====
+
+fix `extend_enum()` for most cases
+
+
+3.1.0
+=====
+
+AddValue is similar to the old AutoNumber: it will always activate, but
+uses _generate_next_value_ to get the next value (so the user has some
+control over the return data type instead of always getting an int).
+
+
+BREAKING CHANGES
+
+AutoValue is gone. It was superflous and its removal simplified the code.
+Simply put the fields needed in an `_init_` and `_generate_next_value_`
+will be called to supply the missing values (this is probably already what
+is happening).
+
+
+
+3.0.0
+=====
+
+standard Enum usage is unchanged
+
+BREAKING CHANGES
+
+Enum
+- the more esoteric method of creating Enums have been modified or removed
+ - AutoNumber setting is gone, inherit from AutoNumberEnum instead
+ - creating members without specifying anything is removed (if you don't
+ know what this means, you weren't doing it)
+
+Flag
+- unique flags are canonical (i.e. flags with powers of two values such as
+ 1, 2, 4, 8, 16, etc.)
+- non-unique flags are aliases (i.e. values such as 3 or 7)
+- iteration of Flag and flag members only uses canonical flags
+
+
+ENHANCEMENTS
+
+Member creation has been redone to match Python 3.10's methods. This also
+allows all supported Pythons (2.7, 3.3+) to use the __set_name__ and
+__init_subclass__ protocols (more robustly than in aenum 2.2.5)
+
+
+CHANGES
+
+enum_property() has been renamed to property() (old name still available, but
+deprecated).
+
+bin() replacement shows negative integers in twos-complement
+
+
+
+
+2.2.5
+=====
+
+call __init_subclass__ after members have been added, and in Pythons < 3.6
+call __set_name__ in Pythons < 3.6
+do not convert/disallow private names
+add iteration/len support to NamedConstant
+
+
+2.2.4
+=====
+
+add support to Constant to retrieve members by value
+
+ --> class K(Constant):
+ ... one = 1
+ ... two = 2
+
+ --> K.one
+ <K.one: 1>
+
+ --> K(1)
+ <K.one: 1>
+
+add pickle/deepcopy support to Constant
+
+add support for Constant to use other Constant values
+ (resulting members /are not/ the same)
+
+ --> class C(Constant)
+ ... one = K.one
+ ... three = 3
+
+ --> C.one == K.one
+ True
+
+ --> C.one is K.one
+ False
+
+AutoNumber and auto() now work together
+
+Enum members are now added to the class as enum_property, which supports
+unshadowing of parent class attributes when called on an Enum member:
+
+ --> class StrEnum(str, Enum):
+ ... lower = 'lower'
+ ... upper = 'upper'
+ ... mixed = 'mixed'
+
+ --> StrEnum.lower
+ <StrEnum.lower: 'lower'>
+
+ --> StrEnum.lower.upper()
+ 'LOWER'
+
+ --> StrEnum.upper
+ <StrEnum.upper: 'upper'>
+
+ --> StrEnum.upper.upper()
+ 'UPPER'
+
+
+2.2.3
+=====
+
+use members' type's methods __str__, __repr__, __format__, and
+__reduce_ex__ if directly assigned in Enum class body; i.e.:
+
+ --> class Color(str, Enum):
+ ... red = 'red'
+ ... green = 'green'
+ ... blue = 'blue'
+ ... __str__ = str.__str__
+
+ --> print(repr(Color.green))
+ <Color.green: 'green'>
+
+ --> print(Color.green)
+ green
+
+
+2.2.2
+=====
+
+replace _RouteClassAttributeToGetattr with enum_property (it is still
+available as an alias)
+
+support constant() and auto() being used together:
+
+ --> class Fruit(Flag):
+ ... _order_ = 'apple banana lemon orange'
+ ... apple = auto()
+ ... banana = auto()
+ ... lemon = auto()
+ ... orange = auto()
+ ... CitrusTypes = constant(lemon | orange)
+
+ --> list(Fruit)
+ [Fruit.apple, Fruit.banana, Fruit.lemon, Fruit.orange]
+
+ --> list(Fruit.CitrusTypes)
+ [Fruit.orange, Fruit.lemon]
+
+ --> Fruit.orange in Fruit.CitrusTypes
+ True
+
+
+2.2.1
+=====
+
+allow Enums to be called without a value
+
+ class Color(Enum):
+ black = 0
+ red = 1
+ green = 2
+ blue = 3
+ #
+ @classmethod
+ def _missing_value_(cls, value):
+ if value is no_arg:
+ return cls.black
+
+ >>> Color()
+ <Color.black: 0>
+
+allow Enum name use while constructing Enum (Python 3.4+ only)
+
+ --> class Color(Enum):
+ ... _order_ = 'BLACK WHITE'
+ ... BLACK = Color('black', '#000')
+ ... WHITE = Color('white', '#fff')
+ ... #
+ ... def __init__(self, label, hex):
+ ... self.label = label
+ ... self.hex = hex
+
+
+2.2.0
+=====
+
+BREAKING CHANGE
+---------------
+In Python 3+ classes defined inside an Enum no longer become members by
+default; in Python 2 they still become members, but see below.
+
+For cross-compatibility and full control two decorators are provided:
+
+- @member --> forces item to become a member
+- @nonmember --> excludes item from becoming a member
+
+So to have an Enum that behaves the same in Python 2 and 3, use the
+decorators (and other compatibility shims):
+
+ class Color(Enum):
+
+ _order_ = 'red green blue'
+
+ red = 1
+ green = 2
+ blue = 3
+
+ @nonmember
+ class Shades(Enum):
+
+ _order_ = 'light medium dark'
+
+ light = 1
+ medium = 2
+ dark = 3
+
+
+2.1.4
+=====
+
+EnumMeta:
+- change __member_new__ to __new_member__ (as the stdlib enum does)
+- assign member name to enum() instances (an Enum helper for defining members)
+- handle empty iterables when using functional API
+- make auto() work with previous enum members
+- keep searching mixins until base class is found
+
+Enum:
+- fix bug in Flag checks (ensure it is a Flag before checking the name)
+- add multiple mixin support
+- do not allow blank names (functional API)
+- raise TypeError if _missing_* returns wrong type
+- fix __format__ to honor custom __str__
+
+extend_enum:
+- support stdlib Enums
+- use _generate_next_value_ if value not provided
+
+general:
+- standardize exception formatting
+- use getfullargspec() in Python 3 (avoids deprecation warnings)
+
+
+2.1.2
+=====
+
+when order is callable, save it for subclass use
+
+
+2.1.1
+=====
+
+correctly raise TypeError for non-Enum containment checks
+support combining names with | for Flag key access
+support _order_ being a callable
+
+
+2.1.0
+=====
+
+support Flags being combined with other data types:
+- add _create_pseudo_member_values_
+- add default __new__ and temporary _init_
+
+
+2.0.10
+======
+
+ensure _ignore_ is set when _settings_ specified in body which includes
+AutoValue
+
+make Flag members iterable
+
+
+2.0.9
+=====
+
+fix missing comma in __all__
+fix extend_enum with custom __new__ methods
+fix MultiValue with AutoNumber without _init_
+
+
+2.0.8
+=====
+
+extend_enum now handles aliases and multivalues correctly
+
+
+2.0.7
+=====
+
+support mixin types with extend_enum
+init and AutoNumber can now work together
+add test for new Enum using EnumMeta
+add tests for variations of multivalue and init
+prevent deletion of NamedConstant.constant
+
+
+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
+=====
+
+auto() now works with other data types
+AutoNumber supports legacy Enums (fixed regression)
+
+
+2.0.0
+=====
+
+Flag and IntFlag added.
+
+
+1.4.7
+=====
+
+fix %-interpolation bug
+defined SqlLiteEnum only if sqlite exists
+support pyflakes
+
+
+1.4.6
+=====
+
+version numbering error
+
+
+1.4.5
+=====
+
+revert AutoNumberEnum to custom __new__ instead of AutoNumber
+use _ignore_ to shield against AutoNumber magic
+inherit start and init settings from base Enums
+
+
+1.4.4
+=====
+
+enabled export as a decorator
+enabled _order_ to replace __order__
+enabled python2 support for settings, init, and start
+
+
+1.4.3
+=====
+
+support _ignore_ for dynamically creating class bodies
+
+
+1.4.2
+=====
+
+MultiValue, NoAlias, Unique, and init now work with Python 2
+
+
+1.4.1
+=====
+
+Py3: added Enum creation flags: Auto, MultiValue, NoAlias, Unique
+
+fixed extend_enum to honor Enum flags
+
+
+1.4.0
+=====
+
+When possible aenum inherits from Python's own enum.
+
+Breaking change: enum members now default to evaluating as True to maintain
+compatibility with the stdlib.
+
+Add your own __bool__ (__nonzero__ in Python 2) if need this behavior:
+
+ def __bool__(self):
+ return bool(self.value)
+ __nonzero__ = __bool__
+
diff --git a/venv/Lib/site-packages/aenum/LICENSE b/venv/Lib/site-packages/aenum/LICENSE
new file mode 100644
index 00000000..b20361ce
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) 2015, 2016, 2017, 2018 Ethan Furman.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+ Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ Neither the name Ethan Furman nor the names of any
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/venv/Lib/site-packages/aenum/__init__.py b/venv/Lib/site-packages/aenum/__init__.py
new file mode 100644
index 00000000..26c504cc
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/__init__.py
@@ -0,0 +1,4111 @@
+"""Python Advanced Enumerations & NameTuples"""
+from __future__ import print_function
+
+# imports
+import sys as _sys
+pyver = _sys.version_info[:2]
+PY2 = pyver < (3, )
+PY3 = pyver >= (3, )
+PY2_6 = (2, 6)
+PY3_3 = (3, 3)
+PY3_4 = (3, 4)
+PY3_5 = (3, 5)
+PY3_6 = (3, 6)
+PY3_11 = (3, 11)
+
+import re
+
+_bltin_property = property
+_bltin_bin = bin
+
+try:
+ from collections import OrderedDict
+except ImportError:
+ OrderedDict = dict
+from collections import defaultdict
+try:
+ import sqlite3
+except ImportError:
+ sqlite3 = None
+
+try:
+ RecursionError
+except NameError:
+ # python3.4
+ RecursionError = RuntimeError
+
+from operator import or_ as _or_, and_ as _and_, xor as _xor_, inv as _inv_
+from operator import abs as _abs_, add as _add_, floordiv as _floordiv_
+from operator import lshift as _lshift_, rshift as _rshift_, mod as _mod_
+from operator import mul as _mul_, neg as _neg_, pos as _pos_, pow as _pow_
+from operator import truediv as _truediv_, sub as _sub_
+
+if PY2:
+ from ._py2 import *
+if PY3:
+ from ._py3 import *
+
+obj_type = type
+
+__all__ = [
+ 'NamedConstant', 'constant', 'skip', 'nonmember', 'member', 'no_arg',
+ 'Enum', 'IntEnum', 'AutoNumberEnum', 'OrderedEnum', 'UniqueEnum',
+ 'StrEnum', 'UpperStrEnum', 'LowerStrEnum',
+ 'Flag', 'IntFlag',
+ 'AddValue', 'MagicValue', 'MultiValue', 'NoAlias', 'Unique',
+ 'AddValueEnum', 'MultiValueEnum', 'NoAliasEnum',
+ 'enum', 'extend_enum', 'unique', 'property',
+ 'NamedTuple', 'SqliteEnum',
+ 'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
+ 'add_stdlib_integration', 'remove_stdlib_integration'
+ ]
+
+if sqlite3 is None:
+ __all__.remove('SqliteEnum')
+
+version = 3, 1, 12, 1
+
+# shims
+try:
+ any
+except NameError:
+ def any(iterable):
+ for element in iterable:
+ if element:
+ return True
+ return False
+
+try:
+ unicode
+ unicode = unicode
+except NameError:
+ # In Python 3 unicode no longer exists (it's just str)
+ unicode = str
+
+try:
+ basestring
+ basestring = bytes, unicode
+except NameError:
+ # In Python 2 basestring is the ancestor of both str and unicode
+ # in Python 3 it's just str, but was missing in 3.1
+ basestring = str,
+
+try:
+ long
+ baseinteger = int, long
+except NameError:
+ baseinteger = int,
+ long = int
+# deprecated
+baseint = baseinteger
+
+try:
+ NoneType
+except NameError:
+ NoneType = type(None)
+
+try:
+ # derive from stdlib enum if possible
+ import enum
+ if hasattr(enum, 'version'):
+ raise ImportError('wrong version')
+ else:
+ from enum import EnumMeta as StdlibEnumMeta, Enum as StdlibEnum, IntEnum as StdlibIntEnum
+ StdlibFlag = StdlibIntFlag = StdlibStrEnum = StdlibReprEnum = None
+except ImportError:
+ StdlibEnumMeta = StdlibEnum = StdlibIntEnum = StdlibIntFlag = StdlibFlag = StdlibStrEnum = None
+
+if StdlibEnum:
+ try:
+ from enum import IntFlag as StdlibIntFlag, Flag as StdlibFlag
+ except ImportError:
+ pass
+ try:
+ from enum import StrEnum as StdlibStrEnum
+ except ImportError:
+ pass
+ try:
+ from enum import ReprEnum as StdlibReprEnum
+ except ImportError:
+ pass
+
+
+
+
+# helpers
+# will be exported later
+MagicValue = AddValue = MultiValue = NoAlias = Unique = None
+
+def _bit_count(num):
+ """
+ return number of set bits
+
+ Counting bits set, Brian Kernighan's way*
+
+ unsigned int v; // count the number of bits set in v
+ unsigned int c; // c accumulates the total bits set in v
+ for (c = 0; v; c++)
+ { v &= v - 1; } //clear the least significant bit set
+
+ This method goes through as many iterations as there are set bits. So if we
+ have a 32-bit word with only the high bit set, then it will only go once
+ through the loop.
+
+ * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988.
+
+ This works because each subtraction "borrows" from the lowest 1-bit. For
+ example:
+
+ loop pass 1 loop pass 2
+ ----------- -----------
+ 101000 100000
+ - 1 - 1
+ = 100111 = 011111
+ & 101000 & 100000
+ = 100000 = 0
+
+ It is an excellent technique for Python, since the size of the integer need
+ not be determined beforehand.
+
+ (from https://wiki.python.org/moin/BitManipulation)
+ """
+ count = 0
+ while num:
+ num &= num - 1
+ count += 1
+ return count
+
+def _is_single_bit(value):
+ """
+ True if only one bit set in value (should be an int)
+ """
+ if value == 0:
+ return False
+ value &= value - 1
+ return value == 0
+
+def _iter_bits_lsb(value):
+ """
+ Return each bit value one at a time.
+
+ >>> list(_iter_bits_lsb(6))
+ [2, 4]
+ """
+
+ while value:
+ bit = value & (~value + 1)
+ yield bit
+ value ^= bit
+
+def bin(value, max_bits=None):
+ """
+ Like built-in bin(), except negative values are represented in
+ twos-compliment, and the leading bit always indicates sign
+ (0=positive, 1=negative).
+
+ >>> bin(10)
+ '0b0 1010'
+ >>> bin(~10) # ~10 is -11
+ '0b1 0101'
+ """
+
+ ceiling = 2 ** (value).bit_length()
+ if value >= 0:
+ s = _bltin_bin(value + ceiling).replace('1', '0', 1)
+ else:
+ s = _bltin_bin(~value ^ (ceiling - 1) + ceiling)
+ sign = s[:3]
+ digits = s[3:]
+ if max_bits is not None:
+ if len(digits) < max_bits:
+ digits = (sign[-1] * max_bits + digits)[-max_bits:]
+ return "%s %s" % (sign, digits)
+
+
+try:
+ from types import DynamicClassAttribute
+ base = DynamicClassAttribute
+except ImportError:
+ base = object
+ DynamicClassAttribute = None
+
+class property(base):
+ """
+ This is a descriptor, used to define attributes that act differently
+ when accessed through an enum member and through an enum class.
+ Instance access is the same as property(), but access to an attribute
+ through the enum class will look in the class' _member_map_.
+ """
+
+ # inherit from DynamicClassAttribute if we can in order to get `inspect`
+ # support
+
+ def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+ self.fget = fget
+ self.fset = fset
+ self.fdel = fdel
+ # next two lines make property act the same as _bltin_property
+ self.__doc__ = doc or fget.__doc__
+ self.overwrite_doc = doc is None
+ # support for abstract methods
+ self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
+ # names, if possible
+
+ def getter(self, fget):
+ fdoc = fget.__doc__ if self.overwrite_doc else None
+ result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
+ result.overwrite_doc = self.__doc__ is None
+ return result
+
+ def setter(self, fset):
+ fdoc = fget.__doc__ if self.overwrite_doc else None
+ result = type(self)(self.fget, fset, self.fdel, self.__doc__)
+ result.overwrite_doc = self.__doc__ is None
+ return result
+
+ def deleter(self, fdel):
+ fdoc = fget.__doc__ if self.overwrite_doc else None
+ result = type(self)(self.fget, self.fset, fdel, self.__doc__)
+ result.overwrite_doc = self.__doc__ is None
+ return result
+
+ def __repr__(self):
+ member = self.ownerclass._member_map_.get(self.name)
+ func = self.fget or self.fset or self.fdel
+ strings = []
+ if member:
+ strings.append('%r' % member)
+ if func:
+ strings.append('function=%s' % func.__name__)
+ return 'property(%s)' % ', '.join(strings)
+
+ def __get__(self, instance, ownerclass=None):
+ if instance is None:
+ try:
+ return ownerclass._member_map_[self.name]
+ except KeyError:
+ raise AttributeError(
+ '%r has no attribute %r' % (ownerclass, self.name)
+ )
+ else:
+ if self.fget is not None:
+ return self.fget(instance)
+ else:
+ if self.fset is not None:
+ raise AttributeError(
+ 'cannot read attribute %r on %r' % (self.name, ownerclass)
+ )
+ else:
+ try:
+ return instance.__dict__[self.name]
+ except KeyError:
+ raise AttributeError(
+ '%r member has no attribute %r' % (ownerclass, self.name)
+ )
+
+ def __set__(self, instance, value):
+ if self.fset is None:
+ if self.fget is not None:
+ raise AttributeError(
+ "cannot set attribute %r on <aenum %r>" % (self.name, self.clsname)
+ )
+ else:
+ instance.__dict__[self.name] = value
+ else:
+ return self.fset(instance, value)
+
+ def __delete__(self, instance):
+ if self.fdel is None:
+ if self.fget or self.fset:
+ raise AttributeError(
+ "cannot delete attribute %r on <aenum %r>" % (self.name, self.clsname)
+ )
+ elif self.name in instance.__dict__:
+ del instance.__dict__[self.name]
+ else:
+ raise AttributeError(
+ "no attribute %r on <aenum %r> member" % (self.name, self.clsname)
+ )
+ else:
+ return self.fdel(instance)
+
+ def __set_name__(self, ownerclass, name):
+ self.name = name
+ self.clsname = ownerclass.__name__
+ self.ownerclass = ownerclass
+
+_RouteClassAttributeToGetattr = property
+if DynamicClassAttribute is None:
+ DynamicClassAttribute = property
+# deprecated
+enum_property = property
+
+class NonMember(object):
+ """
+ Protects item from becaming an Enum member during class creation.
+ """
+ def __init__(self, value):
+ self.value = value
+
+ def __get__(self, instance, ownerclass=None):
+ return self.value
+skip = nonmember = NonMember
+
+class Member(object):
+ """
+ Forces item to became an Enum member during class creation.
+ """
+ def __init__(self, value):
+ self.value = value
+member = Member
+
+class SentinelType(type):
+ def __repr__(cls):
+ return '<%s>' % cls.__name__
+Sentinel = SentinelType('Sentinel', (object, ), {})
+
+def _is_descriptor(obj):
+ """Returns True if obj is a descriptor, False otherwise."""
+ return (
+ hasattr(obj, '__get__') or
+ hasattr(obj, '__set__') or
+ hasattr(obj, '__delete__'))
+
+
+def _is_dunder(name):
+ """Returns True if a __dunder__ name, False otherwise."""
+ return (len(name) > 4 and
+ name[:2] == name[-2:] == '__' and
+ name[2] != '_' and
+ name[-3] != '_')
+
+
+def _is_sunder(name):
+ """Returns True if a _sunder_ name, False otherwise."""
+ return (len(name) > 2 and
+ name[0] == name[-1] == '_' and
+ name[1] != '_' and
+ name[-2] != '_')
+
+def _is_internal_class(cls_name, obj):
+ # only 3.3 and up, always return False in 3.2 and below
+ if pyver < PY3_3:
+ return False
+ else:
+ qualname = getattr(obj, '__qualname__', False)
+ return not _is_descriptor(obj) and qualname and re.search(r"\.?%s\.\w+$" % cls_name, qualname)
+
+def _is_private_name(cls_name, name):
+ pattern = r'^_%s__\w+[^_]_?$' % (cls_name, )
+ return re.search(pattern, name)
+
+def _power_of_two(value):
+ if value < 1:
+ return False
+ return value == 2 ** _high_bit(value)
+
+def bits(num):
+ if num in (0, 1):
+ return str(num)
+ negative = False
+ if num < 0:
+ negative = True
+ num = ~num
+ result = bits(num>>1) + str(num&1)
+ if negative:
+ result = '1' + ''.join(['10'[d=='1'] for d in result])
+ return result
+
+
+def bit_count(num):
+ """
+ return number of set bits
+
+ Counting bits set, Brian Kernighan's way*
+
+ unsigned int v; // count the number of bits set in v
+ unsigned int c; // c accumulates the total bits set in v
+ for (c = 0; v; c++)
+ { v &= v - 1; } //clear the least significant bit set
+
+ This method goes through as many iterations as there are set bits. So if we
+ have a 32-bit word with only the high bit set, then it will only go once
+ through the loop.
+
+ * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988.
+
+ This works because each subtraction "borrows" from the lowest 1-bit. For example:
+
+ loop pass 1 loop pass 2
+ ----------- -----------
+ 101000 100000
+ - 1 - 1
+ = 100111 = 011111
+ & 101000 & 100000
+ = 100000 = 0
+
+ It is an excellent technique for Python, since the size of the integer need not
+ be determined beforehand.
+ """
+ count = 0
+ while(num):
+ num &= num - 1
+ count += 1
+ return(count)
+
+def bit_len(num):
+ length = 0
+ while num:
+ length += 1
+ num >>= 1
+ return length
+
+def is_single_bit(num):
+ """
+ True if only one bit set in num (should be an int)
+ """
+ num &= num - 1
+ return num == 0
+
+def _make_class_unpicklable(obj):
+ """
+ Make the given obj un-picklable.
+
+ obj should be either a dictionary, on an Enum
+ """
+ def _break_on_call_reduce(self, proto):
+ raise TypeError('%r cannot be pickled' % self)
+ if isinstance(obj, dict):
+ obj['__reduce_ex__'] = _break_on_call_reduce
+ obj['__module__'] = '<unknown>'
+ else:
+ setattr(obj, '__reduce_ex__', _break_on_call_reduce)
+ setattr(obj, '__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 = 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
+
+def enumsort(things):
+ """
+ sorts things by value if all same type; otherwise by name
+ """
+ if not things:
+ return things
+ sort_type = type(things[0])
+ if not issubclass(sort_type, tuple):
+ # direct sort or type error
+ if not all((type(v) is sort_type) for v in things[1:]):
+ raise TypeError('cannot sort items of different types')
+ return sorted(things)
+ else:
+ # expecting list of (name, value) tuples
+ sort_type = type(things[0][1])
+ try:
+ if all((type(v[1]) is sort_type) for v in things[1:]):
+ return sorted(things, key=lambda i: i[1])
+ else:
+ raise TypeError('try name sort instead')
+ except TypeError:
+ return sorted(things, key=lambda i: i[0])
+
+def export(collection, namespace=None):
+ """
+ export([collection,] namespace) -> Export members to target namespace.
+
+ If collection is not given, act as a decorator.
+ """
+ if namespace is None:
+ namespace = collection
+ def export_decorator(collection):
+ return export(collection, namespace)
+ return export_decorator
+ elif issubclass(collection, NamedConstant):
+ for n, c in collection.__dict__.items():
+ if isinstance(c, NamedConstant):
+ namespace[n] = c
+ elif issubclass(collection, Enum):
+ data = collection.__members__.items()
+ for n, m in data:
+ namespace[n] = m
+ else:
+ raise TypeError('%r is not a supported collection' % (collection,) )
+ return collection
+
+class _Addendum(object):
+ def __init__(self, dict, doc, ns):
+ # dict is the dict to update with functions
+ # doc is the docstring to put in the dict
+ # ns is the namespace to remove the function names from
+ self.dict = dict
+ self.ns = ns
+ self.added = set()
+ def __call__(self, func):
+ if isinstance(func, (staticmethod, classmethod)):
+ name = func.__func__.__name__
+ elif isinstance(func, (property, _bltin_property)):
+ name = (func.fget or func.fset or func.fdel).__name__
+ else:
+ name = func.__name__
+ self.dict[name] = func
+ self.added.add(name)
+ def resolve(self):
+ ns = self.ns
+ for name in self.added:
+ del ns[name]
+ return self.dict
+
+# Constant / NamedConstant
+
+class constant(object):
+ '''
+ Simple constant descriptor for NamedConstant and Enum use.
+ '''
+ def __init__(self, value, doc=None):
+ self.value = value
+ self.__doc__ = doc
+
+ def __get__(self, *args):
+ return self.value
+
+ 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 PY2:
+ 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)
+
+ def __set_name__(self, ownerclass, name):
+ self.name = name
+ self.clsname = ownerclass.__name__
+
+
+NamedConstant = None
+
+class _NamedConstantDict(dict):
+ """Track constant order and ensure names are not reused.
+
+ NamedConstantMeta will use the names found in self._names as the
+ Constant names.
+ """
+ def __init__(self):
+ super(_NamedConstantDict, self).__init__()
+ self._names = []
+
+ def __setitem__(self, key, value):
+ """Changes anything not dundered or not a constant descriptor.
+
+ If an constant name is used twice, an error is raised; duplicate
+ values are not checked for.
+
+ Single underscore (sunder) names are reserved.
+ """
+ if _is_sunder(key):
+ raise ValueError(
+ '_sunder_ names, such as %r, are reserved for future NamedConstant use'
+ % (key, )
+ )
+ elif _is_dunder(key):
+ pass
+ elif key in self._names:
+ # overwriting an existing constant?
+ raise TypeError('attempt to reuse name: %r' % (key, ))
+ elif isinstance(value, constant) or not _is_descriptor(value):
+ if key in self:
+ # overwriting a descriptor?
+ raise TypeError('%s already defined as: %r' % (key, self[key]))
+ self._names.append(key)
+ super(_NamedConstantDict, self).__setitem__(key, value)
+
+
+class NamedConstantMeta(type):
+ """
+ Block attempts to reassign NamedConstant attributes.
+ """
+
+ @classmethod
+ def __prepare__(metacls, cls, bases, **kwds):
+ return _NamedConstantDict()
+
+ def __new__(metacls, cls, bases, clsdict):
+ if type(clsdict) is dict:
+ original_dict = clsdict
+ clsdict = _NamedConstantDict()
+ for k, v in original_dict.items():
+ clsdict[k] = v
+ newdict = {}
+ constants = {}
+ for name, obj in clsdict.items():
+ if name in clsdict._names:
+ constants[name] = obj
+ continue
+ elif isinstance(obj, nonmember):
+ obj = obj.value
+ newdict[name] = obj
+ newcls = super(NamedConstantMeta, metacls).__new__(metacls, cls, bases, newdict)
+ newcls._named_constant_cache_ = {}
+ newcls._members_ = {}
+ for name, obj in constants.items():
+ new_k = newcls.__new__(newcls, name, obj)
+ newcls._members_[name] = new_k
+ return newcls
+
+ def __bool__(cls):
+ return True
+
+ def __delattr__(cls, attr):
+ cur_obj = cls.__dict__.get(attr)
+ if NamedConstant is not None and isinstance(cur_obj, NamedConstant):
+ raise AttributeError('cannot delete constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_))
+ super(NamedConstantMeta, cls).__delattr__(attr)
+
+ def __iter__(cls):
+ return (k for k in cls._members_.values())
+
+ def __reversed__(cls):
+ return (k for k in reversed(cls._members_.values()))
+
+ def __len__(cls):
+ return len(cls._members_)
+
+ __nonzero__ = __bool__
+
+ def __setattr__(cls, name, value):
+ """Block attempts to reassign NamedConstants.
+ """
+ cur_obj = cls.__dict__.get(name)
+ if NamedConstant is not None and isinstance(cur_obj, NamedConstant):
+ raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_))
+ super(NamedConstantMeta, cls).__setattr__(name, value)
+
+constant_dict = _Addendum(
+ dict=NamedConstantMeta.__prepare__('NamedConstant', (object, )),
+ doc="NamedConstants protection.\n\n Derive from this class to lock NamedConstants.\n\n",
+ ns=globals(),
+ )
+
+@constant_dict
+def __new__(cls, name, value=None, doc=None):
+ if value is None:
+ # lookup, name is value
+ value = name
+ for name, obj in cls.__dict__.items():
+ if isinstance(obj, cls) and obj._value_ == value:
+ return obj
+ else:
+ raise ValueError('%r does not exist in %r' % (value, cls.__name__))
+ cur_obj = cls.__dict__.get(name)
+ if isinstance(cur_obj, NamedConstant):
+ raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_))
+ elif isinstance(value, constant):
+ doc = doc or value.__doc__
+ value = value.value
+ metacls = cls.__class__
+ if isinstance(value, NamedConstant):
+ # constants from other classes are reduced to their actual value
+ value = value._value_
+ actual_type = type(value)
+ value_type = cls._named_constant_cache_.get(actual_type)
+ if value_type is None:
+ value_type = type(cls.__name__, (cls, type(value)), {})
+ cls._named_constant_cache_[type(value)] = value_type
+ obj = actual_type.__new__(value_type, value)
+ obj._name_ = name
+ obj._value_ = value
+ obj.__doc__ = doc
+ cls._members_[name] = obj
+ metacls.__setattr__(cls, name, obj)
+ return obj
+
+@constant_dict
+def __repr__(self):
+ return "<%s.%s: %r>" % (
+ self.__class__.__name__, self._name_, self._value_)
+
+@constant_dict
+def __reduce_ex__(self, proto):
+ return getattr, (self.__class__, self._name_)
+
+NamedConstant = NamedConstantMeta('NamedConstant', (object, ), constant_dict.resolve())
+Constant = NamedConstant
+del constant_dict
+
+# NamedTuple
+
+class _NamedTupleDict(OrderedDict):
+ """Track field order and ensure field names are not reused.
+
+ NamedTupleMeta will use the names found in self._field_names to translate
+ to indices.
+ """
+ def __init__(self, *args, **kwds):
+ self._field_names = []
+ super(_NamedTupleDict, self).__init__(*args, **kwds)
+
+ def __setitem__(self, key, value):
+ """Records anything not dundered or not a descriptor.
+
+ If a field name is used twice, an error is raised.
+
+ Single underscore (sunder) names are reserved.
+ """
+ if _is_sunder(key):
+ if key not in ('_size_', '_order_', '_fields_'):
+ raise ValueError(
+ '_sunder_ names, such as %r, are reserved for future NamedTuple use'
+ % (key, )
+ )
+ elif _is_dunder(key):
+ if key == '__order__':
+ key = '_order_'
+ elif key in self._field_names:
+ # overwriting a field?
+ raise TypeError('attempt to reuse field name: %r' % (key, ))
+ elif not _is_descriptor(value):
+ if key in self:
+ # field overwriting a descriptor?
+ raise TypeError('%s already defined as: %r' % (key, self[key]))
+ self._field_names.append(key)
+ super(_NamedTupleDict, self).__setitem__(key, value)
+
+
+class _TupleAttributeAtIndex(object):
+
+ def __init__(self, name, index, doc, default):
+ self.name = name
+ self.index = index
+ if doc is undefined:
+ doc = None
+ self.__doc__ = doc
+ self.default = default
+
+ def __get__(self, instance, owner):
+ if instance is None:
+ return self
+ if len(instance) <= self.index:
+ raise AttributeError('%s instance has no value for %s' % (instance.__class__.__name__, self.name))
+ return instance[self.index]
+
+ def __repr__(self):
+ return '%s(%d)' % (self.__class__.__name__, self.index)
+
+
+class undefined(object):
+ def __repr__(self):
+ return 'undefined'
+ def __bool__(self):
+ return False
+ __nonzero__ = __bool__
+undefined = undefined()
+
+
+class TupleSize(NamedConstant):
+ fixed = constant('fixed', 'tuple length is static')
+ minimum = constant('minimum', 'tuple must be at least x long (x is calculated during creation')
+ variable = constant('variable', 'tuple length can be anything')
+
+class NamedTupleMeta(type):
+ """Metaclass for NamedTuple"""
+
+ @classmethod
+ def __prepare__(metacls, cls, bases, size=undefined, **kwds):
+ return _NamedTupleDict()
+
+ def __init__(cls, *args , **kwds):
+ super(NamedTupleMeta, cls).__init__(*args)
+
+ def __new__(metacls, cls, bases, clsdict, size=undefined, **kwds):
+ if bases == (object, ):
+ bases = (tuple, object)
+ elif tuple not in bases:
+ if object in bases:
+ index = bases.index(object)
+ bases = bases[:index] + (tuple, ) + bases[index:]
+ else:
+ bases = bases + (tuple, )
+ # include any fields from base classes
+ base_dict = _NamedTupleDict()
+ namedtuple_bases = []
+ for base in bases:
+ if isinstance(base, NamedTupleMeta):
+ namedtuple_bases.append(base)
+ i = 0
+ if namedtuple_bases:
+ for name, index, doc, default in metacls._convert_fields(*namedtuple_bases):
+ base_dict[name] = index, doc, default
+ i = max(i, index)
+ # construct properly ordered dict with normalized indexes
+ for k, v in clsdict.items():
+ base_dict[k] = v
+ original_dict = base_dict
+ if size is not undefined and '_size_' in original_dict:
+ raise TypeError('_size_ cannot be set if "size" is passed in header')
+ add_order = isinstance(clsdict, _NamedTupleDict)
+ clsdict = _NamedTupleDict()
+ clsdict.setdefault('_size_', size or TupleSize.fixed)
+ unnumbered = OrderedDict()
+ numbered = OrderedDict()
+ _order_ = original_dict.pop('_order_', [])
+ if _order_ :
+ _order_ = _order_.replace(',',' ').split()
+ add_order = False
+ # and process this class
+ for k, v in original_dict.items():
+ if k not in original_dict._field_names:
+ clsdict[k] = v
+ else:
+ # TODO:normalize v here
+ if isinstance(v, baseinteger):
+ # assume an offset
+ v = v, undefined, undefined
+ i = v[0] + 1
+ target = numbered
+ elif isinstance(v, basestring):
+ # assume a docstring
+ if add_order:
+ v = i, v, undefined
+ i += 1
+ target = numbered
+ else:
+ v = undefined, v, undefined
+ target = unnumbered
+ elif isinstance(v, tuple) and len(v) in (2, 3) and isinstance(v[0], baseinteger) and isinstance(v[1], (basestring, NoneType)):
+ # assume an offset, a docstring, and (maybe) a default
+ if len(v) == 2:
+ v = v + (undefined, )
+ v = v
+ i = v[0] + 1
+ target = numbered
+ elif isinstance(v, tuple) and len(v) in (1, 2) and isinstance(v[0], (basestring, NoneType)):
+ # assume a docstring, and (maybe) a default
+ if len(v) == 1:
+ v = v + (undefined, )
+ if add_order:
+ v = (i, ) + v
+ i += 1
+ target = numbered
+ else:
+ v = (undefined, ) + v
+ target = unnumbered
+ else:
+ # refuse to guess further
+ raise ValueError('not sure what to do with %s=%r (should be OFFSET [, DOC [, DEFAULT]])' % (k, v))
+ target[k] = v
+ # all index values have been normalized
+ # deal with _order_ (or lack thereof)
+ fields = []
+ aliases = []
+ seen = set()
+ max_len = 0
+ if not _order_:
+ if unnumbered:
+ raise ValueError("_order_ not specified and OFFSETs not declared for %r" % (unnumbered.keys(), ))
+ for name, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])):
+ if index in seen:
+ aliases.append(name)
+ else:
+ fields.append(name)
+ seen.add(index)
+ max_len = max(max_len, index + 1)
+ offsets = numbered
+ else:
+ # check if any unnumbered not in _order_
+ missing = set(unnumbered) - set(_order_)
+ if missing:
+ raise ValueError("unable to order fields: %s (use _order_ or specify OFFSET" % missing)
+ offsets = OrderedDict()
+ # if any unnumbered, number them from their position in _order_
+ i = 0
+ for k in _order_:
+ try:
+ index, doc, default = unnumbered.pop(k, None) or numbered.pop(k)
+ except IndexError:
+ raise ValueError('%s (from _order_) not found in %s' % (k, cls))
+ if index is not undefined:
+ i = index
+ if i in seen:
+ aliases.append(k)
+ else:
+ fields.append(k)
+ seen.add(i)
+ offsets[k] = i, doc, default
+ i += 1
+ max_len = max(max_len, i)
+ # now handle anything in numbered
+ for k, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])):
+ if index in seen:
+ aliases.append(k)
+ else:
+ fields.append(k)
+ seen.add(index)
+ offsets[k] = index, doc, default
+ max_len = max(max_len, index+1)
+
+ # at this point fields and aliases should be ordered lists, offsets should be an
+ # OrdededDict with each value an int, str or None or undefined, default or None or undefined
+ assert len(fields) + len(aliases) == len(offsets), "number of fields + aliases != number of offsets"
+ assert set(fields) & set(offsets) == set(fields), "some fields are not in offsets: %s" % set(fields) & set(offsets)
+ assert set(aliases) & set(offsets) == set(aliases), "some aliases are not in offsets: %s" % set(aliases) & set(offsets)
+ for name, (index, doc, default) in offsets.items():
+ assert isinstance(index, baseinteger), "index for %s is not an int (%s:%r)" % (name, type(index), index)
+ assert isinstance(doc, (basestring, NoneType)) or doc is undefined, "doc is not a str, None, nor undefined (%s:%r)" % (name, type(doc), doc)
+
+ # create descriptors for fields
+ for name, (index, doc, default) in offsets.items():
+ clsdict[name] = _TupleAttributeAtIndex(name, index, doc, default)
+ clsdict['__slots__'] = ()
+
+ # create our new NamedTuple type
+ namedtuple_class = super(NamedTupleMeta, metacls).__new__(metacls, cls, bases, clsdict)
+ namedtuple_class._fields_ = fields
+ namedtuple_class._aliases_ = aliases
+ namedtuple_class._defined_len_ = max_len
+ return namedtuple_class
+
+ @staticmethod
+ def _convert_fields(*namedtuples):
+ "create list of index, doc, default triplets for cls in namedtuples"
+ all_fields = []
+ for cls in namedtuples:
+ base = len(all_fields)
+ for field in cls._fields_:
+ desc = getattr(cls, field)
+ all_fields.append((field, base+desc.index, desc.__doc__, desc.default))
+ return all_fields
+
+ def __add__(cls, other):
+ "A new NamedTuple is created by concatenating the _fields_ and adjusting the descriptors"
+ if not isinstance(other, NamedTupleMeta):
+ return NotImplemented
+ return NamedTupleMeta('%s%s' % (cls.__name__, other.__name__), (cls, other), {})
+
+ def __call__(cls, *args, **kwds):
+ """Creates a new NamedTuple class or an instance of a NamedTuple subclass.
+
+ NamedTuple should have args of (class_name, names, module)
+
+ `names` can be:
+
+ * A string containing member names, separated either with spaces or
+ commas. Values are auto-numbered from 1.
+ * An iterable of member names. Values are auto-numbered from 1.
+ * An iterable of (member name, value) pairs.
+ * A mapping of member name -> value.
+
+ `module`, if set, will be stored in the new class' __module__ attribute;
+
+ Note: if `module` is not set this routine will attempt to discover the
+ calling module by walking the frame stack; if this is unsuccessful
+ the resulting class will not be pickleable.
+
+ subclass should have whatever arguments and/or keywords will be used to create an
+ instance of the subclass
+ """
+ if cls is NamedTuple:
+ original_args = args
+ original_kwds = kwds.copy()
+ # create a new subclass
+ try:
+ if 'class_name' in kwds:
+ class_name = kwds.pop('class_name')
+ else:
+ class_name, args = args[0], args[1:]
+ if 'names' in kwds:
+ names = kwds.pop('names')
+ else:
+ names, args = args[0], args[1:]
+ if 'module' in kwds:
+ module = kwds.pop('module')
+ elif args:
+ module, args = args[0], args[1:]
+ else:
+ module = None
+ if 'type' in kwds:
+ type = kwds.pop('type')
+ elif args:
+ type, args = args[0], args[1:]
+ else:
+ type = None
+
+ except IndexError:
+ raise TypeError('too few arguments to NamedTuple: %s, %s' % (original_args, original_kwds))
+ if args or kwds:
+ raise TypeError('too many arguments to NamedTuple: %s, %s' % (original_args, original_kwds))
+ if PY2:
+ # if class_name is unicode, attempt a conversion to ASCII
+ if isinstance(class_name, unicode):
+ try:
+ class_name = class_name.encode('ascii')
+ except UnicodeEncodeError:
+ raise TypeError('%r is not representable in ASCII' % (class_name, ))
+ # quick exit if names is a NamedTuple
+ if isinstance(names, NamedTupleMeta):
+ names.__name__ = class_name
+ if type is not None and type not in names.__bases__:
+ names.__bases__ = (type, ) + names.__bases__
+ return names
+
+ metacls = cls.__class__
+ bases = (cls, )
+ clsdict = metacls.__prepare__(class_name, bases)
+
+ # special processing needed for names?
+ if isinstance(names, basestring):
+ names = names.replace(',', ' ').split()
+ if isinstance(names, (tuple, list)) and isinstance(names[0], basestring):
+ names = [(e, i) for (i, e) in enumerate(names)]
+ # Here, names is either an iterable of (name, index) or (name, index, doc, default) or a mapping.
+ item = None # in case names is empty
+ for item in names:
+ if isinstance(item, basestring):
+ # mapping
+ field_name, field_index = item, names[item]
+ else:
+ # non-mapping
+ if len(item) == 2:
+ field_name, field_index = item
+ else:
+ field_name, field_index = item[0], item[1:]
+ clsdict[field_name] = field_index
+ if type is not None:
+ if not isinstance(type, tuple):
+ type = (type, )
+ bases = type + bases
+ namedtuple_class = metacls.__new__(metacls, class_name, bases, clsdict)
+
+ # TODO: replace the frame hack if a blessed way to know the calling
+ # module is ever developed
+ if module is None:
+ try:
+ module = _sys._getframe(1).f_globals['__name__']
+ except (AttributeError, ValueError, KeyError):
+ pass
+ if module is None:
+ _make_class_unpicklable(namedtuple_class)
+ else:
+ namedtuple_class.__module__ = module
+
+ return namedtuple_class
+ else:
+ # instantiate a subclass
+ namedtuple_instance = cls.__new__(cls, *args, **kwds)
+ if isinstance(namedtuple_instance, cls):
+ namedtuple_instance.__init__(*args, **kwds)
+ return namedtuple_instance
+
+ @_bltin_property
+ def __fields__(cls):
+ return list(cls._fields_)
+ # collections.namedtuple compatibility
+ _fields = __fields__
+
+ @_bltin_property
+ def __aliases__(cls):
+ return list(cls._aliases_)
+
+ def __repr__(cls):
+ return "<NamedTuple %r>" % (cls.__name__, )
+
+namedtuple_dict = _Addendum(
+ dict=NamedTupleMeta.__prepare__('NamedTuple', (object, )),
+ doc="NamedTuple base class.\n\n Derive from this class to define new NamedTuples.\n\n",
+ ns=globals(),
+ )
+
+@namedtuple_dict
+def __new__(cls, *args, **kwds):
+ if cls._size_ is TupleSize.fixed and len(args) > cls._defined_len_:
+ raise TypeError('%d fields expected, %d received' % (cls._defined_len_, len(args)))
+ unknown = set(kwds) - set(cls._fields_) - set(cls._aliases_)
+ if unknown:
+ raise TypeError('unknown fields: %r' % (unknown, ))
+ final_args = list(args) + [undefined] * (len(cls.__fields__) - len(args))
+ for field, value in kwds.items():
+ index = getattr(cls, field).index
+ if final_args[index] != undefined:
+ raise TypeError('field %s specified more than once' % field)
+ final_args[index] = value
+ missing = []
+ for index, value in enumerate(final_args):
+ if value is undefined:
+ # look for default values
+ name = cls.__fields__[index]
+ default = getattr(cls, name).default
+ if default is undefined:
+ missing.append(name)
+ else:
+ final_args[index] = default
+ if missing:
+ if cls._size_ in (TupleSize.fixed, TupleSize.minimum):
+ raise TypeError('values not provided for field(s): %s' % ', '.join(missing))
+ while final_args and final_args[-1] is undefined:
+ final_args.pop()
+ missing.pop()
+ if cls._size_ is not TupleSize.variable or undefined in final_args:
+ raise TypeError('values not provided for field(s): %s' % ', '.join(missing))
+ return tuple.__new__(cls, tuple(final_args))
+
+@namedtuple_dict
+def __reduce_ex__(self, proto):
+ return self.__class__, tuple(getattr(self, f) for f in self._fields_)
+
+@namedtuple_dict
+def __repr__(self):
+ if len(self) == len(self._fields_):
+ return "%s(%s)" % (
+ self.__class__.__name__, ', '.join(['%s=%r' % (f, o) for f, o in zip(self._fields_, self)])
+ )
+ else:
+ return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(o) for o in self]))
+
+@namedtuple_dict
+def __str__(self):
+ return "%s(%s)" % (
+ self.__class__.__name__, ', '.join(['%r' % (getattr(self, f), ) for f in self._fields_])
+ )
+
+@namedtuple_dict
+@_bltin_property
+def _fields_(self):
+ return list(self.__class__._fields_)
+
+ # compatibility methods with stdlib namedtuple
+@namedtuple_dict
+@_bltin_property
+def __aliases__(self):
+ return list(self.__class__._aliases_)
+
+@namedtuple_dict
+@_bltin_property
+def _fields(self):
+ return list(self.__class__._fields_)
+
+@namedtuple_dict
+@classmethod
+def _make(cls, iterable, new=None, len=None):
+ return cls.__new__(cls, *iterable)
+
+@namedtuple_dict
+def _asdict(self):
+ return OrderedDict(zip(self._fields_, self))
+
+@namedtuple_dict
+def _replace(self, **kwds):
+ current = self._asdict()
+ current.update(kwds)
+ return self.__class__(**current)
+
+NamedTuple = NamedTupleMeta('NamedTuple', (object, ), namedtuple_dict.resolve())
+del namedtuple_dict
+
+
+# Enum
+
+ # _init_ and value and AddValue
+ # -----------------------------
+ # by default, when defining a member everything after the = is "the value", everything is
+ # passed to __new__, everything is passed to __init__
+ #
+ # if _init_ is present then
+ # if `value` is not in _init_, everything is "the value", defaults apply
+ # if `value` is in _init_, only the first thing after the = is the value, and the rest will
+ # be passed to __init__
+ # if fewer values are present for member assignment than _init_ calls for, _generate_next_value_
+ # will be called in an attempt to generate them
+ #
+ # if AddValue is present then
+ # _generate_next_value_ is always called, and any generated values are prepended to provided
+ # values (custom _gnv_s can change that)
+ # default _init_ rules apply
+
+
+ # Constants used in Enum
+
+@export(globals())
+class EnumConstants(NamedConstant):
+ AddValue = constant('addvalue', 'prepends value(s) from _generate_next_value_ to each member')
+ MagicValue = constant('magicvalue', 'calls _generate_next_value_ when no arguments are given')
+ MultiValue = constant('multivalue', 'each member can have several values')
+ NoAlias = constant('noalias', 'duplicate valued members are distinct, not aliased')
+ Unique = constant('unique', 'duplicate valued members are not allowed')
+ def __repr__(self):
+ return self._name_
+
+
+ # Dummy value for Enum as EnumType explicity checks for it, but of course until
+ # EnumType finishes running the first time the Enum class doesn't exist. This
+ # is also why there are checks in EnumType like `if Enum is not None`.
+ #
+ # Ditto for Flag.
+
+Enum = ReprEnum = IntEnum = StrEnum = Flag = IntFlag = EJECT = KEEP = None
+
+class enum(object):
+ """
+ Helper class to track args, kwds.
+ """
+ def __init__(self, *args, **kwds):
+ self._args = args
+ self._kwds = dict(kwds.items())
+ self._hash = hash(args)
+ self.name = None
+
+ @_bltin_property
+ def args(self):
+ return self._args
+
+ @_bltin_property
+ def kwds(self):
+ return self._kwds.copy()
+
+ 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 = []
+ args = ', '.join(['%r' % (a, ) for a in self.args])
+ if args:
+ final.append(args)
+ kwds = ', '.join([('%s=%r') % (k, v) for k, v in enumsort(list(self.kwds.items()))])
+ if kwds:
+ final.append(kwds)
+ return '%s(%s)' % (self.__class__.__name__, ', '.join(final))
+
+_auto_null = SentinelType('no_value', (object, ), {})
+class auto(enum):
+ """
+ Instances are replaced with an appropriate value in Enum class suites.
+ """
+ enum_member = _auto_null
+ _value = _auto_null
+ _operations = []
+
+ def __and__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_and_, (self, other)))
+ return new_auto
+
+ def __rand__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_and_, (other, self)))
+ return new_auto
+
+ def __invert__(self):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_inv_, (self,)))
+ return new_auto
+
+ def __or__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_or_, (self, other)))
+ return new_auto
+
+ def __ror__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_or_, (other, self)))
+ return new_auto
+
+ def __xor__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_xor_, (self, other)))
+ return new_auto
+
+ def __rxor__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_xor_, (other, self)))
+ return new_auto
+
+ def __abs__(self):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_abs_, (self, )))
+ return new_auto
+
+ def __add__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_add_, (self, other)))
+ return new_auto
+
+ def __radd__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_add_, (other, self)))
+ return new_auto
+
+ def __neg__(self):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_neg_, (self, )))
+ return new_auto
+
+ def __pos__(self):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_pos_, (self, )))
+ return new_auto
+
+ if PY2:
+ def __div__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_div_, (self, other)))
+ return new_auto
+
+ def __rdiv__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_div_, (other, self)))
+ return new_auto
+
+ def __floordiv__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_floordiv_, (self, other)))
+ return new_auto
+
+ def __rfloordiv__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_floordiv_, (other, self)))
+ return new_auto
+
+ def __truediv__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_truediv_, (self, other)))
+ return new_auto
+
+ def __rtruediv__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_truediv_, (other, self)))
+ return new_auto
+
+ def __lshift__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_lshift_, (self, other)))
+ return new_auto
+
+ def __rlshift__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_lshift_, (other, self)))
+ return new_auto
+
+ def __rshift__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_rshift_, (self, other)))
+ return new_auto
+
+ def __rrshift__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_rshift_, (other, self)))
+ return new_auto
+
+ def __mod__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_mod_, (self, other)))
+ return new_auto
+
+ def __rmod__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_mod_, (other, self)))
+ return new_auto
+
+ def __mul__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_mul_, (self, other)))
+ return new_auto
+
+ def __rmul__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_mul_, (other, self)))
+ return new_auto
+
+ def __pow__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_pow_, (self, other)))
+ return new_auto
+
+ def __rpow__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_pow_, (other, self)))
+ return new_auto
+
+ def __sub__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_sub_, (self, other)))
+ return new_auto
+
+ def __rsub__(self, other):
+ new_auto = self.__class__()
+ new_auto._operations = self._operations[:]
+ new_auto._operations.append((_sub_, (other, self)))
+ return new_auto
+
+ def __repr__(self):
+ if self._operations:
+ return 'auto(...)'
+ else:
+ return 'auto(%r, *%r, **%r)' % (self._value, self._args, self._kwds)
+
+ @_bltin_property
+ def value(self):
+ if self._value is not _auto_null and self._operations:
+ raise TypeError('auto() object out of sync')
+ elif self._value is _auto_null and not self._operations:
+ return self._value
+ elif self._value is not _auto_null:
+ return self._value
+ else:
+ return self._resolve()
+
+ @value.setter
+ def value(self, value):
+ if self._operations:
+ value = self._resolve(value)
+ self._value = value
+
+ def _resolve(self, base_value=None):
+ cls = self.__class__
+ for op, params in self._operations:
+ values = []
+ for param in params:
+ if isinstance(param, cls):
+ if param.value is _auto_null:
+ if base_value is None:
+ return _auto_null
+ else:
+ values.append(base_value)
+ else:
+ values.append(param.value)
+ else:
+ values.append(param)
+ value = op(*values)
+ self._operations[:] = []
+ self._value = value
+ return value
+
+
+class _EnumArgSpec(NamedTuple):
+ args = 0, 'all args except *args and **kwds'
+ varargs = 1, 'the name of the *args variable'
+ keywords = 2, 'the name of the **kwds variable'
+ defaults = 3, 'any default values'
+ required = 4, 'number of required values (no default available)'
+
+ def __new__(cls, _new_func):
+ argspec = getargspec(_new_func)
+ args, varargs, keywords, defaults = argspec
+ if defaults:
+ reqs = args[1:-len(defaults)]
+ else:
+ reqs = args[1:]
+ return tuple.__new__(_EnumArgSpec, (args, varargs, keywords, defaults, reqs))
+
+
+class _proto_member:
+ """
+ intermediate step for enum members between class execution and final creation
+ """
+
+ def __init__(self, value):
+ self.value = value
+
+ def __set_name__(self, enum_class, member_name):
+ """
+ convert each quasi-member into an instance of the new enum class
+ """
+ # first step: remove ourself from enum_class
+ delattr(enum_class, member_name)
+ # second step: create member based on enum_class
+ value = self.value
+ kwds = {}
+ args = ()
+ init_args = ()
+ extra_mv_args = ()
+ multivalue = None
+ if isinstance(value, tuple) and value and isinstance(value[0], auto):
+ multivalue = value
+ value = value[0]
+ if isinstance(value, auto) and value.value is _auto_null:
+ args = value.args
+ kwds = value.kwds
+ elif isinstance(value, auto):
+ kwds = value.kwds
+ args = (value.value, ) + value.args
+ value = value.value
+ elif isinstance(value, enum):
+ args = value.args
+ kwds = value.kwds
+ elif isinstance(value, Member):
+ value = value.value
+ args = (value, )
+ elif not isinstance(value, tuple):
+ args = (value, )
+ else:
+ args = value
+ if multivalue is not None:
+ value = (value, ) + multivalue[1:]
+ kwds = {}
+ args = value
+ del multivalue
+ # possibilities
+ #
+ # - no init, multivalue -> __new__[0], __init__(*[:]), extra=[1:]
+ # - init w/o value, multivalue -> __new__[0], __init__(*[:]), extra=[1:]
+ #
+ # - init w/value, multivalue -> __new__[0], __init__(*[1:]), extra=[1:]
+ #
+ # - init w/value, no multivalue -> __new__[0], __init__(*[1:]), extra=[]
+ #
+ # - init w/o value, no multivalue -> __new__[:], __init__(*[:]), extra=[]
+ # - no init, no multivalue -> __new__[:], __init__(*[:]), extra=[]
+ if enum_class._multivalue_ or 'value' in enum_class._creating_init_:
+ if enum_class._multivalue_:
+ # when multivalue is True, creating_init can be anything
+ mv_arg = args[0]
+ extra_mv_args = args[1:]
+ if 'value' in enum_class._creating_init_:
+ init_args = args[1:]
+ else:
+ init_args = args
+ args = args[0:1]
+ value = args[0]
+ else:
+ # 'value' is definitely in creating_init
+ if enum_class._auto_init_ and enum_class._new_args_:
+ # we have a custom __new__ and an auto __init__
+ # divvy up according to number of params in each
+ init_args = args[-len(enum_class._creating_init_)+1:]
+ if not enum_class._auto_args_:
+ args = args[:len(enum_class._new_args_.args)]
+ value = args[0]
+ elif enum_class._auto_init_:
+ # don't pass in value
+ init_args = args[1:]
+ args = args[0:1]
+ value = args[0]
+ elif enum_class._new_args_:
+ # do not modify args
+ value = args[0]
+ else:
+ # keep all args for user-defined __init__
+ # keep value as-is
+ init_args = args
+ else:
+ # either no creating_init, or it doesn't have 'value'
+ init_args = args
+ if enum_class._member_type_ is tuple: # special case for tuple enums
+ args = (args, ) # wrap it one more time
+ if not enum_class._use_args_:
+ enum_member = enum_class._new_member_(enum_class)
+ if not hasattr(enum_member, '_value_'):
+ enum_member._value_ = value
+ else:
+ enum_member = enum_class._new_member_(enum_class, *args, **kwds)
+ if not hasattr(enum_member, '_value_'):
+ if enum_class._member_type_ is object:
+ enum_member._value_ = value
+ else:
+ try:
+ enum_member._value_ = enum_class._member_type_(*args, **kwds)
+ except Exception:
+ te = TypeError('_value_ not set in __new__, unable to create it')
+ te.__cause__ = None
+ raise te
+ value = enum_member._value_
+ enum_member._name_ = member_name
+ enum_member.__objclass__ = enum_class
+ enum_member.__init__(*init_args, **kwds)
+ enum_member._sort_order_ = len(enum_class._member_names_)
+ # If another member with the same value was already defined, the
+ # new member becomes an alias to the existing one.
+ if enum_class._noalias_:
+ # unless NoAlias was specified
+ enum_class._member_names_.append(member_name)
+ else:
+ nonunique = defaultdict(list)
+ try:
+ try:
+ # try to do a fast lookup to avoid the quadratic loop
+ enum_member = enum_class._value2member_map_[value]
+ if enum_class._unique_:
+ nonunique[enum_member.name].append(member_name)
+ except TypeError:
+ # unhashable members are stored elsewhere
+ for unhashable_value, canonical_member in enum_class._value2member_seq_:
+ name = canonical_member.name
+ if unhashable_value == enum_member._value_:
+ if enum_class._unique_:
+ nonunique[name].append(member_name)
+ enum_member = canonical_member
+ break
+ else:
+ raise KeyError
+ except KeyError:
+ # this could still be an alias if the value is multi-bit and the
+ # class is a flag class
+ if (
+ Flag is None
+ or not issubclass(enum_class, Flag)
+ ):
+ # no other instances found, record this member in _member_names_
+ enum_class._member_names_.append(member_name)
+ elif (
+ Flag is not None
+ and issubclass(enum_class, Flag)
+ and _is_single_bit(value)
+ ):
+ # no other instances found, record this member in _member_names_
+ enum_class._member_names_.append(member_name)
+ if nonunique:
+ # duplicates not allowed if Unique specified
+ message = []
+ for name, aliases in nonunique.items():
+ bad_aliases = ','.join(aliases)
+ message.append('%s --> %s [%r]' % (name, bad_aliases, enum_class[name].value))
+ raise ValueError(
+ '%s: duplicate names found: %s' %
+ (enum_class.__name__, '; '.join(message))
+ )
+ # if self.value is an `auto()`, replace the value attribute with the new enum member
+ if isinstance(self.value, auto):
+ self.value.enum_member = enum_member
+ # get redirect in place before adding to _member_map_
+ # but check for other instances in parent classes first
+ need_override = False
+ descriptor = None
+ descriptor_property = None
+ for base in enum_class.__mro__[1:]:
+ descriptor = base.__dict__.get(member_name)
+ if descriptor is not None:
+ if isinstance(descriptor, (property, DynamicClassAttribute)):
+ break
+ else:
+ need_override = True
+ if isinstance(descriptor, _bltin_property) and descriptor_property is None:
+ descriptor_property = descriptor
+ # keep looking for an enum.property
+ descriptor = descriptor or descriptor_property
+ if descriptor and not need_override:
+ # previous enum.property found, no further action needed
+ pass
+ else:
+ redirect = property()
+ redirect.__set_name__(enum_class, member_name)
+ if descriptor and need_override:
+ # previous enum.property found, but some other inherited
+ # attribute is in the way; copy fget, fset, fdel to this one
+ redirect.fget = descriptor.fget
+ redirect.fset = descriptor.fset
+ redirect.fdel = descriptor.fdel
+ setattr(enum_class, member_name, redirect)
+ # now add to _member_map_ (even aliases)
+ enum_class._member_map_[member_name] = enum_member
+ #
+ # process (possible) MultiValues
+ values = (value, ) + extra_mv_args
+ if enum_class._multivalue_ and mv_arg not in values:
+ values += (mv_arg, )
+ enum_member._values_ = values
+ for value in values:
+ # first check if value has already been used
+ if enum_class._multivalue_ and (
+ value in enum_class._value2member_map_
+ or any(v == value for (v, m) in enum_class._value2member_seq_)
+ ):
+ raise ValueError('%r has already been used' % (value, ))
+ try:
+ # This may fail if value is not hashable. We can't add the value
+ # to the map, and by-value lookups for this value will be
+ # linear.
+ if enum_class._noalias_:
+ raise TypeError('cannot use dict to store value')
+ enum_class._value2member_map_[value] = enum_member
+ except TypeError:
+ enum_class._value2member_seq_ += ((value, enum_member), )
+
+
+class _EnumDict(dict):
+ """Track enum member order and ensure member names are not reused.
+
+ EnumType will use the names found in self._member_names as the
+ enumeration member names.
+ """
+ def __init__(self, cls_name, settings, start, constructor_init, constructor_start, constructor_boundary):
+ super(_EnumDict, self).__init__()
+ self._cls_name = cls_name
+ self._constructor_init = constructor_init
+ self._constructor_start = constructor_start
+ self._constructor_boundary = constructor_boundary
+ self._generate_next_value = None
+ self._member_names = []
+ self._member_names_set = set()
+ self._settings = settings
+ self._addvalue = addvalue = AddValue in settings
+ self._magicvalue = MagicValue in settings
+ self._multivalue = MultiValue in settings
+ if self._addvalue and self._magicvalue:
+ raise TypeError('%r: AddValue and MagicValue are mutually exclusive' % cls_name)
+ if self._multivalue and self._magicvalue:
+ raise TypeError('%r: MultiValue and MagicValue are mutually exclusive' % cls_name)
+ self._start = start
+ self._addvalue_value = start
+ self._new_args = ()
+ self._auto_args = False
+ # when the magic turns off
+ self._locked = MagicValue not in settings
+ # if init fields are specified
+ self._init = []
+ # list of temporary names
+ self._ignore = []
+ if self._magicvalue:
+ self._ignore = ['property', 'staticmethod', 'classmethod']
+ self._ignore_init_done = False
+ # if _sunder_ values can be changed via the class body
+ self._allow_init = True
+ self._last_values = []
+
+ def __getitem__(self, key):
+ if key == self._cls_name and self._cls_name not in self:
+ return enum
+ elif (
+ self._locked
+ or key in self
+ or key in self._ignore
+ or _is_sunder(key)
+ or _is_dunder(key)
+ ):
+ return super(_EnumDict, self).__getitem__(key)
+ elif self._magicvalue:
+ value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:])
+ self.__setitem__(key, value)
+ return value
+ else:
+ raise Exception('Magic is not set -- why am I here?')
+
+ def __setitem__(self, key, value):
+ """Changes anything not sundured, dundered, nor a descriptor.
+
+ If an enum member name is used twice, an error is raised; duplicate
+ values are not checked for.
+
+ Single underscore (sunder) names are reserved.
+ """
+ # Flag classes that have MagicValue and __new__ will get a generated _gnv_
+ if _is_internal_class(self._cls_name, value):
+ pass
+ elif _is_private_name(self._cls_name, key):
+ pass
+ elif _is_sunder(key):
+ if key not in (
+ '_init_', '_settings_', '_order_', '_ignore_', '_start_',
+ '_create_pseudo_member_', '_create_pseudo_member_values_',
+ '_generate_next_value_', '_boundary_', '_numeric_repr_',
+ '_missing_', '_missing_value_', '_missing_name_',
+ '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
+ ):
+ raise ValueError('%r: _sunder_ names, such as %r, are reserved for future Enum use'
+ % (self._cls_name, key)
+ )
+ 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('%r: cannot set %r after init phase' % (self._cls_name, key))
+ elif key == '_ignore_':
+ if self._ignore_init_done:
+ raise TypeError('%r: ignore can only be specified once' % self._cls_name)
+ if isinstance(value, basestring):
+ value = value.split()
+ else:
+ value = list(value)
+ self._ignore = value
+ already = set(value) & self._member_names_set
+ if already:
+ raise ValueError('%r: _ignore_ cannot specify already set names %s' % (
+ self._cls_name,
+ ', '.join(repr(a) for a in already)
+ ))
+ self._ignore_init_done = True
+ elif key == '_boundary_':
+ if self._constructor_boundary:
+ raise TypeError('%r: boundary specified in constructor and class body' % self._cls_name)
+ elif key == '_start_':
+ if self._constructor_start:
+ raise TypeError('%r: start specified in constructor and class body' % self._cls_name)
+ self._start = value
+ elif key == '_settings_':
+ if not isinstance(value, (set, tuple)):
+ value = (value, )
+ if not isinstance(value, set):
+ value = set(value)
+ self._settings |= value
+ if NoAlias in value and Unique in value:
+ raise TypeError('%r: NoAlias and Unique are mutually exclusive' % self._cls_name)
+ elif MultiValue in value and NoAlias in value:
+ raise TypeError('cannot specify both MultiValue and NoAlias' % self._cls_name)
+ allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue'])
+ for arg in self._settings:
+ if arg not in allowed_settings:
+ raise TypeError('%r: unknown qualifier %r (from %r)' % (self._cls_name, arg, value))
+ allowed_settings[arg] = True
+ self._multivalue = allowed_settings['multivalue']
+ self._addvalue = allowed_settings['addvalue']
+ self._magicvalue = allowed_settings['magicvalue']
+ self._locked = not self._magicvalue
+ if self._magicvalue and not self._ignore_init_done:
+ self._ignore = ['property', 'classmethod', 'staticmethod']
+ if self._addvalue and self._init and 'value' not in self._init:
+ self._init.insert(0, 'value')
+ value = tuple(self._settings)
+ elif key == '_init_':
+ if self._constructor_init:
+ raise TypeError('%r: init specified in constructor and in class body' % self._cls_name)
+ _init_ = value
+ if isinstance(_init_, basestring):
+ _init_ = _init_.replace(',',' ').split()
+ if self._addvalue and 'value' not in self._init:
+ self._init.insert(0, 'value')
+ if self._magicvalue:
+ raise TypeError("%r: _init_ and MagicValue are mutually exclusive" % self._cls_name)
+ self._init = _init_
+ value = _init_
+ elif key == '_generate_next_value_':
+ gnv = value
+ if value is not None:
+ if isinstance(value, staticmethod):
+ gnv = value.__func__
+ elif isinstance(value, classmethod):
+ raise TypeError('%r: _generate_next_value must be a staticmethod, not a classmethod' % self._cls_name)
+ else:
+ gnv = value
+ value = staticmethod(value)
+ self._auto_args = _check_auto_args(value)
+ setattr(self, '_generate_next_value', gnv)
+ elif _is_dunder(key):
+ if key == '__order__':
+ key = '_order_'
+ if not self._allow_init:
+ # _order_ is used during creation, must be specified first
+ raise ValueError('%r: cannot set %r after init phase' % (self._cls_name, key))
+ elif key == '__new__': # and self._new_to_init:
+ if isinstance(value, staticmethod):
+ value = value.__func__
+ self._new_args = _EnumArgSpec(value)
+ elif key == '__init_subclass__':
+ if not isinstance(value, classmethod):
+ value = classmethod(value)
+ if _is_descriptor(value):
+ self._locked = True
+ elif key in self._member_names_set:
+ # descriptor overwriting an enum?
+ raise TypeError('%r: attempt to reuse name: %r' % (self._cls_name, key))
+ elif key in self._ignore:
+ pass
+ elif not _is_descriptor(value):
+ self._allow_init = False
+ if key in self:
+ # enum overwriting a descriptor?
+ raise TypeError('%r: %s already defined as %r' % (self._cls_name, key, self[key]))
+ if type(value) is enum:
+ value.name = key
+ if self._addvalue:
+ raise TypeError('%r: enum() and AddValue are incompatible' % self._cls_name)
+ elif self._addvalue and not self._multivalue:
+ # generate a value
+ value = self._gnv(key, value)
+ elif self._multivalue:
+ # make sure it's a tuple
+ if not isinstance(value, tuple):
+ value = (value, )
+ if isinstance(value[0], auto):
+ value = (self._convert_auto(key, value[0]), ) + value[1:]
+ if self._addvalue:
+ value = self._gnv(key, value)
+ elif isinstance(value, auto):
+ value = self._convert_auto(key, value)
+ elif isinstance(value, tuple) and value and isinstance(value[0], auto):
+ value = (self._convert_auto(key, value[0]), ) + value[1:]
+ elif not isinstance(value, auto):
+ # call generate maybe if
+ # - init is specified; or
+ # - __new__ is specified;
+ # and either of them call for more values than are present
+ new_args = () or self._new_args and self._new_args.required
+ target_len = len(self._init or new_args)
+ if isinstance(value, tuple):
+ source_len = len(value)
+ else:
+ source_len = 1
+ multi_args = len(self._init) > 1 or new_args
+ if source_len < target_len :
+ value = self._gnv(key, value)
+ else:
+ pass
+ if self._init:
+ if isinstance(value, auto):
+ test_value = value.args
+ elif not isinstance(value, tuple):
+ test_value = (value, )
+ else:
+ test_value = value
+ if len(self._init) != len(test_value):
+ raise TypeError(
+ '%s.%s: number of fields provided do not match init [%r != %r]'
+ % (self._cls_name, key, self._init, test_value)
+ )
+ self._member_names.append(key)
+ self._member_names_set.add(key)
+ else:
+ # not a new member, turn off the autoassign magic
+ self._locked = True
+ self._allow_init = False
+ if not (_is_sunder(key) or _is_dunder(key) or _is_private_name(self._cls_name, key) or _is_descriptor(value)):
+ if isinstance(value, auto):
+ self._last_values.append(value.value)
+ elif isinstance(value, tuple) and value and isinstance(value[0], auto):
+ self._last_values.append(value[0].value)
+ elif isinstance(value, tuple):
+ if value:
+ self._last_values.append(value[0])
+ else:
+ self._last_values.append(value)
+ super(_EnumDict, self).__setitem__(key, value)
+
+ def _convert_auto(self, key, value):
+ # if auto.args or auto.kwds, compare to _init_ and __new__ -- if lacking, call gnv
+ # if not auto.args|kwds but auto.value is _auto_null -- call gnv
+ if value.args or value.kwds or value.value is _auto_null:
+ if value.args or value.kwds:
+ values = value.args
+ else:
+ values = ()
+ new_args = () or self._new_args and self._new_args.required
+ target_len = len(self._init or new_args) or 1
+ if isinstance(values, tuple):
+ source_len = len(values)
+ else:
+ source_len = 1
+ multi_args = len(self._init) > 1 or new_args
+ if source_len < target_len :
+ values = self._gnv(key, values)
+ if value.args:
+ value._args = values
+ else:
+ value.value = values
+ return value
+
+ def _gnv(self, key, value):
+ # generate a value
+ if self._auto_args:
+ if not isinstance(value, tuple):
+ value = (value, )
+ value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:], *value)
+ else:
+ value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:])
+ if isinstance(value, tuple) and len(value) == 1:
+ value = value[0]
+ return value
+
+
+no_arg = SentinelType('no_arg', (object, ), {})
+class EnumType(type):
+ """Metaclass for Enum"""
+
+ @classmethod
+ def __prepare__(metacls, cls, bases, init=None, start=None, settings=(), boundary=None, **kwds):
+ metacls._check_for_existing_members_(cls, bases)
+ if Flag is None and cls == 'Flag':
+ initial_flag = True
+ else:
+ initial_flag = False
+ # settings are a combination of current and all past settings
+ constructor_init = init is not None
+ constructor_start = start is not None
+ constructor_boundary = boundary is not None
+ if not isinstance(settings, tuple):
+ settings = settings,
+ settings = set(settings)
+ generate = None
+ order = None
+ # inherit previous flags
+ member_type, first_enum = metacls._get_mixins_(cls, bases)
+ if first_enum is not None:
+ generate = getattr(first_enum, '_generate_next_value_', None)
+ generate = getattr(generate, 'im_func', generate)
+ settings |= metacls._get_settings_(bases)
+ init = init or first_enum._auto_init_[:]
+ order = first_enum._order_function_
+ if start is None:
+ start = first_enum._start_
+ else:
+ # first time through -- creating Enum itself
+ start = 1
+ # check for custom settings
+ if AddValue in settings and init and 'value' not in init:
+ if isinstance(init, list):
+ init.insert(0, 'value')
+ else:
+ init = 'value ' + init
+ if NoAlias in settings and Unique in settings:
+ raise TypeError('%r: NoAlias and Unique are mutually exclusive' % cls)
+ if MultiValue in settings and NoAlias in settings:
+ raise TypeError('%r: MultiValue and NoAlias are mutually exclusive' % cls)
+ allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue'])
+ for arg in settings:
+ if arg not in allowed_settings:
+ raise TypeError('%r: unknown qualifier %r' % (cls, arg))
+ enum_dict = _EnumDict(cls_name=cls, settings=settings, start=start, constructor_init=constructor_init, constructor_start=constructor_start, constructor_boundary=constructor_boundary)
+ enum_dict._member_type = member_type
+ enum_dict._base_type = ('enum', 'flag')[
+ Flag is None and cls == 'Flag'
+ or
+ Flag is not None and any(issubclass(b, Flag) for b in bases)
+ ]
+ if Flag is not None and any(b is Flag for b in bases) and member_type not in (baseinteger + (object, )):
+ if Flag in bases:
+ # when a non-int data type is mixed in with Flag, we end up
+ # needing two values for two `__new__`s:
+ # - the integer value for the Flag itself; and
+ # - the mix-in value for the mix-in
+ #
+ # we provide a default `_generate_next_value_` to supply the int
+ # argument, and a default `__new__` to keep the two straight
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (2 ** count, ) + args
+ enum_dict['_generate_next_value_'] = staticmethod(_generate_next_value_)
+ def __new__(cls, flag_value, type_value):
+ obj = member_type.__new__(cls, type_value)
+ obj._value_ = flag_value
+ return obj
+ enum_dict['__new__'] = __new__
+ else:
+ try:
+ enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__)
+ except TypeError:
+ pass
+ elif not initial_flag:
+ if hasattr(first_enum, '__new_member__'):
+ enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__)
+ if generate:
+ enum_dict['_generate_next_value_'] = generate
+ enum_dict._inherited_gnv = True
+ if init is not None:
+ if isinstance(init, basestring):
+ init = init.replace(',',' ').split()
+ enum_dict._init = init
+ elif hasattr(first_enum, '__new_member__'):
+ enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__)
+ if order is not None:
+ enum_dict['_order_'] = staticmethod(order)
+ return enum_dict
+
+ def __init__(cls, *args , **kwds):
+ pass
+
+ def __new__(metacls, cls, bases, clsdict, init=None, start=None, settings=(), boundary=None, **kwds):
+ # handle py2 case first
+ if type(clsdict) is not _EnumDict:
+ # py2 and/or functional API gyrations
+ init = clsdict.pop('_init_', None)
+ start = clsdict.pop('_start_', None)
+ settings = clsdict.pop('_settings_', ())
+ _order_ = clsdict.pop('_order_', clsdict.pop('__order__', None))
+ _ignore_ = clsdict.pop('_ignore_', None)
+ _create_pseudo_member_ = clsdict.pop('_create_pseudo_member_', None)
+ _create_pseudo_member_values_ = clsdict.pop('_create_pseudo_member_values_', 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)
+ _boundary_ = clsdict.pop('_boundary_', None)
+ _iter_member_ = clsdict.pop('_iter_member_', None)
+ _iter_member_by_value_ = clsdict.pop('_iter_member_by_value_', None)
+ _iter_member_by_def_ = clsdict.pop('_iter_member_by_def_', None)
+ __new__ = clsdict.pop('__new__', None)
+ __new__ = getattr(__new__, 'im_func', __new__)
+ __new__ = getattr(__new__, '__func__', __new__)
+ enum_members = dict([
+ (k, v) for (k, v) in clsdict.items()
+ if not (_is_sunder(k) or _is_dunder(k) or _is_private_name(cls, k) or _is_descriptor(v))
+ ])
+ original_dict = clsdict
+ clsdict = metacls.__prepare__(cls, bases, init=init, start=start)
+ if settings:
+ clsdict['_settings_'] = settings
+ init = init or clsdict._init
+ if _order_ is None:
+ _order_ = clsdict.get('_order_')
+ if _order_ is not None:
+ _order_ = _order_.__get__(cls)
+ if isinstance(original_dict, OrderedDict):
+ calced_order = original_dict
+ elif _order_ is None:
+ calced_order = [name for (name, value) in enumsort(list(enum_members.items()))]
+ elif isinstance(_order_, basestring):
+ calced_order = _order_ = _order_.replace(',', ' ').split()
+ elif callable(_order_):
+ if init:
+ if not isinstance(init, basestring):
+ init = ' '.join(init)
+ member = NamedTuple('member', init and 'name ' + init or ['name', 'value'])
+ calced_order = []
+ for name, value in enum_members.items():
+ if init:
+ if not isinstance(value, tuple):
+ value = (value, )
+ name_value = (name, ) + value
+ else:
+ name_value = tuple((name, value))
+ if member._defined_len_ != len(name_value):
+ raise TypeError('%d values expected (%s), %d received (%s)' % (
+ member._defined_len_,
+ ', '.join(member._fields_),
+ len(name_value),
+ ', '.join([repr(v) for v in name_value]),
+ ))
+ calced_order.append(member(*name_value))
+ calced_order = [m.name for m in sorted(calced_order, key=_order_)]
+ else:
+ calced_order = _order_
+ for name in (
+ '_missing_', '_missing_value_', '_missing_name_',
+ '_ignore_', '_create_pseudo_member_', '_create_pseudo_member_values_',
+ '_generate_next_value_', '_order_', '__new__',
+ '_missing_', '_missing_value_', '_missing_name_',
+ '_boundary_',
+ '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
+ ):
+ attr = locals()[name]
+ if attr is not None:
+ clsdict[name] = attr
+ # now add members
+ for k in calced_order:
+ try:
+ clsdict[k] = original_dict[k]
+ except KeyError:
+ # this error will be handled when _order_ is checked
+ pass
+ for k, v in original_dict.items():
+ if k not in calced_order:
+ clsdict[k] = v
+ del _order_, _ignore_, _create_pseudo_member_, _create_pseudo_member_values_,
+ del _generate_next_value_, _missing_, _missing_value_, _missing_name_
+ #
+ # resume normal path
+ clsdict._locked = True
+ #
+ # check for illegal enum names (any others?)
+ member_names = clsdict._member_names
+ invalid_names = set(member_names) & set(['mro', ''])
+ if invalid_names:
+ raise ValueError('invalid enum member name(s): %s' % (
+ ', '.join(invalid_names), ))
+ _order_ = clsdict.pop('_order_', None)
+ if isinstance(_order_, basestring):
+ _order_ = _order_.replace(',',' ').split()
+ init = clsdict._init
+ start = clsdict._start
+ settings = clsdict._settings
+ creating_init = []
+ new_args = clsdict._new_args
+ auto_args = clsdict._auto_args
+ auto_init = False
+ if init is not None:
+ auto_init = True
+ creating_init = init[:]
+ if 'value' in creating_init and creating_init[0] != 'value':
+ raise TypeError("'value', if specified, must be the first item in 'init'")
+ magicvalue = MagicValue in settings
+ multivalue = MultiValue in settings
+ noalias = NoAlias in settings
+ unique = Unique in settings
+ # an Enum class cannot be mixed with other types (int, float, etc.) if
+ # it has an inherited __new__ unless a new __new__ is defined (or
+ # the resulting class will fail).
+ # an Enum class is final once enumeration items have been defined;
+ #
+ # remove any keys listed in _ignore_
+ clsdict.setdefault('_ignore_', []).append('_ignore_')
+ ignore = clsdict['_ignore_']
+ for key in ignore:
+ clsdict.pop(key, None)
+ #
+ boundary = boundary or clsdict.pop('_boundary_', None)
+ # convert to regular dict
+ clsdict = dict(clsdict.items())
+ member_type, first_enum = metacls._get_mixins_(cls, bases)
+ # get the method to create enum members
+ __new__, save_new, new_uses_args = metacls._find_new_(
+ clsdict,
+ member_type,
+ first_enum,
+ )
+ clsdict['_new_member_'] = staticmethod(__new__)
+ clsdict['_use_args_'] = new_uses_args
+ #
+ # convert future enum members into temporary _proto_members
+ # and record integer values in case this will be a Flag
+ flag_mask = 0
+ for name in member_names:
+ value = test_value = clsdict[name]
+ if isinstance(value, auto) and value.value is not _auto_null:
+ test_value = value.value
+ if isinstance(test_value, baseinteger):
+ flag_mask |= test_value
+ if isinstance(test_value, tuple) and test_value and isinstance(test_value[0], baseinteger):
+ flag_mask |= test_value[0]
+ clsdict[name] = _proto_member(value)
+ #
+ # temp stuff
+ clsdict['_creating_init_'] = creating_init
+ clsdict['_multivalue_'] = multivalue
+ clsdict['_magicvalue_'] = magicvalue
+ clsdict['_noalias_'] = noalias
+ clsdict['_unique_'] = unique
+ #
+ # house-keeping structures
+ clsdict['_member_names_'] = []
+ clsdict['_member_map_'] = OrderedDict()
+ clsdict['_member_type_'] = member_type
+ clsdict['_value2member_map_'] = {}
+ clsdict['_value2member_seq_'] = ()
+ clsdict['_settings_'] = settings
+ clsdict['_start_'] = start
+ clsdict['_auto_init_'] = init
+ clsdict['_new_args_'] = new_args
+ clsdict['_auto_args_'] = auto_args
+ clsdict['_order_function_'] = None
+ # now set the __repr__ for the value
+ clsdict['_value_repr_'] = metacls._find_data_repr_(cls, bases)
+ #
+ # Flag structures (will be removed if final class is not a Flag
+ clsdict['_boundary_'] = (
+ boundary
+ or getattr(first_enum, '_boundary_', None)
+ )
+ clsdict['_flag_mask_'] = flag_mask
+ clsdict['_all_bits_'] = 2 ** ((flag_mask).bit_length()) - 1
+ clsdict['_inverted_'] = None
+ #
+ # move skipped values out of the descriptor
+ for name, obj in clsdict.items():
+ if isinstance(obj, nonmember):
+ clsdict[name] = obj.value
+ #
+ # If a custom type is mixed into the Enum, and it does not know how
+ # to pickle itself, pickle.dumps will succeed but pickle.loads will
+ # fail. Rather than have the error show up later and possibly far
+ # from the source, sabotage the pickle protocol for this class so
+ # that pickle.dumps also fails.
+ #
+ # However, if the new class implements its own __reduce_ex__, do not
+ # sabotage -- it's on them to make sure it works correctly. We use
+ # __reduce_ex__ instead of any of the others as it is preferred by
+ # pickle over __reduce__, and it handles all pickle protocols.
+ unpicklable = False
+ if '__reduce_ex__' not in clsdict:
+ if member_type is not object:
+ methods = ('__getnewargs_ex__', '__getnewargs__',
+ '__reduce_ex__', '__reduce__')
+ if not any(m in member_type.__dict__ for m in methods):
+ _make_class_unpicklable(clsdict)
+ unpicklable = True
+ #
+ # create a default docstring if one has not been provided
+ if '__doc__' not in clsdict:
+ clsdict['__doc__'] = 'An enumeration.'
+ #
+ # create our new Enum type
+ try:
+ exc = None
+ enum_class = type.__new__(metacls, cls, bases, clsdict)
+ except RuntimeError as e:
+ # any exceptions raised by _proto_member (aka member.__new__) will get converted to
+ # a RuntimeError, so get that original exception back and raise
+ # it instead
+ exc = e.__cause__ or e
+ if exc is not None:
+ raise exc
+ #
+ # if Python 3.5 or ealier, implement the __set_name__ and
+ # __init_subclass__ protocols
+ if pyver < PY3_6:
+ for name in member_names:
+ enum_class.__dict__[name].__set_name__(enum_class, name)
+ for name, obj in enum_class.__dict__.items():
+ if name in member_names:
+ continue
+ if hasattr(obj, '__set_name__'):
+ obj.__set_name__(enum_class, name)
+ if Enum is not None:
+ super(enum_class, enum_class).__init_subclass__()
+ #
+ # double check that repr and friends are not the mixin's or various
+ # things break (such as pickle)
+ #
+ # Also, special handling for ReprEnum
+ if ReprEnum is not None and ReprEnum in bases:
+ if member_type is object:
+ raise TypeError(
+ 'ReprEnum subclasses must be mixed with a data type (i.e.'
+ ' int, str, float, etc.)'
+ )
+ if '__format__' not in clsdict:
+ enum_class.__format__ = member_type.__format__
+ clsdict['__format__'] = enum_class.__format__
+ if '__str__' not in clsdict:
+ method = member_type.__str__
+ if method is object.__str__:
+ # if member_type does not define __str__, object.__str__ will use
+ # its __repr__ instead, so we'll also use its __repr__
+ method = member_type.__repr__
+ enum_class.__str__ = method
+ clsdict['__str__'] = enum_class.__str__
+
+ for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
+ if name in clsdict:
+ # class has defined/imported/copied the method
+ continue
+ class_method = getattr(enum_class, name)
+ obj_method = getattr(member_type, name, None)
+ enum_method = getattr(first_enum, name, None)
+ if obj_method is not None and obj_method == class_method:
+ if name == '__reduce_ex__' and unpicklable:
+ continue
+ setattr(enum_class, name, enum_method)
+ clsdict[name] = enum_method
+ #
+ # for Flag, add __or__, __and__, __xor__, and __invert__
+ if Flag is not None and issubclass(enum_class, Flag):
+ for name in (
+ '__or__', '__and__', '__xor__',
+ '__ror__', '__rand__', '__rxor__',
+ '__invert__'
+ ):
+ if name not in clsdict:
+ setattr(enum_class, name, getattr(Flag, name))
+ clsdict[name] = enum_method
+ #
+ # method resolution and int's are not playing nice
+ # Python's less than 2.6 use __cmp__
+ if pyver < PY2_6:
+ #
+ if issubclass(enum_class, int):
+ setattr(enum_class, '__cmp__', getattr(int, '__cmp__'))
+ #
+ elif PY2:
+ #
+ if issubclass(enum_class, int):
+ for method in (
+ '__le__',
+ '__lt__',
+ '__gt__',
+ '__ge__',
+ '__eq__',
+ '__ne__',
+ '__hash__',
+ ):
+ setattr(enum_class, method, getattr(int, method))
+ #
+ # replace any other __new__ with our own (as long as Enum is not None,
+ # anyway) -- again, this is to support pickle
+ if Enum is not None:
+ # if the user defined their own __new__, save it before it gets
+ # clobbered in case they subclass later
+ if save_new:
+ setattr(enum_class, '__new_member__', enum_class.__dict__['__new__'])
+ setattr(enum_class, '__new__', Enum.__dict__['__new__'])
+ #
+ # _order_ checking is spread out into three/four steps
+ # - ensure _order_ is a list, not a string nor a function
+ # - if enum_class is a Flag:
+ # - remove any non-single-bit flags from _order_
+ # - remove any aliases from _order_
+ # - check that _order_ and _member_names_ match
+ #
+ # _order_ step 1: ensure _order_ is a list
+ if _order_:
+ if isinstance(_order_, staticmethod):
+ _order_ = _order_.__func__
+ if callable(_order_):
+ # save order for future subclasses
+ enum_class._order_function_ = staticmethod(_order_)
+ # create ordered list for comparison
+ _order_ = [m.name for m in sorted(enum_class, key=_order_)]
+ #
+ # remove Flag structures if final class is not a Flag
+ if (
+ Flag is None and cls != 'Flag'
+ or Flag is not None and not issubclass(enum_class, Flag)
+ ):
+ delattr(enum_class, '_boundary_')
+ delattr(enum_class, '_flag_mask_')
+ delattr(enum_class, '_all_bits_')
+ delattr(enum_class, '_inverted_')
+ elif Flag is not None and issubclass(enum_class, Flag):
+ # ensure _all_bits_ is correct and there are no missing flags
+ single_bit_total = 0
+ multi_bit_total = 0
+ for flag in enum_class._member_map_.values():
+ if _is_single_bit(flag._value_):
+ single_bit_total |= flag._value_
+ else:
+ # multi-bit flags are considered aliases
+ multi_bit_total |= flag._value_
+ if enum_class._boundary_ is not KEEP:
+ missed = list(_iter_bits_lsb(multi_bit_total & ~single_bit_total))
+ if missed:
+ raise TypeError(
+ 'invalid Flag %r -- missing values: %s'
+ % (cls, ', '.join((str(i) for i in missed)))
+ )
+ enum_class._flag_mask_ = single_bit_total
+ enum_class._all_bits_ = 2 ** ((single_bit_total).bit_length()) - 1
+ #
+ # set correct __iter__
+ if [m._value_ for m in enum_class] != sorted([m._value_ for m in enum_class]):
+ enum_class._iter_member_ = enum_class._iter_member_by_def_
+ if _order_:
+ # _order_ step 2: remove any items from _order_ that are not single-bit
+ _order_ = [
+ o
+ for o in _order_
+ if o not in enum_class._member_map_ or _is_single_bit(enum_class[o]._value_)
+ ]
+ #
+ # check for constants with auto() values
+ for k, v in enum_class.__dict__.items():
+ if isinstance(v, constant) and isinstance(v.value, auto):
+ v.value = enum_class(v.value.value)
+ #
+ if _order_:
+ # _order_ step 3: remove aliases from _order_
+ _order_ = [
+ o
+ for o in _order_
+ if (
+ o not in enum_class._member_map_
+ or
+ (o in enum_class._member_map_ and o in enum_class._member_names_)
+ )]
+ # _order_ step 4: verify that _order_ and _member_names_ match
+ if _order_ != enum_class._member_names_:
+ raise TypeError(
+ 'member order does not match _order_:\n%r\n%r'
+ % (enum_class._member_names_, _order_)
+ )
+ return enum_class
+
+ def __bool__(cls):
+ """
+ classes/types should always be True.
+ """
+ return True
+
+ def __call__(cls, value=no_arg, names=None, module=None, qualname=None, type=None, start=1, boundary=None):
+ """Either returns an existing member, or creates a new enum class.
+
+ This method is used both when an enum class is given a value to match
+ to an enumeration member (i.e. Color(3)) and for the functional API
+ (i.e. Color = Enum('Color', names='red green blue')).
+
+ When used for the functional API: `module`, if set, will be stored in
+ the new class' __module__ attribute; `type`, if set, will be mixed in
+ as the first base class.
+
+ Note: if `module` is not set this routine will attempt to discover the
+ calling module by walking the frame stack; if this is unsuccessful
+ the resulting class will not be pickleable.
+ """
+ if names is None: # simple value lookup
+ return cls.__new__(cls, value)
+ # otherwise, functional API: we're creating a new Enum type
+ return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start, boundary=boundary)
+
+ def __contains__(cls, member):
+ if not isinstance(member, Enum):
+ raise TypeError("%r (%r) is not an <aenum 'Enum'>" % (member, type(member)))
+ if not isinstance(member, cls):
+ return False
+ return True
+
+ def __delattr__(cls, attr):
+ # nicer error message when someone tries to delete an attribute
+ # (see issue19025).
+ if attr in cls._member_map_:
+ raise AttributeError(
+ "%s: cannot delete Enum member %r." % (cls.__name__, attr),
+ )
+ found_attr = _get_attr_from_chain(cls, attr)
+ if isinstance(found_attr, constant):
+ raise AttributeError(
+ "%s: cannot delete constant %r" % (cls.__name__, attr),
+ )
+ elif isinstance(found_attr, property):
+ raise AttributeError(
+ "%s: cannot delete property %r" % (cls.__name__, attr),
+ )
+ super(EnumType, cls).__delattr__(attr)
+
+ def __dir__(cls):
+ interesting = set(cls._member_names_ + [
+ '__class__', '__contains__', '__doc__', '__getitem__',
+ '__iter__', '__len__', '__members__', '__module__',
+ '__name__',
+ ])
+ if cls._new_member_ is not object.__new__:
+ interesting.add('__new__')
+ if cls.__init_subclass__ is not Enum.__init_subclass__:
+ interesting.add('__init_subclass__')
+ if hasattr(object, '__qualname__'):
+ interesting.add('__qualname__')
+ for method in ('__init__', '__format__', '__repr__', '__str__'):
+ if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)):
+ interesting.add(method)
+ if cls._member_type_ is object:
+ return sorted(interesting)
+ else:
+ # return whatever mixed-in data type has
+ return sorted(set(dir(cls._member_type_)) | interesting)
+
+ @_bltin_property
+ def __members__(cls):
+ """Returns a mapping of member name->value.
+
+ This mapping lists all enum members, including aliases. Note that this
+ is a copy of the internal mapping.
+ """
+ return cls._member_map_.copy()
+
+ def __getitem__(cls, name):
+ try:
+ return cls._member_map_[name]
+ except KeyError:
+ exc = _sys.exc_info()[1]
+ if Flag is not None and issubclass(cls, Flag) and '|' in name:
+ try:
+ # may be an __or__ed name
+ result = cls(0)
+ for n in name.split('|'):
+ result |= cls[n]
+ return result
+ except KeyError:
+ raise exc
+ 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_)
+
+ def __reversed__(cls):
+ return (cls._member_map_[name] for name in reversed(cls._member_names_))
+
+ def __len__(cls):
+ return len(cls._member_names_)
+
+ __nonzero__ = __bool__
+
+ def __repr__(cls):
+ return "<aenum %r>" % (cls.__name__, )
+
+ def __setattr__(cls, name, value):
+ """Block attempts to reassign Enum members/constants.
+
+ A simple assignment to the class namespace only changes one of the
+ several possible ways to get an Enum member from the Enum class,
+ resulting in an inconsistent Enumeration.
+ """
+ member_map = cls.__dict__.get('_member_map_', {})
+ if name in member_map:
+ raise AttributeError(
+ '%s: cannot rebind member %r.' % (cls.__name__, name),
+ )
+ found_attr = _get_attr_from_chain(cls, name)
+ if isinstance(found_attr, constant):
+ raise AttributeError(
+ "%s: cannot rebind constant %r" % (cls.__name__, name),
+ )
+ elif isinstance(found_attr, property):
+ raise AttributeError(
+ "%s: cannot rebind property %r" % (cls.__name__, name),
+ )
+ super(EnumType, cls).__setattr__(name, value)
+
+ def _convert(cls, *args, **kwds):
+ import warnings
+ warnings.warn("_convert is deprecated and will be removed, use"
+ " _convert_ instead.", DeprecationWarning, stacklevel=2)
+ return cls._convert_(*args, **kwds)
+
+ def _convert_(cls, name, module, filter, source=None, boundary=None, as_global=False):
+ """
+ Create a new Enum subclass that replaces a collection of global constants
+ """
+ # convert all constants from source (or module) that pass filter() to
+ # a new Enum called name, and export the enum and its members back to
+ # module;
+ # also, replace the __reduce_ex__ method so unpickling works in
+ # previous Python versions
+ module_globals = vars(_sys.modules[module])
+ if source:
+ source = vars(source)
+ else:
+ source = module_globals
+ members = [(key, source[key]) for key in source.keys() if filter(key)]
+ try:
+ # sort by value, name
+ members.sort(key=lambda t: (t[1], t[0]))
+ except TypeError:
+ # unless some values aren't comparable, in which case sort by just name
+ members.sort(key=lambda t: t[0])
+ cls = cls(name, members, module=module, boundary=boundary or KEEP)
+ cls.__reduce_ex__ = _reduce_ex_by_name
+ if as_global:
+ global_enum(cls)
+ else:
+ module_globals.update(cls.__members__)
+ module_globals[name] = cls
+ return cls
+
+ def _create_(cls, class_name, names, module=None, qualname=None, type=None, start=1, boundary=None):
+ """Convenience method to create a new Enum class.
+
+ `names` can be:
+
+ * A string containing member names, separated either with spaces or
+ commas. Values are auto-numbered from 1.
+ * An iterable of member names. Values are auto-numbered from 1.
+ * An iterable of (member name, value) pairs.
+ * A mapping of member name -> value.
+ """
+ if PY2:
+ # if class_name is unicode, attempt a conversion to ASCII
+ if isinstance(class_name, unicode):
+ try:
+ class_name = class_name.encode('ascii')
+ except UnicodeEncodeError:
+ raise TypeError('%r is not representable in ASCII' % (class_name, ))
+ metacls = cls.__class__
+ if type is None:
+ bases = (cls, )
+ else:
+ bases = (type, cls)
+ _, first_enum = cls._get_mixins_(class_name, bases)
+ generate = getattr(first_enum, '_generate_next_value_', None)
+ generate = getattr(generate, 'im_func', generate)
+ # special processing needed for names?
+ if isinstance(names, basestring):
+ names = names.replace(',', ' ').split()
+ if isinstance(names, (tuple, list)) and names and isinstance(names[0], basestring):
+ original_names, names = names, []
+ last_values = []
+ for count, name in enumerate(original_names):
+ value = generate(name, start, count, last_values[:])
+ last_values.append(value)
+ names.append((name, value))
+ # Here, names is either an iterable of (name, value) or a mapping.
+ item = None # in case names is empty
+ clsdict = None
+ for item in names:
+ if clsdict is None:
+ # first time initialization
+ if isinstance(item, basestring):
+ clsdict = {}
+ else:
+ # remember the order
+ clsdict = metacls.__prepare__(class_name, bases)
+ if isinstance(item, basestring):
+ member_name, member_value = item, names[item]
+ else:
+ member_name, member_value = item
+ clsdict[member_name] = member_value
+ if clsdict is None:
+ # in case names was empty
+ clsdict = metacls.__prepare__(class_name, bases)
+ enum_class = metacls.__new__(metacls, class_name, bases, clsdict, boundary=boundary)
+ # TODO: replace the frame hack if a blessed way to know the calling
+ # module is ever developed
+ if module is None:
+ try:
+ module = _sys._getframe(2).f_globals['__name__']
+ except (AttributeError, KeyError):
+ pass
+ if module is None:
+ _make_class_unpicklable(enum_class)
+ else:
+ enum_class.__module__ = module
+ if qualname is not None:
+ enum_class.__qualname__ = qualname
+ return enum_class
+
+ @classmethod
+ def _check_for_existing_members_(mcls, class_name, bases):
+ if Enum is None:
+ return
+ for chain in bases:
+ for base in chain.__mro__:
+ if issubclass(base, Enum) and base._member_names_:
+ raise TypeError(
+ "<aenum %r> cannot extend %r"
+ % (class_name, base)
+ )
+ @classmethod
+ def _get_mixins_(mcls, class_name, bases):
+ """Returns the type for creating enum members, and the first inherited
+ enum class.
+
+ bases: the tuple of bases that was given to __new__
+ """
+ if not bases or Enum is None:
+ return object, Enum
+
+ mcls._check_for_existing_members_(class_name, bases)
+
+ # ensure final parent class is an Enum derivative, find any concrete
+ # data type, and check that Enum has no members
+ first_enum = bases[-1]
+ if not issubclass(first_enum, Enum):
+ raise TypeError("new enumerations should be created as "
+ "`EnumName([mixin_type, ...] [data_type,] enum_type)`")
+ member_type = mcls._find_data_type_(class_name, bases) or object
+ if first_enum._member_names_:
+ raise TypeError("cannot extend enumerations via subclassing")
+ #
+ return member_type, first_enum
+
+ @classmethod
+ def _find_data_repr_(mcls, class_name, bases):
+ for chain in bases:
+ for base in chain.__mro__:
+ if base is object:
+ continue
+ elif issubclass(base, Enum):
+ # if we hit an Enum, use it's _value_repr_
+ return base._value_repr_
+ elif '__repr__' in base.__dict__:
+ # this is our data repr
+ return base.__dict__['__repr__']
+ return None
+
+ @classmethod
+ def _find_data_type_(mcls, class_name, bases):
+ data_types = set()
+ for chain in bases:
+ candidate = None
+ for base in chain.__mro__:
+ if base is object or base is StdlibEnum or base is StdlibFlag:
+ continue
+ elif issubclass(base, Enum):
+ if base._member_type_ is not object:
+ data_types.add(base._member_type_)
+ elif '__new__' in base.__dict__:
+ if issubclass(base, Enum):
+ continue
+ elif StdlibFlag is not None and issubclass(base, StdlibFlag):
+ continue
+ data_types.add(candidate or base)
+ break
+ else:
+ candidate = candidate or base
+ if len(data_types) > 1:
+ raise TypeError('%r: too many data types: %r' % (class_name, data_types))
+ elif data_types:
+ return data_types.pop()
+ else:
+ return None
+
+ @staticmethod
+ def _get_settings_(bases):
+ """Returns the combined _settings_ of all Enum base classes
+
+ bases: the tuple of bases given to __new__
+ """
+ settings = set()
+ for chain in bases:
+ for base in chain.__mro__:
+ if issubclass(base, Enum):
+ for s in base._settings_:
+ settings.add(s)
+ return settings
+
+ @classmethod
+ def _find_new_(mcls, clsdict, member_type, first_enum):
+ """Returns the __new__ to be used for creating the enum members.
+
+ clsdict: the class dictionary given to __new__
+ member_type: the data type whose __new__ will be used by default
+ first_enum: enumeration to check for an overriding __new__
+ """
+ # now find the correct __new__, checking to see of one was defined
+ # by the user; also check earlier enum classes in case a __new__ was
+ # saved as __new_member__
+ __new__ = clsdict.get('__new__', None)
+ #
+ # should __new__ be saved as __new_member__ later?
+ save_new = first_enum is not None and __new__ is not None
+ #
+ if __new__ is None:
+ # check all possibles for __new_member__ before falling back to
+ # __new__
+ for method in ('__new_member__', '__new__'):
+ for possible in (member_type, first_enum):
+ target = getattr(possible, method, None)
+ if target not in (
+ None,
+ None.__new__,
+ object.__new__,
+ Enum.__new__,
+ StdlibEnum.__new__,
+ ):
+ __new__ = target
+ break
+ if __new__ is not None:
+ break
+ else:
+ __new__ = object.__new__
+ # if a non-object.__new__ is used then whatever value/tuple was
+ # assigned to the enum member name will be passed to __new__ and to the
+ # new enum member's __init__
+ if __new__ is object.__new__:
+ new_uses_args = False
+ else:
+ new_uses_args = True
+ #
+ return __new__, save_new, new_uses_args
+
+
+ # In order to support Python 2 and 3 with a single
+ # codebase we have to create the Enum methods separately
+ # and then use the `type(name, bases, dict)` method to
+ # create the class.
+
+EnumMeta = EnumType
+
+enum_dict = _Addendum(
+ dict=EnumType.__prepare__('Enum', (object, )),
+ doc="Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n",
+ ns=globals(),
+ )
+
+@enum_dict
+def __init__(self, *args, **kwds):
+ # auto-init method
+ _auto_init_ = self._auto_init_
+ if _auto_init_ is None:
+ return
+ if 'value' in _auto_init_:
+ # remove 'value' from _auto_init_ as it has already been handled
+ _auto_init_ = _auto_init_[1:]
+ if _auto_init_:
+ if len(_auto_init_) < len(args):
+ raise TypeError('%d arguments expected (%s), %d received (%s)'
+ % (len(_auto_init_), _auto_init_, len(args), args))
+ for name, arg in zip(_auto_init_, args):
+ setattr(self, name, arg)
+ if len(args) < len(_auto_init_):
+ remaining_args = _auto_init_[len(args):]
+ for name in remaining_args:
+ value = kwds.pop(name, undefined)
+ if value is undefined:
+ raise TypeError('missing value for: %r' % (name, ))
+ setattr(self, name, value)
+ if kwds:
+ # too many keyword arguments
+ raise TypeError('invalid keyword(s): %s' % ', '.join(kwds.keys()))
+
+@enum_dict
+def __new__(cls, value):
+ # all enum instances are actually created during class construction
+ # without calling this method; this method is called by the metaclass'
+ # __call__ (i.e. Color(3) ), and by pickle
+ if NoAlias in cls._settings_:
+ raise TypeError('NoAlias enumerations cannot be looked up by value')
+ if type(value) is cls:
+ # For lookups like Color(Color.red)
+ # value = value.value
+ return value
+ # by-value search for a matching enum member
+ # see if it's in the reverse mapping (for hashable values)
+ try:
+ if value in cls._value2member_map_:
+ return cls._value2member_map_[value]
+ except TypeError:
+ # not there, now do long search -- O(n) behavior
+ for name, member in cls._value2member_seq_:
+ if name == value:
+ return member
+ # still not found -- try _missing_ hook
+ try:
+ exc = None
+ result = cls._missing_value_(value)
+ except Exception as e:
+ exc = e
+ result = None
+ if isinstance(result, cls) or getattr(cls, '_boundary_', None) is EJECT:
+ return result
+ else:
+ if value is no_arg:
+ ve_exc = ValueError('%s() should be called with a value' % (cls.__name__, ))
+ else:
+ ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__))
+ if result is None and exc is None:
+ raise ve_exc
+ elif exc is None:
+ exc = TypeError(
+ 'error in %s._missing_: returned %r instead of None or a valid member'
+ % (cls.__name__, result)
+ )
+ if not isinstance(exc, ValueError):
+ exc.__cause__ = ve_exc
+ raise exc
+
+@enum_dict
+@classmethod
+def __init_subclass__(cls, **kwds):
+ if pyver < PY3_6:
+ # end of the line
+ if kwds:
+ raise TypeError('unconsumed keyword arguments: %r' % (kwds, ))
+ else:
+ super(Enum, cls).__init_subclass__(**kwds)
+
+@enum_dict
+@staticmethod
+def _generate_next_value_(name, start, count, last_values, *args, **kwds):
+ for last_value in reversed(last_values):
+ try:
+ new_value = last_value + 1
+ break
+ except TypeError:
+ pass
+ else:
+ new_value = start
+ if args:
+ return (new_value, ) + args
+ else:
+ return new_value
+
+@enum_dict
+@classmethod
+def _missing_(cls, value):
+ "deprecated, use _missing_value_ instead"
+ return None
+
+@enum_dict
+@classmethod
+def _missing_value_(cls, value):
+ "used for failed value access"
+ return cls._missing_(value)
+
+@enum_dict
+@classmethod
+def _missing_name_(cls, name):
+ "used for failed item access"
+ return None
+
+@enum_dict
+def __repr__(self):
+ v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__
+ return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_))
+
+@enum_dict
+def __str__(self):
+ return "%s.%s" % (self.__class__.__name__, self._name_)
+
+if PY3:
+ @enum_dict
+ def __dir__(self):
+ """
+ Returns all members and all public methods
+ """
+ if self.__class__._member_type_ is object:
+ interesting = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value'])
+ else:
+ interesting = set(object.__dir__(self))
+ for name in getattr(self, '__dict__', []):
+ if name[0] != '_':
+ interesting.add(name)
+ for cls in self.__class__.mro():
+ for name, obj in cls.__dict__.items():
+ if name[0] == '_':
+ continue
+ if isinstance(obj, property):
+ # that's an enum.property
+ if obj.fget is not None or name not in self._member_map_:
+ interesting.add(name)
+ else:
+ # in case it was added by `dir(self)`
+ interesting.discard(name)
+ else:
+ interesting.add(name)
+ return sorted(interesting)
+
+@enum_dict
+def __format__(self, format_spec):
+ # mixed-in Enums should use the mixed-in type's __format__, otherwise
+ # we can get strange results with the Enum name showing up instead of
+ # the value
+
+ # pure Enum branch / overridden __str__ branch
+ overridden_str = self.__class__.__str__ != Enum.__str__
+ if self._member_type_ is object or overridden_str:
+ cls = str
+ val = str(self)
+ # mix-in branch
+ else:
+ cls = self._member_type_
+ val = self.value
+ return cls.__format__(val, format_spec)
+
+@enum_dict
+def __hash__(self):
+ return hash(self._name_)
+
+@enum_dict
+def __reduce_ex__(self, proto):
+ return self.__class__, (self._value_, )
+
+
+####################################
+# Python's less than 2.6 use __cmp__
+
+if pyver < PY2_6:
+
+ @enum_dict
+ def __cmp__(self, other):
+ if type(other) is self.__class__:
+ if self is other:
+ return 0
+ return -1
+ return NotImplemented
+ raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__))
+
+else:
+
+ @enum_dict
+ def __le__(self, other):
+ raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__))
+
+ @enum_dict
+ def __lt__(self, other):
+ raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__))
+
+ @enum_dict
+ def __ge__(self, other):
+ raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__))
+
+ @enum_dict
+ def __gt__(self, other):
+ raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__))
+
+
+@enum_dict
+def __eq__(self, other):
+ if type(other) is self.__class__:
+ return self is other
+ return NotImplemented
+
+@enum_dict
+def __ne__(self, other):
+ if type(other) is self.__class__:
+ return self is not other
+ return NotImplemented
+
+@enum_dict
+def __hash__(self):
+ return hash(self._name_)
+
+@enum_dict
+def __reduce_ex__(self, proto):
+ return self.__class__, (self._value_, )
+
+
+# enum.property is used to provide access to the `name`, `value', etc.,
+# properties of enum members while keeping some measure of protection
+# from modification, while still allowing for an enumeration to have
+# members named `name`, `value`, etc.. This works because enumeration
+# members are not set directly on the enum class -- enum.property will
+# look them up in _member_map_.
+
+@enum_dict
+@property
+def name(self):
+ return self._name_
+
+@enum_dict
+@property
+def value(self):
+ return self._value_
+
+@enum_dict
+@property
+def values(self):
+ return self._values_
+
+def _reduce_ex_by_name(self, proto):
+ return self.name
+
+Enum = EnumType('Enum', (object, ), enum_dict.resolve())
+del enum_dict
+
+ # Enum has now been created
+
+class ReprEnum(Enum):
+ """
+ Only changes the repr(), leaving str() and format() to the mixed-in type.
+ """
+
+
+class IntEnum(int, ReprEnum):
+ """
+ Enum where members are also (and must be) ints
+ """
+
+
+class StrEnum(str, ReprEnum):
+ """
+ Enum where members are also (and must already be) strings
+
+ default value is member name, lower-cased
+ """
+
+ def __new__(cls, *values, **kwds):
+ if kwds:
+ raise TypeError('%r: keyword arguments not supported' % (cls.__name__))
+ if values:
+ if not isinstance(values[0], str):
+ raise TypeError('%s: values must be str [%r is a %r]' % (cls.__name__, values[0], type(values[0])))
+ value = str(*values)
+ member = str.__new__(cls, value)
+ member._value_ = value
+ return member
+
+ __str__ = str.__str__
+
+ def _generate_next_value_(name, start, count, last_values):
+ """
+ Return the lower-cased version of the member name.
+ """
+ return name.lower()
+
+
+class LowerStrEnum(StrEnum):
+ """
+ Enum where members are also (and must already be) lower-case strings
+
+ default value is member name, lower-cased
+ """
+
+ def __new__(cls, value, *args, **kwds):
+ obj = StrEnum.__new_member__(cls, value, *args, **kwds)
+ if value != value.lower():
+ raise ValueError('%r is not lower-case' % value)
+ return obj
+
+
+class UpperStrEnum(StrEnum):
+ """
+ Enum where members are also (and must already be) upper-case strings
+
+ default value is member name, upper-cased
+ """
+
+ def __new__(cls, value, *args, **kwds):
+ obj = StrEnum.__new_member__(cls, value, *args, **kwds)
+ if value != value.upper():
+ raise ValueError('%r is not upper-case' % value)
+ return obj
+
+ def _generate_next_value_(name, start, count, last_values, *args, **kwds):
+ return name.upper()
+
+
+if PY3:
+ class AutoEnum(Enum):
+ """
+ automatically use _generate_next_value_ when values are missing (Python 3 only)
+ """
+ _settings_ = MagicValue
+
+
+class AutoNumberEnum(Enum):
+ """
+ Automatically assign increasing values to members.
+
+ Py3: numbers match creation order
+ Py2: numbers are assigned alphabetically by member name
+ (unless `_order_` is specified)
+ """
+
+ def __new__(cls, *args, **kwds):
+ value = len(cls.__members__) + 1
+ if cls._member_type_ is int:
+ obj = int.__new__(cls, value)
+ elif cls._member_type_ is long:
+ obj = long.__new__(cls, value)
+ else:
+ obj = object.__new__(cls)
+ obj._value_ = value
+ return obj
+
+
+class AddValueEnum(Enum):
+ _settings_ = AddValue
+
+
+class MultiValueEnum(Enum):
+ """
+ Multiple values can map to each member.
+ """
+ _settings_ = MultiValue
+
+
+class NoAliasEnum(Enum):
+ """
+ Duplicate value members are distinct, but cannot be looked up by value.
+ """
+ _settings_ = NoAlias
+
+
+class OrderedEnum(Enum):
+ """
+ Add ordering based on values of Enum members.
+ """
+
+ def __ge__(self, other):
+ if self.__class__ is other.__class__:
+ return self._value_ >= other._value_
+ return NotImplemented
+
+ def __gt__(self, other):
+ if self.__class__ is other.__class__:
+ return self._value_ > other._value_
+ return NotImplemented
+
+ def __le__(self, other):
+ if self.__class__ is other.__class__:
+ return self._value_ <= other._value_
+ return NotImplemented
+
+ def __lt__(self, other):
+ if self.__class__ is other.__class__:
+ return self._value_ < other._value_
+ return NotImplemented
+
+
+if sqlite3:
+ class SqliteEnum(Enum):
+ def __conform__(self, protocol):
+ if protocol is sqlite3.PrepareProtocol:
+ return self.name
+
+
+class UniqueEnum(Enum):
+ """
+ Ensure no duplicate values exist.
+ """
+ _settings_ = Unique
+
+
+def convert(enum, name, module, filter, source=None):
+ """
+ Create a new Enum subclass that replaces a collection of global constants
+
+ enum: Enum, IntEnum, ...
+ name: name of new Enum
+ module: name of module (__name__ in global context)
+ filter: function that returns True if name should be converted to Enum member
+ source: namespace to check (defaults to 'module')
+ """
+ # convert all constants from source (or module) that pass filter() to
+ # a new Enum called name, and export the enum and its members back to
+ # module;
+ # also, replace the __reduce_ex__ method so unpickling works in
+ # previous Python versions
+ module_globals = vars(_sys.modules[module])
+ if source:
+ source = vars(source)
+ else:
+ source = module_globals
+ members = dict((name, value) for name, value in source.items() if filter(name))
+ enum = enum(name, members, module=module)
+ enum.__reduce_ex__ = _reduce_ex_by_name
+ module_globals.update(enum.__members__)
+ module_globals[name] = enum
+
+def extend_enum(enumeration, name, *args, **kwds):
+ """
+ Add a new member to an existing Enum.
+ """
+ # there are four possibilities:
+ # - extending an aenum Enum or 3.11+ enum Enum
+ # - extending an aenum Flag or 3.11+ enum Flag
+ # - extending a pre-3.11 stdlib Enum Flag
+ # - extending a 3.11+ stdlib Flag
+ #
+ # fail early if name is already in the enumeration
+ if (
+ name in enumeration.__dict__
+ or name in enumeration._member_map_
+ or name in [t[1] for t in getattr(enumeration, '_value2member_seq_', ())]
+ ):
+ raise TypeError('%r already in use as %r' % (name, enumeration.__dict__.get(name, enumeration[name])))
+ # and check for other instances in parent classes
+ descriptor = None
+ for base in enumeration.__mro__[1:]:
+ descriptor = base.__dict__.get(name)
+ if descriptor is not None:
+ if isinstance(descriptor, (property, DynamicClassAttribute)):
+ break
+ else:
+ raise TypeError('%r already in use in superclass %r' % (name, base.__name__))
+ try:
+ _member_map_ = enumeration._member_map_
+ _member_names_ = enumeration._member_names_
+ _member_type_ = enumeration._member_type_
+ _value2member_map_ = enumeration._value2member_map_
+ base_attributes = set([a for b in enumeration.mro() for a in b.__dict__])
+ except AttributeError:
+ raise TypeError('%r is not a supported Enum' % (enumeration, ))
+ try:
+ _value2member_seq_ = enumeration._value2member_seq_
+ _multi_value_ = MultiValue in enumeration._settings_
+ _no_alias_ = NoAlias in enumeration._settings_
+ _unique_ = Unique in enumeration._settings_
+ _auto_init_ = enumeration._auto_init_ or []
+ except AttributeError:
+ # standard Enum
+ _value2member_seq_ = []
+ _multi_value_ = False
+ _no_alias_ = False
+ _unique_ = False
+ _auto_init_ = []
+ if _multi_value_ and not args:
+ # must specify values for multivalue enums
+ raise ValueError('no values specified for MultiValue enum %r' % enumeration.__name__)
+ mt_new = _member_type_.__new__
+ _new = getattr(enumeration, '__new_member__', mt_new)
+ if not args:
+ last_values = [m.value for m in enumeration]
+ count = len(enumeration)
+ start = getattr(enumeration, '_start_', None)
+ if start is None:
+ start = last_values and (last_values[-1] + 1) or 1
+ _gnv = getattr(enumeration, '_generate_next_value_', None)
+ if _gnv is not None:
+ args = ( _gnv(name, start, count, last_values), )
+ else:
+ # must be a 3.4 or 3.5 Enum
+ args = (start, )
+ if _new is object.__new__:
+ new_uses_args = False
+ else:
+ new_uses_args = True
+ if len(args) == 1:
+ [value] = args
+ else:
+ value = args
+ more_values = ()
+ kwds = {}
+ if isinstance(value, enum):
+ args = value.args
+ kwds = value.kwds
+ if not isinstance(value, tuple):
+ args = (value, )
+ else:
+ args = value
+ # tease value out of auto-init if specified
+ if 'value' in _auto_init_:
+ if 'value' in kwds:
+ value = kwds.pop('value')
+ else:
+ value, args = args[0], args[1:]
+ elif _multi_value_:
+ value, more_values, args = args[0], args[1:], ()
+ if new_uses_args:
+ args = (value, )
+ if _member_type_ is tuple:
+ args = (args, )
+ if not new_uses_args:
+ new_member = _new(enumeration)
+ if not hasattr(new_member, '_value_'):
+ new_member._value_ = value
+ else:
+ new_member = _new(enumeration, *args, **kwds)
+ if not hasattr(new_member, '_value_'):
+ new_member._value_ = _member_type_(*args)
+ value = new_member._value_
+ if _multi_value_:
+ if 'value' in _auto_init_:
+ args = more_values
+ else:
+ # put all the values back into args for the init call
+ args = (value, ) + more_values
+ new_member._name_ = name
+ new_member.__objclass__ = enumeration.__class__
+ new_member.__init__(*args)
+ new_member._values_ = (value, ) + more_values
+ # do final checks before modifying enum structures:
+ # - is new member a flag?
+ # - does the new member fit in the enum's declared _boundary_?
+ # - is new member an alias?
+ #
+ _all_bits_ = _flag_mask_ = None
+ if hasattr(enumeration, '_all_bits_'):
+ _all_bits_ = enumeration._all_bits_ | value
+ _flag_mask_ = enumeration._flag_mask_ | value
+ if enumeration._boundary_ != 'keep':
+ missed = list(_iter_bits_lsb(_flag_mask_ & ~_all_bits_))
+ if missed:
+ raise TypeError(
+ 'invalid Flag %r -- missing values: %s'
+ % (cls, ', '.join((str(i) for i in missed)))
+ )
+ # If another member with the same value was already defined, the
+ # new member becomes an alias to the existing one.
+ if _no_alias_:
+ # unless NoAlias was specified
+ return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_)
+ else:
+ # handle "normal" aliases
+ new_values = new_member._values_
+ for canonical_member in _member_map_.values():
+ canonical_values_ = getattr(canonical_member, '_values_', [canonical_member._value_])
+ for canonical_value in canonical_values_:
+ for new_value in new_values:
+ if canonical_value == new_value:
+ # name is an alias
+ if _unique_ or _multi_value_:
+ # aliases not allowed in Unique and MultiValue enums
+ raise ValueError('%r is a duplicate of %r' % (new_member, canonical_member))
+ else:
+ # aliased name can be added, remaining checks irrelevant
+ # aliases don't appear in member names (only in __members__ and _member_map_).
+ return _finalize_extend_enum(enumeration, canonical_member, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True)
+ # not a standard alias, but maybe a flag alias
+ if pyver < PY3_6:
+ flag_bases = Flag,
+ else:
+ flag_bases = Flag, StdlibFlag
+ if issubclass(enumeration, flag_bases) and hasattr(enumeration, '_all_bits_'):
+ # handle the new flag type
+ if _is_single_bit(value):
+ # a new member! (an aliase would have been discovered in the previous loop)
+ return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_)
+ else:
+ # might be an 3.11 Flag alias
+ if value & enumeration._flag_mask_ == value and _value2member_map_.get(value) is not None:
+ # yup, it's an alias to existing members... and its an alias of an alias
+ canonical = _value2member_map_.get(value)
+ return _finalize_extend_enum(enumeration, canonical, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True)
+ else:
+ return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_, is_alias=True)
+ else:
+ # if we get here, we have a brand new member
+ return _finalize_extend_enum(enumeration, new_member)
+
+def _finalize_extend_enum(enumeration, new_member, name=None, bits=None, mask=None, is_alias=False):
+ name = name or new_member.name
+ descriptor = None
+ for base in enumeration.__mro__[1:]:
+ descriptor = base.__dict__.get(name)
+ if descriptor is not None:
+ if isinstance(descriptor, (property, DynamicClassAttribute)):
+ break
+ else:
+ raise TypeError('%r already in use in superclass %r' % (name, base.__name__))
+ if not descriptor:
+ # get redirect in place before adding to _member_map_
+ redirect = property()
+ redirect.__set_name__(enumeration, name)
+ setattr(enumeration, name, redirect)
+ if not is_alias:
+ enumeration._member_names_.append(name)
+ enumeration._member_map_[name] = new_member
+ for v in getattr(new_member, '_values_', [new_member._value_]):
+ try:
+ enumeration._value2member_map_[v] = new_member
+ except TypeError:
+ enumeration._value2member_seq_ += ((v, new_member), )
+ if bits:
+ enumeration._all_bits_ = bits
+ enumeration._flag_mask_ = mask
+ return new_member
+
+def unique(enumeration):
+ """
+ Class decorator that ensures only unique members exist in an enumeration.
+ """
+ duplicates = []
+ for name, member in enumeration.__members__.items():
+ if name != member.name:
+ duplicates.append((name, member.name))
+ if duplicates:
+ duplicate_names = ', '.join(
+ ["%s -> %s" % (alias, name) for (alias, name) in duplicates]
+ )
+ raise ValueError('duplicate names found in %r: %s' %
+ (enumeration, duplicate_names)
+ )
+ return enumeration
+
+# Flag
+
+@export(globals())
+class FlagBoundary(StrEnum):
+ """
+ control how out of range values are handled
+ "strict" -> error is raised [default]
+ "conform" -> extra bits are discarded
+ "eject" -> lose flag status (becomes a normal integer)
+ """
+ STRICT = auto()
+ CONFORM = auto()
+ EJECT = auto()
+ KEEP = auto()
+assert FlagBoundary.STRICT == 'strict', (FlagBoundary.STRICT, FlagBoundary.CONFORM)
+
+class Flag(Enum):
+ """
+ Generic flag enumeration.
+
+ Derive from this class to define new flag enumerations.
+ """
+
+ _boundary_ = STRICT
+ _numeric_repr_ = repr
+
+
+ def _generate_next_value_(name, start, count, last_values, *args, **kwds):
+ """
+ Generate the next value when not given.
+
+ name: the name of the member
+ start: the initital start value or None
+ count: the number of existing members
+ last_value: the last value assigned or None
+ """
+ if not count:
+ if args:
+ return ((1, start)[start is not None], ) + args
+ else:
+ return (1, start)[start is not None]
+ else:
+ last_value = max(last_values)
+ try:
+ high_bit = _high_bit(last_value)
+ result = 2 ** (high_bit+1)
+ if args:
+ return (result,) + args
+ else:
+ return result
+ except Exception:
+ pass
+ raise TypeError('invalid Flag value: %r' % last_value)
+
+ @classmethod
+ def _iter_member_by_value_(cls, value):
+ """
+ Extract all members from the value in definition (i.e. increasing value) order.
+ """
+ for val in _iter_bits_lsb(value & cls._flag_mask_):
+ yield cls._value2member_map_.get(val)
+
+ _iter_member_ = _iter_member_by_value_
+
+ @classmethod
+ def _iter_member_by_def_(cls, value):
+ """
+ Extract all members from the value in definition order.
+ """
+ members = list(cls._iter_member_by_value_(value))
+ members.sort(key=lambda m: m._sort_order_)
+ for member in members:
+ yield member
+
+ @classmethod
+ def _missing_(cls, value):
+ """
+ return a member matching the given value, or None
+ """
+ return cls._create_pseudo_member_(value)
+
+ @classmethod
+ def _create_pseudo_member_(cls, *values):
+ """
+ Create a composite member.
+ """
+ value = values[0]
+ if not isinstance(value, baseinteger):
+ raise ValueError(
+ "%r is not a valid %s" % (value, getattr(cls, '__qualname__', cls.__name__))
+ )
+ # check boundaries
+ # - value must be in range (e.g. -16 <-> +15, i.e. ~15 <-> 15)
+ # - value must not include any skipped flags (e.g. if bit 2 is not
+ # defined, then 0d10 is invalid)
+ neg_value = None
+ if (
+ not ~cls._all_bits_ <= value <= cls._all_bits_
+ or value & (cls._all_bits_ ^ cls._flag_mask_)
+ ):
+ if cls._boundary_ is STRICT:
+ max_bits = max(value.bit_length(), cls._flag_mask_.bit_length())
+ raise ValueError(
+ "%s: invalid value: %r\n given %s\n allowed %s"
+ % (cls.__name__, value, bin(value, max_bits), bin(cls._flag_mask_, max_bits))
+ )
+ elif cls._boundary_ is CONFORM:
+ value = value & cls._flag_mask_
+ elif cls._boundary_ is EJECT:
+ return value
+ elif cls._boundary_ is KEEP:
+ if value < 0:
+ value = (
+ max(cls._all_bits_+1, 2**(value.bit_length()))
+ + value
+ )
+ else:
+ raise ValueError(
+ 'unknown flag boundary: %r' % (cls._boundary_, )
+ )
+ if value < 0:
+ neg_value = value
+ value = cls._all_bits_ + 1 + value
+ # get members and unknown
+ unknown = value & ~cls._flag_mask_
+ members = list(cls._iter_member_(value))
+ if unknown and cls._boundary_ is not KEEP:
+ raise ValueError(
+ '%s(%r) --> unknown values %r [%s]'
+ % (cls.__name__, value, unknown, bin(unknown))
+ )
+ # let class adjust values
+ values = cls._create_pseudo_member_values_(members, *values)
+ __new__ = getattr(cls, '__new_member__', None)
+ if cls._member_type_ is object and not __new__:
+ # construct a singleton enum pseudo-member
+ pseudo_member = object.__new__(cls)
+ else:
+ pseudo_member = (__new__ or cls._member_type_.__new__)(cls, *values)
+ if not hasattr(pseudo_member, 'value'):
+ pseudo_member._value_ = value
+ if members:
+ pseudo_member._name_ = '|'.join([m._name_ for m in members])
+ if unknown:
+ pseudo_member._name_ += '|%s' % cls._numeric_repr_(unknown)
+ else:
+ pseudo_member._name_ = None
+ # use setdefault in case another thread already created a composite
+ # with this value, but only if all members are known
+ # note: zero is a special case -- add it
+ if not unknown:
+ pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member)
+ if neg_value is not None:
+ cls._value2member_map_[neg_value] = pseudo_member
+ return pseudo_member
+
+
+ @classmethod
+ def _create_pseudo_member_values_(cls, members, *values):
+ """
+ Return values to be fed to __new__ to create new member.
+ """
+ if cls._member_type_ in (baseinteger + (object, )):
+ return values
+ elif len(values) < 2:
+ return values + (cls._member_type_(), )
+ else:
+ return values
+
+ def __contains__(self, other):
+ """
+ Returns True if self has at least the same flags set as other.
+ """
+ if not isinstance(other, self.__class__):
+ raise TypeError(
+ "unsupported operand type(s) for 'in': '%s' and '%s'" % (
+ type(other).__name__, self.__class__.__name__))
+ if other._value_ == 0 or self._value_ == 0:
+ return False
+ return other._value_ & self._value_ == other._value_
+
+ def __iter__(self):
+ """
+ Returns flags in definition order.
+ """
+ for member in self._iter_member_(self._value_):
+ yield member
+
+ def __len__(self):
+ return _bit_count(self._value_)
+
+ def __repr__(self):
+ cls = self.__class__
+ if self._name_ is None:
+ # only zero is unnamed by default
+ return '<%s: %r>' % (cls.__name__, self._value_)
+ else:
+ return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_)
+
+ def __str__(self):
+ cls = self.__class__
+ if self._name_ is None:
+ return '%s(%s)' % (cls.__name__, self._value_)
+ else:
+ return '%s.%s' % (cls.__name__, self._name_)
+
+ if PY2:
+ def __nonzero__(self):
+ return bool(self._value_)
+ else:
+ def __bool__(self):
+ return bool(self._value_)
+
+ def __or__(self, other):
+ if isinstance(other, self.__class__):
+ other_value = other._value_
+ elif self._member_type_ is not object and isinstance(other, self._member_type_):
+ other_value = other
+ else:
+ return NotImplemented
+ return self.__class__(self._value_ | other_value)
+
+ def __and__(self, other):
+ if isinstance(other, self.__class__):
+ other_value = other._value_
+ elif self._member_type_ is not object and isinstance(other, self._member_type_):
+ other_value = other
+ else:
+ return NotImplemented
+ return self.__class__(self._value_ & other_value)
+
+ def __xor__(self, other):
+ if isinstance(other, self.__class__):
+ other_value = other._value_
+ elif self._member_type_ is not object and isinstance(other, self._member_type_):
+ other_value = other
+ else:
+ return NotImplemented
+ return self.__class__(self._value_ ^ other_value)
+
+ def __invert__(self):
+ if self._inverted_ is None:
+ if self._boundary_ is KEEP:
+ # use all bits
+ self._inverted_ = self.__class__(~self._value_)
+ else:
+ # calculate flags not in this member
+ self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_)
+ self._inverted_._inverted_ = self
+ return self._inverted_
+
+ __ror__ = __or__
+ __rand__ = __and__
+ __rxor__ = __xor__
+
+
+
+class IntFlag(int, ReprEnum, Flag):
+ """Support for integer-based Flags"""
+
+ _boundary_ = EJECT
+
+
+def _high_bit(value):
+ """returns index of highest bit, or -1 if value is zero or negative"""
+ return value.bit_length() - 1
+
+def global_enum_repr(self):
+ """
+ use module.enum_name instead of class.enum_name
+
+ the module is the last module in case of a multi-module name
+ """
+ module = self.__class__.__module__.split('.')[-1]
+ return '%s.%s' % (module, self._name_)
+
+def global_flag_repr(self):
+ """
+ use module.flag_name instead of class.flag_name
+
+ the module is the last module in case of a multi-module name
+ """
+ module = self.__class__.__module__.split('.')[-1]
+ cls_name = self.__class__.__name__
+ if self._name_ is None:
+ return "%s.%s(%r)" % (module, cls_name, self._value_)
+ if _is_single_bit(self):
+ return '%s.%s' % (module, self._name_)
+ if self._boundary_ is not FlagBoundary.KEEP:
+ return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')])
+ else:
+ name = []
+ for n in self._name_.split('|'):
+ if n[0].isdigit():
+ name.append(n)
+ else:
+ name.append('%s.%s' % (module, n))
+ return '|'.join(name)
+
+def global_str(self):
+ """
+ use enum_name instead of class.enum_name
+ """
+ if self._name_ is None:
+ return "%s(%r)" % (cls_name, self._value_)
+ else:
+ return self._name_
+
+def global_enum(cls, update_str=False):
+ """
+ decorator that makes the repr() of an enum member reference its module
+ instead of its class; also exports all members to the enum's module's
+ global namespace
+ """
+ if issubclass(cls, Flag):
+ cls.__repr__ = global_flag_repr
+ else:
+ cls.__repr__ = global_enum_repr
+ if not issubclass(cls, ReprEnum) or update_str:
+ cls.__str__ = global_str
+ _sys.modules[cls.__module__].__dict__.update(cls.__members__)
+ return cls
+
+
+class module(object):
+
+ def __init__(self, cls, *args):
+ self.__name__ = cls.__name__
+ self._parent_module = cls.__module__
+ self.__all__ = []
+ all_objects = cls.__dict__
+ if not args:
+ args = [k for k, v in all_objects.items() if isinstance(v, (NamedConstant, Enum))]
+ for name in args:
+ self.__dict__[name] = all_objects[name]
+ self.__all__.append(name)
+
+ def register(self):
+ _sys.modules["%s.%s" % (self._parent_module, self.__name__)] = self
+
+if StdlibEnumMeta:
+
+ from _weakrefset import WeakSet
+
+ def __subclasscheck__(cls, subclass):
+ """
+ Override for issubclass(subclass, cls).
+ """
+ if not isinstance(subclass, type):
+ raise TypeError('issubclass() arg 1 must be a class (got %r)' % (subclass, ))
+ # Check cache
+ try:
+ cls.__dict__['_subclass_cache_']
+ except KeyError:
+ cls._subclass_cache_ = WeakSet()
+ cls._subclass_negative_cache_ = WeakSet()
+ except RecursionError:
+ import sys
+ exc, cls, tb = sys.exc_info()
+ exc = RecursionError('possible causes for endless recursion:\n - __getattribute__ is not ignoring __dunder__ attibutes\n - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n see `aenum.remove_stdlib_integration` for temporary work-around')
+ raise_from_none(exc)
+ if subclass in cls._subclass_cache_:
+ return True
+ # Check negative cache
+ elif subclass in cls._subclass_negative_cache_:
+ return False
+ if cls is subclass:
+ cls._subclass_cache_.add(subclass)
+ return True
+ # Check if it's a direct subclass
+ if cls in getattr(subclass, '__mro__', ()):
+ cls._subclass_cache_.add(subclass)
+ return True
+ # Check if it's an aenum.Enum|IntEnum|IntFlag|Flag subclass
+ if cls is StdlibIntFlag and issubclass(subclass, IntFlag):
+ cls._subclass_cache_.add(subclass)
+ return True
+ elif cls is StdlibFlag and issubclass(subclass, Flag):
+ cls._subclass_cache_.add(subclass)
+ return True
+ elif cls is StdlibIntEnum and issubclass(subclass, IntEnum):
+ cls._subclass_cache_.add(subclass)
+ return True
+ if cls is StdlibEnum and issubclass(subclass, Enum):
+ cls._subclass_cache_.add(subclass)
+ return True
+ # No dice; update negative cache
+ cls._subclass_negative_cache_.add(subclass)
+ return False
+
+ def __instancecheck__(cls, instance):
+ subclass = instance.__class__
+ try:
+ return cls.__subclasscheck__(subclass)
+ except RecursionError:
+ import sys
+ exc, cls, tb = sys.exc_info()
+ exc = RecursionError('possible causes for endless recursion:\n - __getattribute__ is not ignoring __dunder__ attibutes\n - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n see `aenum.remove_stdlib_integration` for temporary work-around')
+ raise_from_none(exc)
+
+ StdlibEnumMeta.__subclasscheck__ = __subclasscheck__
+ StdlibEnumMeta.__instancecheck__ = __instancecheck__
+
+def add_stdlib_integration():
+ if StdlibEnum:
+ StdlibEnumMeta.__subclasscheck__ = __subclasscheck__
+ StdlibEnumMeta.__instancecheck__ = __instancecheck__
+
+def remove_stdlib_integration():
+ """
+ Remove the __instancecheck__ and __subclasscheck__ overrides from the stdlib Enum.
+
+ Those overrides are in place so that code detecting stdlib enums will also detect
+ aenum enums. If a buggy __getattribute__, __instancecheck__, or __subclasscheck__
+ is defined on a custom EnumMeta then RecursionErrors can result; using this
+ function after importing aenum will solve that problem, but the better solution is
+ to fix the buggy method.
+ """
+ if StdlibEnum:
+ del StdlibEnumMeta.__instancecheck__
+ del StdlibEnumMeta.__subclasscheck__
+
diff --git a/venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 00000000..176f78e2
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/aenum/__pycache__/_py3.cpython-39.pyc b/venv/Lib/site-packages/aenum/__pycache__/_py3.cpython-39.pyc
new file mode 100644
index 00000000..c289c1bf
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/__pycache__/_py3.cpython-39.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/aenum/_py2.py b/venv/Lib/site-packages/aenum/_py2.py
new file mode 100644
index 00000000..a6e7b290
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/_py2.py
@@ -0,0 +1,7 @@
+from operator import div as _div_
+from inspect import getargspec
+
+def raise_with_traceback(exc, tb):
+ raise exc, None, tb
+
+__all__ = ['_div_', 'getargspec', 'raise_with_traceback']
diff --git a/venv/Lib/site-packages/aenum/_py3.py b/venv/Lib/site-packages/aenum/_py3.py
new file mode 100644
index 00000000..2edeefdf
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/_py3.py
@@ -0,0 +1,12 @@
+from inspect import getfullargspec as _getfullargspec
+
+def getargspec(method):
+ args, varargs, keywords, defaults, _, _, _ = _getfullargspec(method)
+ return args, varargs, keywords, defaults
+
+def raise_with_traceback(exc, tb):
+ raise exc.with_traceback(tb)
+
+def raise_from_none(exc):
+ raise exc from None
+
diff --git a/venv/Lib/site-packages/aenum/doc/aenum.rst b/venv/Lib/site-packages/aenum/doc/aenum.rst
new file mode 100644
index 00000000..42ef0d3c
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/doc/aenum.rst
@@ -0,0 +1,1568 @@
+``aenum`` --- support for advanced enumerations, namedtuples, and constants
+===========================================================================
+
+.. :synopsis:: enumerations are sets of symbolic names bound to unique,
+ constant values; namedtuples are fixed- or variable-length
+ tuples with the positions addressable by field name as well as by index;
+ constants are classes of named constants that cannot be rebound.
+.. :moduleauthor:: Ethan Furman <ethan@stoneleaf.us>
+
+----------------
+
+An ``Enum`` is a set of symbolic names (members) bound to unique, constant
+values. Within an enumeration, the members can be compared by identity, and
+the enumeration itself can be iterated over.
+
+A ``NamedTuple`` is a class-based, fixed-length tuple with a name for each
+possible position accessible using attribute-access notation.
+
+A ``NamedConstant`` is a class whose members cannot be rebound; it lacks all
+other ``Enum`` capabilities, however; consequently, it can have duplicate
+values. There is also a ``module`` function that can insert the
+``NamedConstant`` class into ``sys.modules`` where it will appear to be a
+module whose top-level names cannot be rebound.
+
+.. note::
+ ``constant`` refers to names not being rebound; mutable objects can be
+ mutated.
+
+
+Module Contents
+---------------
+
+This module defines five enumeration classes that can be used to define unique
+sets of names and values, one ``Enum`` class decorator, one ``NamedTuple``
+class, one ``NamedConstant`` class, and several helpers.
+
+``NamedConstant``
+
+NamedConstant class for creating groups of constants. These names cannot be
+rebound to other values.
+
+``Enum``
+
+Base class for creating enumerated constants. See section `Enum Functional API`_
+for an alternate construction syntax.
+
+``AddValue``
+
+Flag specifying that ``_generate_next_value_`` should always be called to
+provide the initial value for an enum member.
+
+``MultiValue``
+
+Flag specifying that each item of tuple value is a separate value for that
+member; the first tuple item is the canonical one.
+
+``NoAlias``
+
+Flag specifying that duplicate valued members are distinct and not aliases;
+by-value lookups are disabled.
+
+``Unique``
+
+Flag specifying that duplicate valued members are not allowed.
+
+.. note::
+ The flags are inherited by the enumeration's subclasses. To use them in
+ Python 2 assign to ``_settings_`` in the class body.
+
+``IntEnum``
+
+Base class for creating enumerated constants that are also subclasses of ``int``.
+
+``AutoNumberEnum``
+
+Derived class that automatically assigns an ``int`` value to each member.
+
+``OrderedEnum``
+
+Derived class that adds ``<``, ``<=``, ``>=``, and ``>`` methods to an ``Enum``.
+
+``UniqueEnum``
+
+Derived class that ensures only one name is bound to any one value.
+
+``unique``
+
+Enum class decorator that ensures only one name is bound to any one value.
+
+.. note::
+
+ the ``UniqueEnum`` class, the ``unique`` decorator, and the Unique
+ flag all do the same thing; you do not need to use more than one of
+ them at the same time.
+
+``NamedTuple``
+
+Base class for `creating NamedTuples`_, either by subclassing or via it's
+functional API.
+
+``constant``
+
+Descriptor to add constant values to an ``Enum``, or advanced constants to
+``NamedConstant``.
+
+``convert``
+
+Helper to transform target global variables into an ``Enum``.
+
+``enum``
+
+Helper for specifying keyword arguments when creating ``Enum`` members.
+
+``export``
+
+Helper for inserting ``Enum`` members and ``NamedConstant`` constants into a
+namespace (usually ``globals()``.
+
+``extend_enum``
+
+Helper for adding new ``Enum`` members, both stdlib and aenum.
+
+``module``
+
+Function to take a ``NamedConstant`` or ``Enum`` class and insert it into
+``sys.modules`` with the affect of a module whose top-level constant and
+member names cannot be rebound.
+
+``skip``
+
+Descriptor to add a normal (non-``Enum`` member) attribute to an ``Enum``
+or ``NamedConstant``.
+
+
+Creating an Enum
+----------------
+
+Enumerations are created using the ``class`` syntax, which makes them
+easy to read and write. An alternative creation method is described in
+`Enum Functional API`_. To define an enumeration, subclass ``Enum`` as
+follows::
+
+ >>> from aenum import Enum
+ >>> class Color(Enum):
+ ... red = 1
+ ... green = 2
+ ... blue = 3
+
+*Nomenclature*
+
+ - The class ``Color`` is an *enumeration* (or *enum*)
+ - The attributes ``Color.red``, ``Color.green``, etc., are
+ *enumeration members* (or *enum members*).
+ - The enum members have *names* and *values* (the name of
+ ``Color.red`` is ``red``, the value of ``Color.blue`` is
+ ``3``, etc.)
+
+.. note::
+
+ Even though we use the ``class`` syntax to create Enums, Enums
+ are not normal Python classes. See `How are Enums different?`_ for
+ more details.
+
+Enumeration members have human readable string representations::
+
+ >>> print(Color.red)
+ Color.red
+
+...while their ``repr`` has more information::
+
+ >>> print(repr(Color.red))
+ <Color.red: 1>
+
+The *type* of an enumeration member is the enumeration it belongs to::
+
+ >>> type(Color.red)
+ <aenum 'Color'>
+ >>> isinstance(Color.green, Color)
+ True
+
+Enumerations support iteration. In Python 3.x definition order is used; in
+Python 2.x the definition order is not available, but class attribute
+``_order_`` is supported; otherwise, value order is used if posible,
+otherwise alphabetical name order is used::
+
+ >>> class Shake(Enum):
+ ... _order_ = 'vanilla chocolate cookies mint' # only needed in 2.x
+ ... vanilla = 7
+ ... chocolate = 4
+ ... cookies = 9
+ ... mint = 3
+ ...
+ >>> for shake in Shake:
+ ... print(shake)
+ ...
+ Shake.vanilla
+ Shake.chocolate
+ Shake.cookies
+ Shake.mint
+
+The ``_order_`` attribute is always removed, but in 3.x it is also used to
+verify that definition order is the same (useful for py2&3 code bases);
+however, in the stdlib version it will be ignored and not removed.
+
+.. note::
+
+ To maintain compatibility with Python 3.4 and 3.5, use __order__
+ instead (double leading and trailing underscores).
+
+Enumeration members are hashable, so they can be used in dictionaries and sets::
+
+ >>> apples = {}
+ >>> apples[Color.red] = 'red delicious'
+ >>> apples[Color.green] = 'granny smith'
+ >>> apples == {Color.red: 'red delicious', Color.green: 'granny smith'}
+ True
+
+In Python 3 the class syntax has a few extra advancements::
+
+ --> class Color(
+ ... Enum,
+ ... settings=(AddValue, MultiValue, NoAlias, Unique),
+ ... init='field_name1 field_name2 ...',
+ ... start=7,
+ ... )
+ ...
+
+``start`` is used to specify the starting value for the first member::
+
+ --> class Count(Enum, start=11):
+ ... eleven
+ ... twelve
+ ...
+ --> Count.twelve.value == 12
+ True
+
+``init`` specifies the attribute names to store creation values to::
+
+ --> class Planet(Enum, init='mass radius'):
+ ... MERCURY = (3.303e+23, 2.4397e6)
+ ... EARTH = (5.976e+24, 6.37814e6)
+ ...
+ --> Planet.EARTH.value
+ (5.976e+24, 6378140.0)
+ --> Planet.EARTH.radius
+ 2.4397e6
+
+The various settings enable special behavior:
+
+- ``AddValue`` calls a user supplied ``_generate_next_value_`` to provide
+ the initial value
+- ``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
+
+.. note::
+
+ To use these features in Python 2 use the _sundered_ versions of
+ the names in the class body: ``_start_``, ``_init_``, ``_settings_``.
+
+
+Programmatic access to enumeration members and their attributes
+---------------------------------------------------------------
+
+Sometimes it's useful to access members in enumerations programmatically (i.e.
+situations where ``Color.red`` won't do because the exact color is not known
+at program-writing time). ``Enum`` allows such access::
+
+ >>> Color(1)
+ <Color.red: 1>
+ >>> Color(3)
+ <Color.blue: 3>
+
+If you want to access enum members by *name*, use item access::
+
+ >>> Color['red']
+ <Color.red: 1>
+ >>> Color['green']
+ <Color.green: 2>
+
+If have an enum member and need its ``name`` or ``value``::
+
+ >>> member = Color.red
+ >>> member.name
+ 'red'
+ >>> member.value
+ 1
+
+
+Duplicating enum members and values
+-----------------------------------
+
+Having two enum members (or any other attribute) with the same name is invalid;
+in Python 3.x this would raise an error, but in Python 2.x the second member
+simply overwrites the first::
+
+ # python 2.x
+ --> class Shape(Enum):
+ ... square = 2
+ ... square = 3
+ ...
+ --> Shape.square
+ <Shape.square: 3>
+
+ # python 3.x
+ --> class Shape(Enum):
+ ... square = 2
+ ... square = 3
+ Traceback (most recent call last):
+ ...
+ TypeError: Attempted to reuse key: 'square'
+
+However, two enum members are allowed to have the same value. Given two members
+A and B with the same value (and A defined first), B is an alias to A. By-value
+lookup of the value of A and B will return A. By-name lookup of B will also
+return A::
+
+ >>> class Shape(Enum):
+ ... _order_ = 'square diamond circle' # needed in 2.x
+ ... square = 2
+ ... diamond = 1
+ ... circle = 3
+ ... alias_for_square = 2
+ ...
+ >>> Shape.square
+ <Shape.square: 2>
+ >>> Shape.alias_for_square
+ <Shape.square: 2>
+ >>> Shape(2)
+ <Shape.square: 2>
+
+
+Allowing aliases is not always desirable. ``unique`` can be used to ensure
+that none exist in a particular enumeration::
+
+ >>> from aenum import unique
+ >>> @unique
+ ... class Mistake(Enum):
+ ... _order_ = 'one two three' # only needed in 2.x
+ ... one = 1
+ ... two = 2
+ ... three = 3
+ ... four = 3
+ Traceback (most recent call last):
+ ...
+ ValueError: duplicate names found in <aenum 'Mistake'>: four -> three
+
+Iterating over the members of an enum does not provide the aliases::
+
+ >>> list(Shape)
+ [<Shape.square: 2>, <Shape.diamond: 1>, <Shape.circle: 3>]
+
+The special attribute ``__members__`` is a dictionary mapping names to members.
+It includes all names defined in the enumeration, including the aliases::
+
+ >>> for name, member in sorted(Shape.__members__.items()):
+ ... name, member
+ ...
+ ('alias_for_square', <Shape.square: 2>)
+ ('circle', <Shape.circle: 3>)
+ ('diamond', <Shape.diamond: 1>)
+ ('square', <Shape.square: 2>)
+
+The ``__members__`` attribute can be used for detailed programmatic access to
+the enumeration members. For example, finding all the aliases::
+
+ >>> [n for n, mbr in Shape.__members__.items() if mbr.name != n]
+ ['alias_for_square']
+
+Comparisons
+-----------
+
+Enumeration members are compared by identity::
+
+ >>> Color.red is Color.red
+ True
+ >>> Color.red is Color.blue
+ False
+ >>> Color.red is not Color.blue
+ True
+
+Ordered comparisons between enumeration values are *not* supported. Enum
+members are not integers (but see `IntEnum`_ below)::
+
+ >>> Color.red < Color.blue
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ TypeError: unorderable types: Color() < Color()
+
+.. warning::
+
+ In Python 2 *everything* is ordered, even though the ordering may not
+ make sense. If you want your enumerations to have a sensible ordering
+ consider using an `OrderedEnum`_.
+
+
+Equality comparisons are defined though::
+
+ >>> Color.blue == Color.red
+ False
+ >>> Color.blue != Color.red
+ True
+ >>> Color.blue == Color.blue
+ True
+
+Comparisons against non-enumeration values will always compare not equal
+(again, ``IntEnum`` was explicitly designed to behave differently, see
+below)::
+
+ >>> Color.blue == 2
+ False
+
+
+Allowed members and attributes of enumerations
+----------------------------------------------
+
+The examples above use integers for enumeration values. Using integers is
+short and handy (and provided by default by the `Enum Functional API`_), but not
+strictly enforced. In the vast majority of use-cases, one doesn't care what
+the actual value of an enumeration is. But if the value *is* important,
+enumerations can have arbitrary values.
+
+Enumerations are Python classes, and can have methods and special methods as
+usual. If we have this enumeration::
+
+ >>> class Mood(Enum):
+ ... funky = 1
+ ... happy = 3
+ ...
+ ... def describe(self):
+ ... # self is the member here
+ ... return self.name, self.value
+ ...
+ ... def __str__(self):
+ ... return 'my custom str! {0}'.format(self.value)
+ ...
+ ... @classmethod
+ ... def favorite_mood(cls):
+ ... # cls here is the enumeration
+ ... return cls.happy
+
+Then::
+
+ >>> Mood.favorite_mood()
+ <Mood.happy: 3>
+ >>> Mood.happy.describe()
+ ('happy', 3)
+ >>> str(Mood.funky)
+ 'my custom str! 1'
+
+The rules for what is allowed are as follows: _sunder_ names (starting and
+ending with a single underscore) are reserved by enum and cannot be used;
+all other attributes defined within an enumeration will become members of this
+enumeration, with the exception of *__dunder__* names and descriptors (methods
+are also descriptors).
+
+.. note::
+
+ If your enumeration defines ``__new__`` and/or ``__init__`` then
+ whatever value(s) were given to the enum member will be passed into
+ those methods. See `Planet`_ for an example.
+
+
+Restricted Enum subclassing
+---------------------------
+
+A new `Enum` class must have one base Enum class, up to one concrete
+data type, and as many `object`-based mixin classes as needed. The
+order of these base classes is::
+
+ def EnumName([mix-in, ...,] [data-type,] base-enum):
+ pass
+
+Also, subclassing an enumeration is allowed only if the enumeration does not define
+
+any members. So this is forbidden::
+
+ >>> class MoreColor(Color):
+ ... pink = 17
+ Traceback (most recent call last):
+ ...
+ TypeError: <aenum 'MoreColor'> cannot extend <aenum 'Color'>
+
+But this is allowed::
+
+ >>> class Foo(Enum):
+ ... def some_behavior(self):
+ ... pass
+ ...
+ >>> class Bar(Foo):
+ ... happy = 1
+ ... sad = 2
+ ...
+
+Allowing subclassing of enums that define members would lead to a violation of
+some important invariants of types and instances. On the other hand, it makes
+sense to allow sharing some common behavior between a group of enumerations.
+(See `OrderedEnum`_ for an example.)
+
+
+Pickling
+--------
+
+Enumerations can be pickled and unpickled::
+
+ >>> from aenum.test import Fruit
+ >>> from pickle import dumps, loads
+ >>> Fruit.tomato is loads(dumps(Fruit.tomato, 2))
+ True
+
+The usual restrictions for pickling apply: picklable enums must be defined in
+the top level of a module, since unpickling requires them to be importable
+from that module.
+
+.. note::
+
+ With pickle protocol version 4 (introduced in Python 3.4) it is possible
+ to easily pickle enums nested in other classes.
+
+
+
+Enum Functional API
+-------------------
+
+The ``Enum`` class is callable, providing the following functional API::
+
+ >>> Animal = Enum('Animal', 'ant bee cat dog')
+ >>> Animal
+ <aenum 'Animal'>
+ >>> Animal.ant
+ <Animal.ant: 1>
+ >>> Animal.ant.value
+ 1
+ >>> list(Animal)
+ [<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]
+
+The semantics of this API resemble ``namedtuple``. The first argument
+of the call to ``Enum`` is the name of the enumeration.
+
+The second argument is the *source* of enumeration member names. It can be a
+whitespace-separated string of names, a sequence of names, a sequence of
+2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to
+values. The last two options enable assigning arbitrary values to
+enumerations; the others auto-assign increasing integers starting with 1. A
+new class derived from ``Enum`` is returned. In other words, the above
+assignment to ``Animal`` is equivalent to::
+
+ >>> class Animals(Enum):
+ ... ant = 1
+ ... bee = 2
+ ... cat = 3
+ ... dog = 4
+
+Pickling enums created with the functional API can be tricky as frame stack
+implementation details are used to try and figure out which module the
+enumeration is being created in (e.g. it will fail if you use a utility
+function in separate module, and also may not work on IronPython or Jython).
+The solution is to specify the module name explicitly as follows::
+
+ >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__)
+
+Derived Enumerations
+--------------------
+
+IntEnum
+^^^^^^^
+
+A variation of ``Enum`` is provided which is also a subclass of
+``int``. Members of an ``IntEnum`` can be compared to integers;
+by extension, integer enumerations of different types can also be compared
+to each other::
+
+ >>> from aenum import IntEnum
+ >>> class Shape(IntEnum):
+ ... circle = 1
+ ... square = 2
+ ...
+ >>> class Request(IntEnum):
+ ... post = 1
+ ... get = 2
+ ...
+ >>> Shape == 1
+ False
+ >>> Shape.circle == 1
+ True
+ >>> Shape.circle == Request.post
+ True
+
+However, they still can't be compared to standard ``Enum`` enumerations::
+
+ >>> class Shape(IntEnum):
+ ... circle = 1
+ ... square = 2
+ ...
+ >>> class Color(Enum):
+ ... red = 1
+ ... green = 2
+ ...
+ >>> Shape.circle == Color.red
+ False
+
+``IntEnum`` values behave like integers in other ways you'd expect::
+
+ >>> int(Shape.circle)
+ 1
+ >>> ['a', 'b', 'c'][Shape.circle]
+ 'b'
+ >>> [i for i in range(Shape.square)]
+ [0, 1]
+
+For the vast majority of code, ``Enum`` is strongly recommended,
+since ``IntEnum`` breaks some semantic promises of an enumeration (by
+being comparable to integers, and thus by transitivity to other
+unrelated enumerations). It should be used only in special cases where
+there's no other choice; for example, when integer constants are
+replaced with enumerations and backwards compatibility is required with code
+that still expects integers.
+
+
+IntFlag
+^^^^^^^
+
+The next variation of ``Enum`` provided, ``IntFlag``, is also based
+on ``int``. The difference being ``IntFlag`` members can be combined
+using the bitwise operators (&, \|, ^, ~) and the result is still an
+``IntFlag`` member. However, as the name implies, ``IntFlag``
+members also subclass ``int`` and can be used wherever an ``int`` is
+used. Any operation on an ``IntFlag`` member besides the bit-wise
+operations will lose the ``IntFlag`` membership.
+
+Sample ``IntFlag`` class::
+
+ >>> from aenum import IntFlag
+ >>> class Perm(IntFlag):
+ ... _order_ = 'R W X'
+ ... R = 4
+ ... W = 2
+ ... X = 1
+ ...
+ >>> Perm.R | Perm.W
+ <Perm.R|W: 6>
+ >>> Perm.R + Perm.W
+ 6
+ >>> RW = Perm.R | Perm.W
+ >>> Perm.R in RW
+ True
+
+It is also possible to name the combinations::
+
+ >>> class Perm(IntFlag):
+ ... _order_ = 'R W X'
+ ... R = 4
+ ... W = 2
+ ... X = 1
+ ... RWX = 7
+ >>> Perm.RWX
+ <Perm.RWX: 7>
+ >>> ~Perm.RWX
+ <Perm: 0>
+
+Another important difference between ``IntFlag`` and ``Enum`` is that
+if no flags are set (the value is 0), its boolean evaluation is ``False``::
+
+ >>> Perm.R & Perm.X
+ <Perm: 0>
+ >>> bool(Perm.R & Perm.X)
+ False
+
+Because ``IntFlag`` members are also subclasses of ``int`` they can
+be combined with them::
+
+ >>> Perm.X | 4
+ <Perm.R|X: 5>
+
+If the result is not a ``Flag`` then, depending on the ``_boundary_`` setting,
+an exception is raised (``STRICT``), the extra bits are lost (``CONFORM``), or
+it reverts to an int (``EJECT``):
+
+ >>> from aenum import STRICT, CONFORM, EJECT
+ >>> Perm._boundary_ = STRICT
+ >>> Perm.X | 8
+ Traceback (most recent call last):
+ ...
+ ValueError: Perm: invalid value: 9
+ given 0b0 1001
+ allowed 0b0 0111
+
+ >>> Perm._boundary_ = EJECT
+ >>> Perm.X | 8
+ 9
+
+ >>> Perm._boundary_ = CONFORM
+ >>> Perm.X | 8
+ <Perm.X: 1>
+
+
+Flag
+^^^^
+
+The last variation is ``Flag``. Like ``IntFlag``, ``Flag``
+members can be combined using the bitwise operators (&, \|, ^, ~). Unlike
+``IntFlag``, they cannot be combined with, nor compared against, any
+other ``Flag`` enumeration, nor ``int``. While it is possible to
+specify the values directly it is recommended to use ``auto`` as the
+value and let ``Flag`` select an appropriate value.
+
+Like ``IntFlag``, if a combination of ``Flag`` members results in no
+flags being set, the boolean evaluation is ``False``::
+
+ >>> from aenum import Flag, auto
+ >>> class Color(Flag):
+ ... RED = auto()
+ ... BLUE = auto()
+ ... GREEN = auto()
+ ...
+ >>> Color.RED & Color.GREEN
+ <Color: 0>
+ >>> bool(Color.RED & Color.GREEN)
+ False
+
+Individual flags should have values that are powers of two (1, 2, 4, 8, ...),
+while combinations of flags won't::
+
+ --> class Color(Flag):
+ ... RED = auto()
+ ... BLUE = auto()
+ ... GREEN = auto()
+ ... WHITE = RED | BLUE | GREEN
+ ...
+ --> Color.WHITE
+ <Color.WHITE: 7>
+
+Giving a name to the "no flags set" condition does not change its boolean
+value::
+
+ >>> class Color(Flag):
+ ... BLACK = 0
+ ... RED = auto()
+ ... BLUE = auto()
+ ... GREEN = auto()
+ ...
+ >>> Color.BLACK
+ <Color.BLACK: 0>
+ >>> bool(Color.BLACK)
+ False
+
+Flags can be iterated over to retrieve the individual truthy flags in the value::
+
+ >>> class Color(Flag):
+ ... _order_ = 'BLACK RED BLUE GREEN WHITE'
+ ... BLACK = 0
+ ... RED = auto()
+ ... BLUE = auto()
+ ... GREEN = auto()
+ ... WHITE = RED | BLUE | GREEN
+ ...
+ >>> list(Color.GREEN)
+ [<Color.GREEN: 4>]
+ >>> list(Color.WHITE)
+ [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 4>]
+
+.. note::
+
+ For the majority of new code, ``Enum`` and ``Flag`` are strongly
+ recommended, since ``IntEnum`` and ``IntFlag`` break some
+ semantic promises of an enumeration (by being comparable to integers, and
+ thus by transitivity to other unrelated enumerations). ``IntEnum``
+ and ``IntFlag`` should be used only in cases where ``Enum`` and
+ ``Flag`` will not do; for example, when integer constants are replaced
+ with enumerations, or for interoperability with other systems.
+
+
+Others
+^^^^^^
+
+While ``IntEnum`` is part of the ``aenum`` module, it would be very
+simple to implement independently::
+
+ class MyIntEnum(int, Enum):
+ pass
+
+This demonstrates how similar derived enumerations can be defined; for example
+a ``MyStrEnum`` that mixes in ``str`` instead of ``int``.
+
+Some rules:
+
+1. When subclassing ``Enum``, mix-in types must appear before
+ ``Enum`` itself in the sequence of bases, as in the ``MyIntEnum``
+ example above.
+2. While ``Enum`` can have members of any type, once you mix in an
+ additional type, all the members must have values of that type or be
+ convertible into that type. This restriction does not apply to mix-ins
+ which only add methods and don't specify another data type.
+3. When another data type is mixed in, the ``value`` attribute is *not the
+ same* as the enum member itself, although it is equivalant and will compare
+ equal.
+4. %-style formatting: ``%s`` and ``%r`` call ``Enum``'s ``__str__`` and
+ ``__repr__`` respectively; other codes (such as ``%i`` or ``%h`` for
+ MyIntEnum) treat the enum member as its mixed-in type.
+5. ``str.__format__`` (or ``format``) will use the mixed-in
+ type's ``__format__``. If the ``Enum``'s ``str`` or ``repr`` is desired
+ use the ``!s`` or ``!r`` ``str`` format codes.
+
+.. note::
+
+ If you override the ``__str__`` method, then it will be used to provide the
+ string portion of the ``format()`` call.
+
+.. note::
+
+ Prior to Python 3.4 there is a bug in ``str``'s %-formatting: ``int``
+ subclasses are printed as strings and not numbers when the ``%d``, ``%i``,
+ or ``%u`` codes are used.
+
+
+Extra Goodies
+-------------
+
+aenum supports a few extra techniques not found in the stdlib version.
+
+enum
+^^^^
+
+If you have several items to initialize your ``Enum`` members with and
+would like to use keyword arguments, the ``enum`` helper is for you::
+
+ >>> from aenum import enum
+ >>> class Presidents(Enum):
+ ... Washington = enum('George Washington', circa=1776, death=1797)
+ ... Jackson = enum('Andrew Jackson', circa=1830, death=1837)
+ ... Lincoln = enum('Abraham Lincoln', circa=1860, death=1865)
+ ...
+ >>> Presidents.Lincoln
+ <Presidents.Lincoln: enum('Abraham Lincoln', circa=1860, death=1865)>
+
+extend_enum
+^^^^^^^^^^^
+
+For those rare cases when you need to create your ``Enum`` in pieces, you
+can use ``extend_enum`` to add new members after the initial creation
+(the new member is returned)::
+
+ >>> from aenum import extend_enum
+ >>> class Color(Enum):
+ ... red = 1
+ ... green = 2
+ ... blue = 3
+ ...
+ >>> list(Color)
+ [<Color.red: 1>, <Color.green: 2>, <Color.blue: 3>]
+ >>> extend_enum(Color, 'opacity', 4)
+ <Color.opacity: 4>
+ >>> list(Color)
+ [<Color.red: 1>, <Color.green: 2>, <Color.blue: 3>, <Color.opacity: 4>]
+ >>> Color.opacity in Color
+ True
+ >>> Color.opacity.name == 'opacity'
+ True
+ >>> Color.opacity.value == 4
+ True
+ >>> Color(4)
+ <Color.opacity: 4>
+ >>> Color['opacity']
+ <Color.opacity: 4>
+
+ --> Color.__members__
+ OrderedDict([
+ ('red', <Color.red: 1>),
+ ('green', <Color.green: 2>),
+ ('blue', <Color.blue: 3>),
+ ('opacity', <Color.opacity: 4>)
+ ])
+
+constant
+^^^^^^^^
+
+If you need to have some constant value in your ``Enum`` that isn't a member,
+use ``constant``::
+
+ >>> from aenum import constant
+ >>> class Planet(Enum):
+ ... MERCURY = (3.303e+23, 2.4397e6)
+ ... EARTH = (5.976e+24, 6.37814e6)
+ ... JUPITER = (1.9e+27, 7.1492e7)
+ ... URANUS = (8.686e+25, 2.5559e7)
+ ... G = constant(6.67300E-11)
+ ... def __init__(self, mass, radius):
+ ... self.mass = mass # in kilograms
+ ... self.radius = radius # in meters
+ ... @property
+ ... def surface_gravity(self):
+ ... # universal gravitational constant (m3 kg-1 s-2)
+ ... return self.G * self.mass / (self.radius * self.radius)
+ ...
+ >>> Planet.EARTH.value
+ (5.976e+24, 6378140.0)
+ >>> Planet.EARTH.surface_gravity
+ 9.802652743337129
+ >>> Planet.G
+ 6.673e-11
+ >>> Planet.G = 9
+ Traceback (most recent call last):
+ ...
+ AttributeError: Planet: cannot rebind constant 'G'
+
+skip
+^^^^
+
+If you need a standard attribute that is not converted into an ``Enum``
+member, use ``skip``::
+
+ >>> from aenum import skip
+ >>> class Color(Enum):
+ ... red = 1
+ ... green = 2
+ ... blue = 3
+ ... opacity = skip(0.45)
+ ...
+ >>> Color.opacity
+ 0.45
+ >>> Color.opacity = 0.77
+ >>> Color.opacity
+ 0.77
+
+start
+^^^^^
+
+``start`` can be used to turn on auto-numbering (useful for when you don't
+care which numbers are assigned as long as they are consistent and in order)
+The Python 3 version can look like this::
+
+ >>> class Color(Enum, start=1): # doctest: +SKIP
+ ... red, green, blue
+ ...
+ >>> Color.blue
+ <Color.blue: 3>
+
+This can also be done in Python 2, albeit not as elegantly (this also works in
+Python 3)::
+
+ >>> class Color(Enum): # doctest: +SKIP
+ ... _start_ = 1
+ ... red = auto()
+ ... green = auto()
+ ... blue = auto()
+ ...
+ >>> Color.blue
+ <Color.blue: 3>
+
+init
+^^^^
+
+If you need an ``__init__`` method that does nothing besides save its
+arguments, ``init`` is for you::
+
+ >>> class Planet(Enum, init='mass radius'): # doctest: +SKIP
+ ... MERCURY = (3.303e+23, 2.4397e6)
+ ... EARTH = (5.976e+24, 6.37814e6)
+ ... JUPITER = (1.9e+27, 7.1492e7)
+ ... URANUS = (8.686e+25, 2.5559e7)
+ ... G = constant(6.67300E-11)
+ ... @property
+ ... def surface_gravity(self):
+ ... # universal gravitational constant (m3 kg-1 s-2)
+ ... return self.G * self.mass / (self.radius * self.radius)
+ ...
+ >>> Planet.JUPITER.value
+ (1.9e+27, 71492000.0)
+ >>> Planet.JUPITER.mass
+ 1.9e+27
+
+.. note::
+
+ Just as with ``start`` above, in Python 2 you must put the keyword as a
+ _sunder_ in the class body -- ``_init_ = 'mass radius'``.
+
+init and missing values
+^^^^^^^^^^^^^^^^^^^^^^^
+
+If ``_init_`` calls for values that are not supplied, ``_generate_next_value_``
+will be called in an effort to generate them. Here is an example in Python 2::
+
+ >>> from aenum import Enum
+ >>> 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
+ ... 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')>
+ >>> NotificationType.B.db
+ 'B'
+ >>> NotificationType.B.user
+ 'Both'
+
+combining Flag with other data types
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Flag does support being combined with other data types. To support this you
+need to provide a ``_create_pseudo_member_values_`` method which will be called
+with the members in a composite flag. You may also need to provide a custom
+``__new__`` method::
+
+ >>> class AnsiFlag(str, Flag):
+ ... def __new__(cls, value, code):
+ ... str_value = '\x1b[%sm' % code
+ ... obj = str.__new__(cls, str_value)
+ ... obj._value_ = value
+ ... obj.code = code
+ ... return obj
+ ... @classmethod
+ ... def _create_pseudo_member_values_(cls, members, *values):
+ ... code = ';'.join(m.code for m in members)
+ ... return values + (code, )
+ ... _order_ = 'FG_Red FG_Green BG_Magenta BG_White'
+ ... FG_Red = '31' # ESC [ 31 m # red
+ ... FG_Green = '32' # ESC [ 32 m # green
+ ... BG_Magenta = '45' # ESC [ 35 m # magenta
+ ... BG_White = '47' # ESC [ 37 m # white
+ ...
+ >>> color = AnsiFlag.BG_White | AnsiFlag.FG_Red
+ >>> repr(color)
+ '<AnsiFlag.FG_Red|BG_White: 9>'
+ >>> str.__repr__(color)
+ "'\\x1b[31;47m'"
+
+.. note::
+
+ If you do not provide your own ``_create_pseudo_member_values_`` the flags
+ may still combine, but may be missing functionality.
+
+
+Decorators
+----------
+
+unique
+^^^^^^
+
+A ``class`` decorator specifically for enumerations. It searches an
+enumeration's ``__members__`` gathering any aliases it finds; if any are
+found ``ValueError`` is raised with the details::
+
+ >>> @unique
+ ... class NoDupes(Enum):
+ ... first = 'one'
+ ... second = 'two'
+ ... third = 'two'
+ Traceback (most recent call last):
+ ...
+ ValueError: duplicate names found in <aenum 'NoDupes'>: third -> second
+
+
+Interesting examples
+--------------------
+
+While ``Enum`` and ``IntEnum`` are expected to cover the majority of
+use-cases, they cannot cover them all. Here are recipes for some different
+types of enumerations that can be used directly (the first three are included
+in the module), or as examples for creating one's own.
+
+
+AutoNumber
+^^^^^^^^^^
+
+Avoids having to specify the value for each enumeration member::
+
+ >>> class AutoNumber(Enum):
+ ... def __new__(cls):
+ ... value = len(cls.__members__) + 1
+ ... obj = object.__new__(cls)
+ ... obj._value_ = value
+ ... return obj
+ ...
+ >>> class Color(AutoNumber):
+ ... _order_ = "red green blue" # only needed in 2.x
+ ... red = ()
+ ... green = ()
+ ... blue = ()
+ ...
+ >>> Color.green.value == 2
+ True
+
+.. note::
+
+ The `__new__` method, if defined, is used during creation of the Enum
+ members; it is then replaced by Enum's `__new__` which is used after
+ class creation for lookup of existing members. Due to the way Enums are
+ supposed to behave, there is no way to customize Enum's `__new__` without
+ modifying the class after it is created.
+
+
+UniqueEnum
+^^^^^^^^^^
+
+Raises an error if a duplicate member name is found instead of creating an
+alias::
+
+ >>> class UniqueEnum(Enum):
+ ... def __init__(self, *args):
+ ... cls = self.__class__
+ ... if any(self.value == e.value for e in cls):
+ ... a = self.name
+ ... e = cls(self.value).name
+ ... raise ValueError(
+ ... "aliases not allowed in UniqueEnum: %r --> %r"
+ ... % (a, e))
+ ...
+ >>> class Color(UniqueEnum):
+ ... _order_ = 'red green blue'
+ ... red = 1
+ ... green = 2
+ ... blue = 3
+ ... grene = 2
+ Traceback (most recent call last):
+ ...
+ ValueError: aliases not allowed in UniqueEnum: 'grene' --> 'green'
+
+
+OrderedEnum
+^^^^^^^^^^^
+
+An ordered enumeration that is not based on ``IntEnum`` and so maintains
+the normal ``Enum`` invariants (such as not being comparable to other
+enumerations)::
+
+ >>> class OrderedEnum(Enum):
+ ... def __ge__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self._value_ >= other._value_
+ ... return NotImplemented
+ ... def __gt__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self._value_ > other._value_
+ ... return NotImplemented
+ ... def __le__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self._value_ <= other._value_
+ ... return NotImplemented
+ ... def __lt__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self._value_ < other._value_
+ ... return NotImplemented
+ ...
+ >>> class Grade(OrderedEnum):
+ ... __ordered__ = 'A B C D F'
+ ... A = 5
+ ... B = 4
+ ... C = 3
+ ... D = 2
+ ... F = 1
+ ...
+ >>> Grade.C < Grade.A
+ True
+
+
+Planet
+^^^^^^
+
+If ``__new__`` or ``__init__`` is defined the value of the enum member
+will be passed to those methods::
+
+ >>> class Planet(Enum):
+ ... MERCURY = (3.303e+23, 2.4397e6)
+ ... VENUS = (4.869e+24, 6.0518e6)
+ ... EARTH = (5.976e+24, 6.37814e6)
+ ... MARS = (6.421e+23, 3.3972e6)
+ ... JUPITER = (1.9e+27, 7.1492e7)
+ ... SATURN = (5.688e+26, 6.0268e7)
+ ... URANUS = (8.686e+25, 2.5559e7)
+ ... NEPTUNE = (1.024e+26, 2.4746e7)
+ ... def __init__(self, mass, radius):
+ ... self.mass = mass # in kilograms
+ ... self.radius = radius # in meters
+ ... @property
+ ... def surface_gravity(self):
+ ... # universal gravitational constant (m3 kg-1 s-2)
+ ... G = 6.67300E-11
+ ... return G * self.mass / (self.radius * self.radius)
+ ...
+ >>> Planet.EARTH.value
+ (5.976e+24, 6378140.0)
+ >>> Planet.EARTH.surface_gravity
+ 9.802652743337129
+
+
+How are Enums different?
+------------------------
+
+Enums have a custom metaclass that affects many aspects of both derived Enum
+classes and their instances (members).
+
+
+Enum Classes
+^^^^^^^^^^^^
+
+The ``EnumMeta`` metaclass is responsible for providing the
+``__contains__``, ``__dir__``, ``__iter__`` and other methods that
+allow one to do things with an ``Enum`` class that fail on a typical
+class, such as ``list(Color)`` or ``some_var in Color``. ``EnumMeta`` is
+responsible for ensuring that various other methods on the final ``Enum``
+class are correct (such as ``__new__``, ``__getnewargs__``,
+``__str__`` and ``__repr__``).
+
+.. note::
+
+ ``__dir__`` is not changed in the Python 2 line as it messes up some
+ of the decorators included in the stdlib.
+
+
+Enum Members (aka instances)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The most interesting thing about Enum members is that they are singletons.
+``EnumMeta`` creates them all while it is creating the ``Enum``
+class itself, and then puts a custom ``__new__`` in place to ensure
+that no new ones are ever instantiated by returning only the existing
+member instances.
+
+
+Finer Points
+^^^^^^^^^^^^
+
+``Enum`` members are instances of an ``Enum`` class, but are not
+accessible as `EnumClass.member1.member2`.
+(changed in version 1.1.1 to be accessible)
+(changed in version 2.2.4 to be inaccessible)::
+
+ >>> class FieldTypes(Enum):
+ ... name = 1
+ ... value = 2
+ ... size = 3
+ ...
+ >>> FieldTypes.size.value
+ 3
+ >>> FieldTypes.size
+ <FieldTypes.size: 3>
+ >>> FieldTypes.value.size
+ Traceback (most recent call last):
+ ...
+ AttributeError: <aenum 'FieldTypes'> member has no attribute 'size'
+
+The ``__members__`` attribute is only available on the class.
+
+
+``__members__`` is always an ``OrderedDict``, with the order being the
+definition order in Python 3.x or the order in ``_order_`` in Python 2.7;
+if no ``_order_`` was specified in Python 2.7 then the order of
+``__members__`` is either increasing value or alphabetically by name.
+
+If you give your ``Enum`` subclass extra methods, like the `Planet`_
+class above, those methods will show up in a `dir` of the member,
+but not of the class (in Python 3.x)::
+
+ --> dir(Planet)
+ ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS',
+ 'VENUS', '__class__', '__doc__', '__members__', '__module__']
+ --> dir(Planet.EARTH)
+ ['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']
+
+A ``__new__`` method will only be used for the creation of the
+``Enum`` members -- after that it is replaced. This means if you wish to
+change how ``Enum`` members are looked up you either have to write a
+helper function or a ``classmethod``.
+
+.. note::
+
+ If you create your own ``__new__`` you should set the ``_value_`` in it;
+ if you do not, aenum will try to, but will raise a ``TypeError`` if it
+ cannot.
+
+If the stdlib ``enum`` is available (Python 3.4+ and it hasn't been shadowed
+by, for example, ``enum34``) then aenum will be a subclass of it.
+
+To use the ``AddValue``, ``MultiValue``, ``NoAlias``, and ``Unique`` flags
+in Py2 or Py2/Py3 codebases, use ``_settings_ = ...`` in the class body.
+
+To use ``init`` in Py2 or Py2/Py3 codebases use ``_init_`` in the class body.
+
+To use ``start`` in Py2 or Py2/Py3 codebases use ``_start_`` in the class body.
+
+When creating class bodies dynamically, put any variables you need to use into
+``_ignore_``::
+
+ >>> from datetime import timedelta
+ >>> from aenum import NoAlias
+ >>> class Period(timedelta, Enum):
+ ... '''
+ ... different lengths of time
+ ... '''
+ ... _init_ = 'value period'
+ ... _settings_ = NoAlias
+ ... _ignore_ = 'Period i'
+ ... Period = vars()
+ ... for i in range(31):
+ ... Period['day_%d' % i] = i, 'day'
+ ... for i in range(15):
+ ... Period['week_%d' % i] = i*7, 'week'
+ ...
+ >>> hasattr(Period, '_ignore_')
+ False
+ >>> hasattr(Period, 'Period')
+ False
+ >>> hasattr(Period, 'i')
+ False
+
+The name listed in ``_ignore_``, as well as ``_ignore_`` itself, will not be
+present in the final enumeration as neither attributes nor members.
+
+.. note::
+
+ except for __dunder__ attributes/methods, all _sunder_ attributes must
+ be before any thing else in the class body
+
+.. note::
+
+ all _sunder_ attributes that affect member creation are only looked up in
+ the last ``Enum`` class listed in the class header
+
+
+Creating NamedTuples
+--------------------
+
+Simple
+^^^^^^
+
+The most common way to create a new NamedTuple will be via the functional API::
+
+ >>> from aenum import NamedTuple
+ >>> Book = NamedTuple('Book', 'title author genre', module=__name__)
+
+This creates a ``NamedTuple`` called ``Book`` that will always contain three
+items, each of which is also addressable as ``title``, ``author``, or ``genre``.
+
+``Book`` instances can be created using positional or keyword argements or a
+mixture of the two::
+
+ >>> b1 = Book('Lord of the Rings', 'J.R.R. Tolkien', 'fantasy')
+ >>> b2 = Book(title='Jhereg', author='Steven Brust', genre='fantasy')
+ >>> b3 = Book('Empire', 'Orson Scott Card', genre='scifi')
+
+If too few or too many arguments are used a ``TypeError`` will be raised::
+
+ >>> b4 = Book('Hidden Empire')
+ Traceback (most recent call last):
+ ...
+ TypeError: values not provided for field(s): author, genre
+ >>> b5 = Book(genre='business')
+ Traceback (most recent call last):
+ ...
+ TypeError: values not provided for field(s): title, author
+
+As a ``class`` the above ``Book`` ``NamedTuple`` would look like::
+
+ >>> class Book(NamedTuple):
+ ... title = 0
+ ... author = 1
+ ... genre = 2
+ ...
+
+For compatibility with the stdlib ``namedtuple``, NamedTuple also has the
+``_asdict``, ``_make``, and ``_replace`` methods, and the ``_fields``
+attribute, which all function similarly::
+
+ >>> class Point(NamedTuple):
+ ... x = 0, 'horizontal coordinate', 1
+ ... y = 1, 'vertical coordinate', -1
+ ...
+ >>> class Color(NamedTuple):
+ ... r = 0, 'red component', 11
+ ... g = 1, 'green component', 29
+ ... b = 2, 'blue component', 37
+ ...
+ >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__)
+ >>> pixel = Pixel(99, -101, 255, 128, 0)
+
+ >>> pixel._asdict()
+ OrderedDict([('x', 99), ('y', -101), ('r', 255), ('g', 128), ('b', 0)])
+
+ >>> Point._make((4, 5))
+ Point(x=4, y=5)
+
+ >>> purple = Color(127, 0, 127)
+ >>> mid_gray = purple._replace(g=127)
+ >>> mid_gray
+ Color(r=127, g=127, b=127)
+
+ >>> pixel._fields
+ ['x', 'y', 'r', 'g', 'b']
+
+ >>> Pixel._fields
+ ['x', 'y', 'r', 'g', 'b']
+
+
+Advanced
+^^^^^^^^
+
+The simple method of creating ``NamedTuples`` requires always specifying all
+possible arguments when creating instances; failure to do so will raise
+exceptions::
+
+ >>> class Point(NamedTuple):
+ ... x = 0
+ ... y = 1
+ ...
+ >>> Point()
+ Traceback (most recent call last):
+ ...
+ TypeError: values not provided for field(s): x, y
+ >>> Point(1)
+ Traceback (most recent call last):
+ ...
+ TypeError: values not provided for field(s): y
+ >>> Point(y=2)
+ Traceback (most recent call last):
+ ...
+ TypeError: values not provided for field(s): x
+
+However, it is possible to specify both docstrings and default values when
+creating a ``NamedTuple`` using the class method::
+
+ >>> class Point(NamedTuple):
+ ... x = 0, 'horizontal coordinate', 0
+ ... y = 1, 'vertical coordinate', 0
+ ...
+ >>> Point()
+ Point(x=0, y=0)
+ >>> Point(1)
+ Point(x=1, y=0)
+ >>> Point(y=2)
+ Point(x=0, y=2)
+
+It is also possible to create ``NamedTuples`` that only have named attributes
+for certain fields; any fields without names can still be accessed by index::
+
+ >>> class Person(NamedTuple):
+ ... fullname = 2
+ ... phone = 5
+ ...
+ >>> p = Person('Ethan', 'Furman', 'Ethan Furman',
+ ... 'ethan at stoneleaf dot us',
+ ... 'ethan.furman', '999.555.1212')
+ >>> p
+ Person('Ethan', 'Furman', 'Ethan Furman', 'ethan at stoneleaf dot us',
+ 'ethan.furman', '999.555.1212')
+ >>> p.fullname
+ 'Ethan Furman'
+ >>> p.phone
+ '999.555.1212'
+ >>> p[0]
+ 'Ethan'
+
+In the above example the last named field was also the last field possible; in
+those cases where you don't need to have the last possible field named, you can
+provide a ``_size_`` of ``TupleSize.minimum`` to declare that more fields are
+okay::
+
+ >>> from aenum import TupleSize
+ >>> class Person(NamedTuple):
+ ... _size_ = TupleSize.minimum
+ ... first = 0
+ ... last = 1
+ ...
+
+or, optionally if using Python 3::
+
+ >>> class Person(NamedTuple, size=TupleSize.minimum): # doctest: +SKIP
+ ... first = 0
+ ... last = 1
+
+and in use::
+
+ >>> Person('Ethan', 'Furman')
+ Person(first='Ethan', last='Furman')
+
+ >>> Person('Ethan', 'Furman', 'ethan.furman')
+ Person('Ethan', 'Furman', 'ethan.furman')
+
+ >>> Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!')
+ Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!')
+
+ >>> Person('Ethan')
+ Traceback (most recent call last):
+ ...
+ TypeError: values not provided for field(s): last
+
+Also, for those cases where even named fields may not be present, you can
+specify ``TupleSize.variable``::
+
+ >>> class Person(NamedTuple):
+ ... _size_ = TupleSize.variable
+ ... first = 0
+ ... last = 1
+ ...
+
+ >>> Person('Ethan')
+ Person('Ethan')
+
+ >>> Person(last='Furman')
+ Traceback (most recent call last):
+ ...
+ TypeError: values not provided for field(s): first
+
+Creating new ``NamedTuples`` from existing ``NamedTuples`` is simple::
+
+ >>> Point = NamedTuple('Point', 'x y')
+ >>> Color = NamedTuple('Color', 'r g b')
+ >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__)
+ >>> Pixel
+ <NamedTuple 'Pixel'>
+
+The existing fields in the bases classes are renumbered to fit the new class,
+but keep their doc strings and default values. If you use standard
+subclassing::
+
+ >>> Point = NamedTuple('Point', 'x y')
+ >>> class Pixel(Point):
+ ... r = 2, 'red component', 11
+ ... g = 3, 'green component', 29
+ ... b = 4, 'blue component', 37
+ ...
+ >>> Pixel.__fields__
+ ['x', 'y', 'r', 'g', 'b']
+
+You must manage the numbering yourself.
+
+
+Creating NamedConstants
+-----------------------
+
+A ``NamedConstant`` class is created much like an ``Enum``::
+
+ >>> from aenum import NamedConstant
+ >>> class Konstant(NamedConstant):
+ ... PI = 3.14159
+ ... TAU = 2 * PI
+
+ >>> Konstant.PI
+ <Konstant.PI: 3.14159>
+
+ >> print(Konstant.PI)
+ 3.14159
+
+ >>> Konstant.PI = 'apple'
+ Traceback (most recent call last):
+ ...
+ AttributeError: cannot rebind constant <Konstant.PI>
+
+ >>> del Konstant.PI
+ Traceback (most recent call last):
+ ...
+ AttributeError: cannot delete constant <Konstant.PI>
diff --git a/venv/Lib/site-packages/aenum/test.py b/venv/Lib/site-packages/aenum/test.py
new file mode 100644
index 00000000..224293cb
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/test.py
@@ -0,0 +1,6832 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import division, print_function
+import sys
+import aenum
+import doctest
+import os
+import shutil
+import tempfile
+import textwrap
+import unittest
+import uuid
+import warnings
+from aenum import EnumType, EnumMeta, Enum, IntEnum, StrEnum, LowerStrEnum, UpperStrEnum
+from aenum import AutoNumberEnum, MultiValueEnum, OrderedEnum, UniqueEnum, AddValueEnum, Flag, IntFlag
+from aenum import NamedTuple, TupleSize, NamedConstant, constant, NoAlias, AddValue, Unique
+from aenum import STRICT, CONFORM, EJECT, KEEP
+from aenum import _reduce_ex_by_name, unique, skip, extend_enum, auto, enum, MultiValue, member, nonmember, no_arg
+from aenum import basestring, baseinteger, unicode, enum_property
+from aenum import pyver, PY2, PY3, PY2_6, PY3_3, PY3_4, PY3_5, PY3_6, PY3_11
+from collections import OrderedDict
+from datetime import timedelta
+from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
+from operator import or_ as _or_, and_ as _and_, xor as _xor_, inv as _inv_
+from operator import abs as _abs_, add as _add_, floordiv as _floordiv_
+from operator import lshift as _lshift_, rshift as _rshift_, mod as _mod_
+from operator import mul as _mul_, neg as _neg_, pos as _pos_, pow as _pow_
+from operator import truediv as _truediv_, sub as _sub_
+if PY2:
+ from operator import div as _div_
+try:
+ import threading
+except ImportError:
+ threading = None
+
+try:
+ any
+except NameError:
+ from aenum import any
+
+MODULE = __name__
+SHORT_MODULE = MODULE.split('.')[-1]
+
+def load_tests(loader, tests, ignore):
+ tests.addTests(doctest.DocTestSuite(aenum))
+ tests.addTests(doctest.DocFileSuite(
+ 'doc/aenum.rst',
+ package=aenum,
+ optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE,
+ ))
+ return tests
+
+class TestCase(unittest.TestCase):
+
+ def __init__(self, *args, **kwds):
+ regex = getattr(self, 'assertRaisesRegex', None)
+ if regex is None:
+ self.assertRaisesRegex = getattr(self, 'assertRaisesRegexp')
+ super(TestCase, self).__init__(*args, **kwds)
+
+
+# for pickle tests
+try:
+ class Stooges(Enum):
+ LARRY = 1
+ CURLY = 2
+ MOE = 3
+except Exception:
+ Stooges = sys.exc_info()[1]
+
+try:
+ class IntStooges(int, Enum):
+ LARRY = 1
+ CURLY = 2
+ MOE = 3
+except Exception:
+ IntStooges = sys.exc_info()[1]
+
+try:
+ class FloatStooges(float, Enum):
+ LARRY = 1.39
+ CURLY = 2.72
+ MOE = 3.142596
+except Exception:
+ FloatStooges = sys.exc_info()[1]
+
+try:
+ class FlagStooges(Flag):
+ LARRY = 1
+ CURLY = 2
+ MOE = 3
+except Exception as exc:
+ FlagStooges = exc
+
+try:
+ LifeForm = NamedTuple('LifeForm', 'branch genus species', module=__name__)
+except Exception:
+ LifeForm = sys.exc_info()[1]
+
+try:
+ class DeathForm(NamedTuple):
+ color = 0
+ rigidity = 1
+ odor = 2
+except Exception:
+ DeathForm = sys.exc_info()[1]
+
+# for pickle test and subclass tests
+try:
+ class Name(StrEnum):
+ BDFL = 'Guido van Rossum'
+ FLUFL = 'Barry Warsaw'
+except Exception:
+ Name = sys.exc_info()[1]
+
+try:
+ Question = Enum('Question', 'who what when where why', module=__name__)
+except Exception:
+ Question = sys.exc_info()[1]
+
+try:
+ Answer = Enum('Answer', 'him this then there because')
+except Exception:
+ Answer = sys.exc_info()[1]
+
+try:
+ class WhatsIt(NamedTuple):
+ def what(self):
+ return self[0]
+ class ThatsIt(WhatsIt):
+ blah = 0
+ bleh = 1
+except Exception:
+ ThatsIt = sys.exc_info()[1]
+
+# for doctests
+try:
+ class Fruit(Enum):
+ tomato = 1
+ banana = 2
+ cherry = 3
+except Exception:
+ pass
+
+def test_pickle_dump_load(assertion, source, target=None, protocol=(0, HIGHEST_PROTOCOL)):
+ start, stop = protocol
+ failures = []
+ for protocol in range(start, stop+1):
+ try:
+ if target is None:
+ assertion(loads(dumps(source, protocol=protocol)), source)
+ else:
+ assertion(loads(dumps(source, protocol=protocol)), target)
+ except Exception:
+ exc, tb = sys.exc_info()[1:]
+ failures.append('%2d: %s' %(protocol, exc))
+ if failures:
+ raise ValueError('Failed with protocols: %s' % ', '.join(failures))
+
+def test_pickle_exception(assertion, exception, obj,
+ protocol=(0, HIGHEST_PROTOCOL)):
+ start, stop = protocol
+ failures = []
+ for protocol in range(start, stop+1):
+ try:
+ assertion(exception, dumps, obj, protocol=protocol)
+ except Exception:
+ exc = sys.exc_info()[1]
+ failures.append('%d: %s %s' % (protocol, exc.__class__.__name__, exc))
+ if failures:
+ raise ValueError('Failed with protocols: %s' % ', '.join(failures))
+
+if PY3:
+ from aenum.test_v3 import TestEnumV3, TestOrderV3, TestNamedTupleV3, TestStackoverflowAnswersV3, TestIssuesV3, TestExtendEnumV3
+ from aenum import test_v3
+ test_v3.IntStooges = IntStooges
+ test_v3.test_pickle_exception = test_pickle_exception
+ test_v3.test_pickle_dump_load = test_pickle_dump_load
+
+# for subclassing tests
+
+class classproperty(object):
+
+ def __init__(self, fget=None, fset=None, fdel=None, doc=None):
+ self.fget = fget
+ self.fset = fset
+ self.fdel = fdel
+ if doc is None and fget is not None:
+ doc = fget.__doc__
+ self.__doc__ = doc
+
+ def __get__(self, instance, ownerclass):
+ return self.fget(ownerclass)
+
+
+# tests
+class TestOrder(TestCase):
+ """
+ Test _order_ extra/missing members.
+ """
+
+ def test_same_members(self):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+
+ def test_same_members_with_aliases(self):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ verde = green
+
+ def test_order_has_extra_members(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 3
+
+ def test_order_has_extra_members_with_aliases(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 3
+ verde = green
+
+ def test_enum_has_extra_members(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ purple = 4
+
+ def test_enum_has_extra_members_with_aliases(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ purple = 4
+ verde = green
+
+ def test_same_members_flag(self):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+
+ def test_same_members_with_aliases_flag(self):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+ verde = green
+
+ def test_order_has_extra_members_flag(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 4
+
+ def test_order_has_extra_members_with_aliases_flag(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 4
+ verde = green
+
+ def test_enum_has_extra_members_flag(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+ purple = 8
+
+ def test_enum_has_extra_members_with_aliases_flag(self):
+ with self.assertRaisesRegex(TypeError, r'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+ purple = 8
+ verde = green
+
+
+class TestAutoValue(TestCase):
+
+ def test_bare(self):
+ #
+ class BareEnum(Enum):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(BareEnum.THREE.value, 3)
+ #
+ class BareIntEnum(IntEnum):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(BareIntEnum.THREE, 3)
+ #
+ class BareFlag(Flag):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(BareFlag.THREE.value, 4)
+ #
+ class BareIntFlag(IntFlag):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(BareIntFlag.THREE, 4)
+
+ def test_init_only_final(self):
+ #
+ class InitEnumValue(Enum):
+ _init_ = 'value description'
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitEnumValue.THREE.value, 3)
+ self.assertEqual(InitEnumValue.THREE.description, 'a triangle')
+ #
+ class InitEnum(Enum):
+ _init_ = 'value description'
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitEnum.THREE.value, 3)
+ self.assertEqual(InitEnum.THREE.description, 'a triangle')
+ #
+ class InitIntEnum(IntEnum):
+ _init_ = 'value description'
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitIntEnum.THREE, 3)
+ self.assertEqual(InitIntEnum.THREE.description, 'a triangle')
+ #
+ class InitFlag(Flag):
+ _init_ = 'value description'
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitFlag.THREE.value, 4)
+ self.assertEqual(InitFlag.THREE.description, 'a triangle')
+ #
+ class InitIntFlag(IntFlag):
+ _init_ = 'value description'
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitIntFlag.THREE, 4)
+ self.assertEqual(InitIntFlag.THREE.description, 'a triangle')
+
+ def test_init_only_inherit(self):
+ #
+ class InitInheritEnum(Enum):
+ _init_ = 'value description'
+ #
+ class InitEnum(InitInheritEnum):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitEnum.THREE.value, 3)
+ self.assertEqual(InitEnum.THREE.description, 'a triangle')
+ #
+ #
+ class InitInheritValueEnum(Enum):
+ _init_ = 'value description'
+ #
+ class InitEnum(InitInheritValueEnum):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitEnum.THREE.value, 3)
+ self.assertEqual(InitEnum.THREE.description, 'a triangle')
+ #
+ class InitIntEnum(int, InitInheritValueEnum):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitIntEnum.THREE, 3)
+ self.assertEqual(InitIntEnum.THREE.description, 'a triangle')
+ #
+ class InitInheritValueFlag(Flag):
+ _init_ = 'value description'
+ #
+ class InitFlag(InitInheritValueFlag):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitFlag.THREE.value, 4)
+ self.assertEqual(InitFlag.THREE.description, 'a triangle')
+ #
+ class InitIntFlag(int, InitInheritValueFlag):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitIntFlag.THREE, 4)
+ self.assertEqual(InitIntFlag.THREE.description, 'a triangle')
+
+ def test_new_only_final(self):
+ #
+ class NewFinalEnum(Enum):
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value):
+ member = object.__new__(cls)
+ member._value_ = value
+ member.proof = 'NFE1'
+ return member
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalEnum.THREE.value, 3)
+ self.assertEqual(NewFinalEnum.TWO.proof, 'NFE1')
+ #
+ class NewFinalIntEnum(IntEnum):
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value):
+ member = int.__new__(cls, value)
+ member._value_ = value
+ member.proof = 'NFE2'
+ return member
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalIntEnum.THREE, 3)
+ self.assertEqual(NewFinalIntEnum.TWO.proof, 'NFE2')
+ #
+ class NewFinalFlag(Flag):
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value):
+ member = object.__new__(cls)
+ member._value_ = value
+ member.proof = 'NFE3'
+ return member
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalFlag.THREE.value, 4)
+ self.assertEqual(NewFinalFlag.TWO.proof, 'NFE3')
+ #
+ class NewFinalIntFlag(IntFlag):
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value):
+ member = int.__new__(cls, value)
+ member._value_ = value
+ member.proof = 'NFE4'
+ return member
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalIntFlag.THREE, 4)
+ self.assertEqual(NewFinalIntFlag.TWO.proof, 'NFE4')
+ #
+ class NewFinalStrEnum(str, Enum):
+ #
+ _order_ = "AllReset Bright FG_Cyan BG_Black"
+ #
+ def __new__(cls, value, code, description):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ obj.description = description
+ return obj
+ #
+ __str__ = str.__str__
+ #
+ AllReset = '0', 'reset all (colors and brightness)'
+ Bright = '1', 'bright lights!'
+ FG_Cyan = '36', 'cyan'
+ BG_Black = '40', 'black'
+ self.assertEqual(NewFinalStrEnum.FG_Cyan.value, 3)
+ self.assertEqual(NewFinalStrEnum.BG_Black.value, 4)
+ self.assertEqual(NewFinalStrEnum.AllReset.code, '0')
+ self.assertEqual(NewFinalStrEnum.Bright.description, 'bright lights!')
+ #
+ class NewFinalStrFlag(str, Flag):
+ #
+ _order_ = "AllReset Bright FG_Cyan BG_Black"
+ #
+ def __new__(cls, value, code, description):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ obj.description = description
+ return obj
+ #
+ __str__ = str.__str__
+ #
+ AllReset = '0', 'reset all (colors and brightness)'
+ Bright = '1', 'bright lights!'
+ FG_Cyan = '36', 'cyan'
+ BG_Black = '40', 'black'
+ self.assertEqual(NewFinalStrFlag.FG_Cyan.value, 4)
+ self.assertEqual(NewFinalStrFlag.BG_Black.value, 8)
+ self.assertEqual(NewFinalStrFlag.AllReset.code, '0')
+ self.assertEqual(NewFinalStrFlag.Bright.description, 'bright lights!')
+
+ def test_new_only_inherited(self):
+ #
+ class NewInheritEnum(Enum):
+ def __new__(cls, value):
+ if cls._member_type_ is int:
+ member = int.__new__(cls, value*2)
+ else:
+ member = object.__new__(cls)
+ member._value_ = value * 2
+ member.proof = 'NIE'
+ return member
+ #
+ class NewFinalEnum(NewInheritEnum):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalEnum.THREE.value, 6)
+ self.assertEqual(NewFinalEnum.TWO.proof, 'NIE')
+ #
+ class NewFinalIntEnum(int, NewInheritEnum):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalIntEnum.THREE, 6)
+ self.assertEqual(NewFinalIntEnum.TWO.proof, 'NIE')
+ #
+ class NewInheritFlag(Flag):
+ def __new__(cls, value):
+ if cls._member_type_ is int:
+ member = int.__new__(cls, value*2)
+ else:
+ member = object.__new__(cls)
+ member._value_ = value * 2
+ member.proof = 'NIE'
+ return member
+ #
+ class NewFinalFlag(NewInheritFlag):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalFlag.THREE.value, 8)
+ self.assertEqual(NewFinalFlag.TWO.proof, 'NIE')
+ #
+ class NewFinalIntFlag(int, NewInheritFlag):
+ _order_ = 'ONE TWO THREE'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ self.assertEqual(NewFinalIntFlag.THREE, 8)
+ self.assertEqual(NewFinalIntFlag.TWO.proof, 'NIE')
+
+ def test_init_new_only(self):
+ #
+ class InitNewEnum(Enum):
+ _init_ = "value description"
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value, *args):
+ member = object.__new__(cls)
+ member._value_ = value
+ member.proof = 'INE1'
+ return member
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewEnum.THREE.value, 3)
+ self.assertEqual(InitNewEnum.THREE.description, 'a triangle')
+ self.assertEqual(InitNewEnum.TWO.proof, 'INE1')
+ #
+ class InitNewIntEnum(IntEnum):
+ _init_ = "value description"
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value, *args):
+ member = int.__new__(cls, value)
+ member._value_ = value
+ member.proof = 'INE2'
+ return member
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewIntEnum.THREE, 3)
+ self.assertEqual(InitNewIntEnum.THREE.description, 'a triangle')
+ self.assertEqual(InitNewIntEnum.TWO.proof, 'INE2')
+ #
+ class InitNewFlag(Flag):
+ _init_ = "value description"
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value, *args):
+ member = object.__new__(cls)
+ member._value_ = value
+ member.proof = 'INE3'
+ return member
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewFlag.THREE.value, 4)
+ self.assertEqual(InitNewFlag.THREE.description, 'a triangle')
+ self.assertEqual(InitNewFlag.TWO.proof, 'INE3')
+ #
+ class InitNewIntFlag(IntFlag):
+ _init_ = "value description"
+ _order_ = 'ONE TWO THREE'
+ def __new__(cls, value, *args):
+ member = int.__new__(cls, value)
+ member._value_ = value
+ member.proof = 'INE4'
+ return member
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewIntFlag.THREE, 4)
+ self.assertEqual(InitNewIntFlag.THREE.description, 'a triangle')
+ self.assertEqual(InitNewIntFlag.TWO.proof, 'INE4')
+
+ def test_init_new_inherit(self):
+ #
+ class InitNew(Enum):
+ _init_ = "value description"
+ def __new__(cls, value, *args):
+ member = object.__new__(cls)
+ member._value_ = value
+ member.proof = 'IN'
+ return member
+ #
+ class InitNewEnum(InitNew):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewEnum.THREE.value, 3)
+ self.assertEqual(InitNewEnum.THREE.description, 'a triangle')
+ self.assertEqual(InitNewEnum.TWO.proof, 'IN')
+ #
+ class InitNewInt(Enum):
+ _init_ = "value description"
+ def __new__(cls, value, *args):
+ member = int.__new__(cls, value)
+ member._value_ = value
+ member.proof = 'IN'
+ return member
+ #
+ class InitNewIntEnum(int, InitNewInt):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewIntEnum.THREE, 3)
+ self.assertEqual(InitNewIntEnum.THREE.description, 'a triangle')
+ self.assertEqual(InitNewIntEnum.TWO.proof, 'IN')
+ #
+ class InitNewFlagBase(Flag):
+ _init_ = "value description"
+ def __new__(cls, value, *args):
+ member = object.__new__(cls)
+ member._value_ = value
+ member.proof = 'IN'
+ return member
+ #
+ class InitNewFlag(InitNewFlagBase):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewFlag.THREE.value, 4)
+ self.assertEqual(InitNewFlag.THREE.description, 'a triangle')
+ self.assertEqual(InitNewFlag.TWO.proof, 'IN')
+ #
+ class InitNewIntFlagBase(int, Flag):
+ _init_ = "value description"
+ def __new__(cls, value, *args):
+ member = int.__new__(cls, value)
+ member._value_ = value
+ member.proof = 'IN'
+ return member
+ #
+ class InitNewIntFlag(InitNewIntFlagBase):
+ _order_ = 'ONE TWO THREE'
+ ONE = 'the loneliest number'
+ TWO = 'the number with you'
+ THREE = 'a triangle'
+ self.assertEqual(InitNewIntFlag.THREE, 4)
+ self.assertEqual(InitNewIntFlag.THREE.description, 'a triangle')
+ self.assertEqual(InitNewIntFlag.TWO.proof, 'IN')
+
+
+class TestHelpers(TestCase):
+ # _is_descriptor, _is_sunder, _is_dunder
+
+ def test_is_descriptor(self):
+ class foo:
+ pass
+ for attr in ('__get__','__set__','__delete__'):
+ obj = foo()
+ self.assertFalse(aenum._is_descriptor(obj))
+ setattr(obj, attr, 1)
+ self.assertTrue(aenum._is_descriptor(obj))
+
+ def test_is_sunder(self):
+ for s in ('_a_', '_aa_'):
+ self.assertTrue(aenum._is_sunder(s))
+
+ for s in ('a', 'a_', '_a', '__a', 'a__', '__a__', '_a__', '__a_', '_',
+ '__', '___', '____', '_____',):
+ self.assertFalse(aenum._is_sunder(s))
+
+ def test_is_dunder(self):
+ for s in ('__a__', '__aa__'):
+ self.assertTrue(aenum._is_dunder(s))
+ for s in ('a', 'a_', '_a', '__a', 'a__', '_a_', '_a__', '__a_', '_',
+ '__', '___', '____', '_____',):
+ self.assertFalse(aenum._is_dunder(s))
+
+ def test_auto(self):
+ def tester(first, op, final, second=None):
+ if second is None:
+ left = auto()
+ value = op(left)
+ left.value = first
+ self.assertEqual(value.value, final,
+ "%s %r -> %r != %r" % (op.__name__, first, value, final))
+ else:
+ left = first
+ right = auto()
+ value = op(left, right)
+ right.value = second
+ self.assertEqual(value.value, final,
+ "forward: %r %s %r -> %r != %r" % (first, op.__name__, second, value.value, final))
+ left = auto()
+ right = second
+ value = op(left, right)
+ left.value = first
+ self.assertEqual(value.value, final,
+ "reversed: %r %s %r -> %r != %r" % (second, op.__name__, first, value.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 PY2:
+ tester(12, _div_, 12 // 5, 5)
+ # 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 PY2:
+ 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 TestEnumType(TestCase):
+
+ def test_immutability(self):
+ class Hah(object):
+ @classproperty
+ def all_values(cls):
+ return [m.value for m in cls]
+ class Huh(Hah, Enum):
+ one = 1
+ two = 2
+ self.assertRaisesRegex(AttributeError, 'cannot rebind property', setattr, Huh, 'value', 'boom')
+ self.assertRaisesRegex(AttributeError, 'cannot delete property', delattr, Huh, 'value')
+ self.assertRaisesRegex(AttributeError, 'cannot set attribute', setattr, Huh.one, 'value', 'boom')
+ self.assertRaisesRegex(AttributeError, 'cannot delete attribute', delattr, Huh.two, 'value')
+ self.assertEqual(Huh.one.value, 1)
+ self.assertEqual(Huh.two.value, 2)
+ self.assertEqual(Huh.all_values, [1, 2])
+ setattr(Huh, 'all_values', 99)
+ self.assertEqual(Huh.all_values, 99)
+
+ def test_enum_shadow_base(self):
+ class hohum(object):
+ def cyan(self):
+ "cyanize a color"
+ return self.value * 'cyan'
+ @property
+ def azure(self):
+ return 'azure ' + self.name
+ class Color(hohum, Enum):
+ red = 1
+ green = 2
+ blue = 3
+ cyan = 4
+ azure = 5
+ self.assertEqual(len(Color), 5)
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.cyan, Color.azure])
+ self.assertRaisesRegex(AttributeError, 'no attribute .cyan.', lambda: Color.blue.cyan)
+ self.assertEqual(Color.red.azure, 'azure red')
+
+
+class TestEnum(TestCase):
+
+ def setUp(self):
+ class Season(Enum):
+ SPRING = 1
+ SUMMER = 2
+ AUTUMN = 3
+ WINTER = 4
+ self.Season = Season
+
+ class Konstants(float, Enum):
+ E = 2.7182818
+ PI = 3.1415926
+ TAU = 2 * PI
+ self.Konstants = Konstants
+
+ class Grades(IntEnum):
+ A = 5
+ B = 4
+ C = 3
+ D = 2
+ F = 0
+ self.Grades = Grades
+
+ class Directional(str, Enum):
+ EAST = 'east'
+ WEST = 'west'
+ NORTH = 'north'
+ SOUTH = 'south'
+ self.Directional = Directional
+
+ from datetime import date
+ class Holiday(date, Enum):
+ NEW_YEAR = 2013, 1, 1
+ IDES_OF_MARCH = 2013, 3, 15
+ self.Holiday = Holiday
+
+ def test_set_name(self):
+ class Descriptor(object):
+ name = None
+ def __get__(self, instance, owner_class=None):
+ if instance is None:
+ return self
+ else:
+ return instance.__dict__[self.name]
+ def __set__(self, instance, value):
+ instance.__dict__[self.name] = value
+ def __set_name__(self, owner, name):
+ self.name = name
+ #
+ class AnEnum(Enum):
+ ONE = 'one'
+ two = Descriptor()
+ #
+ self.assertEqual(list(AnEnum), [AnEnum.ONE])
+ self.assertEqual(AnEnum.two.name, 'two')
+ AnEnum.ONE.two = 'three'
+ self.assertEqual(AnEnum.ONE.two, 'three')
+ self.assertEqual(AnEnum.ONE.__dict__['two'], 'three')
+
+ def test_private_names(self):
+ class Private(Enum):
+ __corporal = 'Radar'
+ __major_ = 'Hoolihan'
+ self.assertEqual(len(Private), 0)
+ self.assertEqual(Private._Private__corporal, 'Radar')
+ self.assertFalse(isinstance(Private._Private__corporal, Enum))
+ self.assertEqual(Private._Private__major_, 'Hoolihan')
+ self.assertFalse(isinstance(Private._Private__major_, Enum))
+
+ def test_new_with_keywords(self):
+ class Huh(IntEnum):
+ __order__ = 'PLAIN BOLD_ITALIC HIGHLIGHT'
+ def __new__(cls, docstring, open=None, close=None):
+ value = len(cls.__members__)
+ member = int.__new__(cls, value)
+ if open and close is None:
+ close = open
+ member.open = open
+ member.close = close
+ member.__doc__ = docstring
+ member._value_ = value
+ return member
+ PLAIN = 'normal'
+ BOLD_ITALIC = '***really super important***', '***'
+ HIGHLIGHT = 'please ==take notice==', '==', '=='
+ p = Huh.PLAIN
+ self.assertTrue(type(p) is Huh, type(p))
+ self.assertEqual(
+ (p.value, p.__doc__, p.open, p.close),
+ (0, 'normal', None, None),
+ )
+ bi = Huh.BOLD_ITALIC
+ self.assertEqual(
+ (bi.value, bi.__doc__, bi.open, bi.close),
+ (1, '***really super important***', '***', '***'),
+ )
+ h = Huh.HIGHLIGHT
+ self.assertEqual(
+ (h.value, h.__doc__, h.open, h.close),
+ (2, 'please ==take notice==', '==', '=='),
+ )
+
+ def test_members_is_ordereddict_if_ordered(self):
+ class Ordered(Enum):
+ __order__ = 'first second third'
+ first = 'bippity'
+ second = 'boppity'
+ third = 'boo'
+ self.assertTrue(type(Ordered.__members__) is OrderedDict)
+
+ def test_members_is_ordereddict_if_not_ordered(self):
+ class Unordered(Enum):
+ this = 'that'
+ these = 'those'
+ self.assertTrue(type(Unordered.__members__) is OrderedDict)
+
+ def test_enum_in_enum_out(self):
+ Season = self.Season
+ self.assertTrue(Season(Season.WINTER) is Season.WINTER)
+
+ def test_enum_value(self):
+ Season = self.Season
+ self.assertEqual(Season.SPRING.value, 1)
+
+ def test_intenum_value(self):
+ self.assertEqual(IntStooges.CURLY.value, 2)
+
+ def test_enum(self):
+ Season = self.Season
+ lst = list(Season)
+ self.assertEqual(len(lst), len(Season))
+ self.assertEqual(len(Season), 4, Season)
+ self.assertEqual(
+ [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst)
+
+ for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split()):
+ i += 1
+ e = Season(i)
+ self.assertEqual(e, getattr(Season, season))
+ self.assertEqual(e.value, i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, season)
+ self.assertTrue(e in Season)
+ self.assertTrue(type(e) is Season)
+ self.assertTrue(isinstance(e, Season))
+ self.assertEqual(str(e), 'Season.' + season)
+ self.assertEqual(
+ 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_enum_in_enum(self):
+ #
+ class Level(Enum):
+ _order_ = 'DATA_CHECK DESIGN_CHECK ALERT'
+ #
+ def __new__(cls, *args, **kwds):
+ member = object.__new__(cls)
+ member._value_ = len(cls) + 1 # members are 1-based
+ return member
+ #
+ def __init__(self, prereq=None, dependent=None):
+ # create priority level lists
+ self.lower_priority_levels = list(self.__class__._member_map_.values())
+ self.greater_priority_levels = []
+ # update previous members' greater priority list
+ for member in self.lower_priority_levels:
+ member.greater_priority_levels.append(self)
+ # and save prereq and dependent
+ self.prerequisite = prereq and self.__class__[prereq.name] or None
+ self.dependent = dependent and self.__class__[dependent.name] or None
+ #
+ DATA_CHECK = enum()
+ DESIGN_CHECK = enum(DATA_CHECK)
+ ALERT = enum(None, DATA_CHECK)
+ #
+ self.assertEqual(Level.DATA_CHECK.value, 1)
+ self.assertEqual(Level.DATA_CHECK.prerequisite, None)
+ self.assertEqual(Level.DATA_CHECK.dependent, None)
+ self.assertEqual(Level.DESIGN_CHECK.prerequisite, Level.DATA_CHECK)
+ self.assertEqual(Level.DESIGN_CHECK.dependent, None)
+ self.assertEqual(Level.ALERT.prerequisite, None)
+ self.assertEqual(Level.ALERT.dependent, Level.DATA_CHECK)
+
+ def test_value_name(self):
+ Season = self.Season
+ self.assertEqual(Season.SPRING.name, 'SPRING')
+ self.assertEqual(Season.SPRING.value, 1)
+ def set_name(obj, new_value):
+ obj.name = new_value
+ def set_value(obj, new_value):
+ obj.value = new_value
+ self.assertRaises(AttributeError, set_name, Season.SPRING, 'invierno', )
+ self.assertRaises(AttributeError, set_value, Season.SPRING, 2)
+
+ def test_attribute_deletion(self):
+ class Season(Enum):
+ SPRING = 1
+ SUMMER = 2
+ AUTUMN = 3
+ WINTER = 4
+
+ def spam(cls):
+ pass
+
+ self.assertTrue(hasattr(Season, 'spam'))
+ del Season.spam
+ self.assertFalse(hasattr(Season, 'spam'))
+
+ self.assertRaises(AttributeError, delattr, Season, 'SPRING')
+ self.assertRaises(AttributeError, delattr, Season, 'DRY')
+ self.assertRaises(AttributeError, delattr, Season.SPRING, 'name')
+
+ def test_bool_of_class(self):
+ class Empty(Enum):
+ pass
+ self.assertTrue(bool(Empty))
+
+ def test_bool_of_member(self):
+ class Count(Enum):
+ zero = 0
+ one = 1
+ two = 2
+ for member in Count:
+ self.assertTrue(bool(member))
+
+ def test_invalid_names(self):
+ def create_bad_class_1():
+ class Wrong(Enum):
+ mro = 9
+ def create_bad_class_2():
+ class Wrong(Enum):
+ _reserved_ = 3
+ self.assertRaises(ValueError, create_bad_class_1)
+ self.assertRaises(ValueError, create_bad_class_2)
+
+ def test_bool(self):
+ class Logic(Enum):
+ true = True
+ false = False
+ def __bool__(self):
+ return bool(self.value)
+ __nonzero__ = __bool__
+ self.assertTrue(Logic.true)
+ self.assertFalse(Logic.false)
+
+ def test_contains(self):
+ Season = self.Season
+ self.assertRaises(TypeError, lambda: 'AUTUMN' in Season)
+ self.assertTrue(Season.AUTUMN in Season)
+ self.assertRaises(TypeError, lambda: 3 not in Season)
+ val = Season(3)
+ self.assertTrue(val in Season)
+ #
+ class OtherEnum(Enum):
+ one = 1; two = 2
+ self.assertTrue(OtherEnum.two not in Season)
+ #
+ class Wierd(Enum):
+ this = [1, 2, 3]
+ that = (1, 2, 3)
+ those = {1: 1, 2: 2, 3: 3}
+ self.assertTrue(Wierd.this in Wierd)
+ self.assertRaises(TypeError, lambda: [1, 2, 3] in Wierd)
+ self.assertRaises(TypeError, lambda: {1: 1, 2: 2, 3: 3} in Wierd)
+
+ def test_member_contains(self):
+ self.assertRaises(TypeError, lambda: 'test' in self.Season.AUTUMN)
+
+ if pyver >= PY2_6: # when `format` came into being
+
+ def test_format_enum(self):
+ Season = self.Season
+ self.assertEqual('{0}'.format(Season.SPRING),
+ '{0}'.format(str(Season.SPRING)))
+ self.assertEqual( '{0:}'.format(Season.SPRING),
+ '{0:}'.format(str(Season.SPRING)))
+ self.assertEqual('{0:20}'.format(Season.SPRING),
+ '{0:20}'.format(str(Season.SPRING)))
+ self.assertEqual('{0:^20}'.format(Season.SPRING),
+ '{0:^20}'.format(str(Season.SPRING)))
+ self.assertEqual('{0:>20}'.format(Season.SPRING),
+ '{0:>20}'.format(str(Season.SPRING)))
+ self.assertEqual('{0:<20}'.format(Season.SPRING),
+ '{0:<20}'.format(str(Season.SPRING)))
+
+ def test_custom_format(self):
+ class TestFloat(float, Enum):
+ one = 1.0
+ two = 2.0
+ def __format__(self, spec):
+ return 'TestFloat success!'
+ self.assertEqual(str(TestFloat.one), 'TestFloat.one')
+ self.assertEqual('{0}'.format(TestFloat.one), 'TestFloat success!')
+
+ def test_format_with_custom_str(self):
+ class TestInt(int, Enum):
+ one = 1
+ two = 2
+ def __str__(self):
+ return self.name * 3
+ self.assertEqual(str(TestInt.two), 'twotwotwo')
+ self.assertEqual('{0}'.format(TestInt.two), 'twotwotwo')
+
+ def assertFormatIsValue(self, spec, member):
+ self.assertEqual(spec.format(member), spec.format(member.value))
+
+ def test_format_enum_date(self):
+ Holiday = self.Holiday
+ self.assertFormatIsValue('{0}', Holiday.IDES_OF_MARCH)
+ self.assertFormatIsValue('{0:}', Holiday.IDES_OF_MARCH)
+ self.assertFormatIsValue('{0:20}', Holiday.IDES_OF_MARCH)
+ self.assertFormatIsValue('{0:^20}', Holiday.IDES_OF_MARCH)
+ self.assertFormatIsValue('{0:>20}', Holiday.IDES_OF_MARCH)
+ self.assertFormatIsValue('{0:<20}', Holiday.IDES_OF_MARCH)
+ self.assertFormatIsValue('{0:%Y %m}', Holiday.IDES_OF_MARCH)
+ self.assertFormatIsValue('{0:%Y %m %M:00}', Holiday.IDES_OF_MARCH)
+
+ def test_format_enum_float(self):
+ Konstants = self.Konstants
+ self.assertFormatIsValue('{0}', Konstants.TAU)
+ self.assertFormatIsValue('{0:}', Konstants.TAU)
+ self.assertFormatIsValue('{0:20}', Konstants.TAU)
+ self.assertFormatIsValue('{0:^20}', Konstants.TAU)
+ self.assertFormatIsValue('{0:>20}', Konstants.TAU)
+ self.assertFormatIsValue('{0:<20}', Konstants.TAU)
+ self.assertFormatIsValue('{0:n}', Konstants.TAU)
+ self.assertFormatIsValue('{0:5.2}', Konstants.TAU)
+ self.assertFormatIsValue('{0:f}', Konstants.TAU)
+
+ def test_format_enum_int(self):
+ Grades = self.Grades
+ self.assertFormatIsValue('{0}', Grades.C)
+ self.assertFormatIsValue('{0:}', Grades.C)
+ self.assertFormatIsValue('{0:20}', Grades.C)
+ self.assertFormatIsValue('{0:^20}', Grades.C)
+ self.assertFormatIsValue('{0:>20}', Grades.C)
+ self.assertFormatIsValue('{0:<20}', Grades.C)
+ self.assertFormatIsValue('{0:+}', Grades.C)
+ self.assertFormatIsValue('{0:08X}', Grades.C)
+ self.assertFormatIsValue('{0:b}', Grades.C)
+
+ def test_format_enum_str(self):
+ Directional = self.Directional
+ self.assertFormatIsValue('{0}', Directional.WEST)
+ self.assertFormatIsValue('{0:}', Directional.WEST)
+ self.assertFormatIsValue('{0:20}', Directional.WEST)
+ self.assertFormatIsValue('{0:^20}', Directional.WEST)
+ self.assertFormatIsValue('{0:>20}', Directional.WEST)
+ self.assertFormatIsValue('{0:<20}', Directional.WEST)
+
+ def test_hash(self):
+ Season = self.Season
+ dates = {}
+ dates[Season.WINTER] = '1225'
+ dates[Season.SPRING] = '0315'
+ dates[Season.SUMMER] = '0704'
+ dates[Season.AUTUMN] = '1031'
+ self.assertEqual(dates[Season.AUTUMN], '1031')
+
+ def test_enum_duplicates(self):
+ class Season(Enum):
+ __order__ = "SPRING SUMMER AUTUMN WINTER"
+ SPRING = 1
+ SUMMER = 2
+ AUTUMN = FALL = 3
+ WINTER = 4
+ ANOTHER_SPRING = 1
+ lst = list(Season)
+ self.assertEqual(
+ lst,
+ [Season.SPRING, Season.SUMMER,
+ Season.AUTUMN, Season.WINTER,
+ ])
+ self.assertTrue(Season.FALL is Season.AUTUMN)
+ self.assertEqual(Season.FALL.value, 3)
+ self.assertEqual(Season.AUTUMN.value, 3)
+ self.assertTrue(Season(3) is Season.AUTUMN)
+ self.assertTrue(Season(1) is Season.SPRING)
+ self.assertEqual(Season.FALL.name, 'AUTUMN')
+ self.assertEqual(
+ set([k for k,v in Season.__members__.items() if v.name != k]),
+ set(['FALL', 'ANOTHER_SPRING']),
+ )
+
+ def test_enum_with_value_name(self):
+ class Huh(Enum):
+ _order_ = 'name value'
+ name = 1
+ value = 2
+ self.assertEqual(
+ list(Huh),
+ [Huh.name, Huh.value],
+ )
+ self.assertTrue(type(Huh.name) is Huh)
+ self.assertEqual(Huh.name.name, 'name')
+ self.assertEqual(Huh.name.value, 1)
+
+ def test_intenum_from_scratch(self):
+ class phy(int, Enum):
+ pi = 3
+ tau = 2 * pi
+ self.assertTrue(phy.pi < phy.tau)
+
+ def test_intenum_inherited(self):
+ class IntEnum(int, Enum):
+ pass
+ class phy(IntEnum):
+ pi = 3
+ tau = 2 * pi
+ self.assertTrue(phy.pi < phy.tau)
+
+ def test_floatenum_from_scratch(self):
+ class phy(float, Enum):
+ pi = 3.1415926
+ tau = 2 * pi
+ self.assertTrue(phy.pi < phy.tau)
+
+ def test_floatenum_inherited(self):
+ class FloatEnum(float, Enum):
+ pass
+ class phy(FloatEnum):
+ pi = 3.1415926
+ tau = 2 * pi
+ self.assertTrue(phy.pi < phy.tau)
+
+ def test_strenum_from_scratch(self):
+ class phy(str, Enum):
+ pi = 'Pi'
+ tau = 'Tau'
+ self.assertTrue(phy.pi < phy.tau)
+
+ def test_intenum(self):
+ class WeekDay(IntEnum):
+ SUNDAY = 1
+ MONDAY = 2
+ TUESDAY = 3
+ WEDNESDAY = 4
+ THURSDAY = 5
+ FRIDAY = 6
+ SATURDAY = 7
+
+ self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c')
+ self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2])
+
+ lst = list(WeekDay)
+ self.assertEqual(len(lst), len(WeekDay))
+ self.assertEqual(len(WeekDay), 7)
+ target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY'
+ target = target.split()
+ for i, weekday in enumerate(target):
+ i += 1
+ e = WeekDay(i)
+ self.assertEqual(e, i)
+ self.assertEqual(int(e), i)
+ self.assertEqual(e.name, weekday)
+ self.assertTrue(e in WeekDay)
+ self.assertEqual(lst.index(e)+1, i)
+ self.assertTrue(0 < e < 8)
+ self.assertTrue(type(e) is WeekDay)
+ self.assertTrue(isinstance(e, int))
+ self.assertTrue(isinstance(e, Enum))
+
+ def test_intenum_duplicates(self):
+ class WeekDay(IntEnum):
+ __order__ = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY'
+ SUNDAY = 1
+ MONDAY = 2
+ TUESDAY = TEUSDAY = 3
+ WEDNESDAY = 4
+ THURSDAY = 5
+ FRIDAY = 6
+ SATURDAY = 7
+ self.assertTrue(WeekDay.TEUSDAY is WeekDay.TUESDAY)
+ self.assertEqual(WeekDay(3).name, 'TUESDAY')
+ self.assertEqual([k for k,v in WeekDay.__members__.items()
+ if v.name != k], ['TEUSDAY', ])
+
+ def test_floatenum_fromhex(self):
+ h = float.hex(FloatStooges.MOE.value)
+ self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE)
+ h = float.hex(FloatStooges.MOE.value + 0.01)
+ with self.assertRaises(ValueError):
+ FloatStooges.fromhex(h)
+
+ def test_pickle_enum(self):
+ if isinstance(Stooges, Exception):
+ raise Stooges
+ test_pickle_dump_load(self.assertTrue, Stooges.CURLY)
+ test_pickle_dump_load(self.assertTrue, Stooges)
+
+ def test_pickle_int(self):
+ if isinstance(IntStooges, Exception):
+ raise IntStooges
+ test_pickle_dump_load(self.assertTrue, IntStooges.CURLY)
+ test_pickle_dump_load(self.assertTrue, IntStooges)
+
+ def test_pickle_float(self):
+ if isinstance(FloatStooges, Exception):
+ raise FloatStooges
+ test_pickle_dump_load(self.assertTrue, FloatStooges.CURLY)
+ test_pickle_dump_load(self.assertTrue, FloatStooges)
+
+ def test_pickle_enum_function(self):
+ if isinstance(Answer, Exception):
+ raise Answer
+ test_pickle_dump_load(self.assertTrue, Answer.him)
+ test_pickle_dump_load(self.assertTrue, Answer)
+
+ def test_pickle_enum_function_with_module(self):
+ if isinstance(Question, Exception):
+ raise Question
+ test_pickle_dump_load(self.assertTrue, Question.who)
+ test_pickle_dump_load(self.assertTrue, Question)
+
+ def test_pickle_by_name(self):
+ class ReplaceGlobalInt(IntEnum):
+ ONE = 1
+ TWO = 2
+ ReplaceGlobalInt.__reduce_ex__ = _reduce_ex_by_name
+ for proto in range(HIGHEST_PROTOCOL):
+ self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO')
+
+ def test_exploding_pickle(self):
+ BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter')
+ aenum._make_class_unpicklable(BadPickle)
+ globals()['BadPickle'] = BadPickle
+ test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill)
+ test_pickle_exception(self.assertRaises, PicklingError, BadPickle)
+
+ def test_string_enum(self):
+ class SkillLevel(str, Enum):
+ master = 'what is the sound of one hand clapping?'
+ journeyman = 'why did the chicken cross the road?'
+ apprentice = 'knock, knock!'
+ self.assertEqual(SkillLevel.apprentice, 'knock, knock!')
+
+ def test_getattr_getitem(self):
+ class Period(Enum):
+ morning = 1
+ noon = 2
+ evening = 3
+ night = 4
+ self.assertTrue(Period(2) is Period.noon)
+ self.assertTrue(getattr(Period, 'night') is Period.night)
+ self.assertTrue(Period['morning'] is Period.morning)
+
+ def test_getattr_dunder(self):
+ Season = self.Season
+ self.assertTrue(getattr(Season, '__hash__'))
+
+ def test_iteration_order(self):
+ class Season(Enum):
+ __order__ = 'SUMMER WINTER AUTUMN SPRING'
+ SUMMER = 2
+ WINTER = 4
+ AUTUMN = 3
+ SPRING = 1
+ self.assertEqual(
+ list(Season),
+ [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING],
+ )
+
+ def test_iteration_order_reversed(self):
+ self.assertEqual(
+ list(reversed(self.Season)),
+ [self.Season.WINTER, self.Season.AUTUMN, self.Season.SUMMER,
+ self.Season.SPRING]
+ )
+
+ def test_iteration_order_with_unorderable_values(self):
+ class Complex(Enum):
+ a = complex(7, 9)
+ b = complex(3.14, 2)
+ c = complex(1, -1)
+ d = complex(-77, 32)
+ self.assertEqual(
+ list(Complex),
+ [Complex.a, Complex.b, Complex.c, Complex.d],
+ )
+
+ def test_programatic_function_string(self):
+ SummerMonth = Enum('SummerMonth', 'june july august')
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_string_with_start(self):
+ SummerMonth = Enum('SummerMonth', 'june july august', start=10)
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 10):
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_string_list(self):
+ SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'])
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_string_list_with_start(self):
+ SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'], start=20)
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 20):
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_iterable(self):
+ SummerMonth = Enum(
+ 'SummerMonth',
+ (('june', 1), ('july', 2), ('august', 3))
+ )
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_from_dict(self):
+ SummerMonth = Enum(
+ 'SummerMonth',
+ dict((('june', 1), ('july', 2), ('august', 3)))
+ )
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ if PY2:
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_type(self):
+ SummerMonth = Enum('SummerMonth', 'june july august', type=int)
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_type_with_start(self):
+ SummerMonth = Enum('SummerMonth', 'june july august', type=int, start=30)
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 30):
+ e = SummerMonth(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_type_from_subclass(self):
+ SummerMonth = IntEnum('SummerMonth', 'june july august')
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_type_from_subclass_with_start(self):
+ SummerMonth = IntEnum('SummerMonth', 'june july august', start=40)
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate('june july august'.split(), 40):
+ e = SummerMonth(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_unicode(self):
+ SummerMonth = Enum('SummerMonth', unicode('june july august'))
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate(unicode('june july august').split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_unicode_list(self):
+ SummerMonth = Enum('SummerMonth', [unicode('june'), unicode('july'), unicode('august')])
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate(unicode('june july august').split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_unicode_iterable(self):
+ SummerMonth = Enum(
+ 'SummerMonth',
+ ((unicode('june'), 1), (unicode('july'), 2), (unicode('august'), 3))
+ )
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate(unicode('june july august').split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_from_unicode_dict(self):
+ SummerMonth = Enum(
+ 'SummerMonth',
+ dict(((unicode('june'), 1), (unicode('july'), 2), (unicode('august'), 3)))
+ )
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ if PY2:
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate(unicode('june july august').split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(int(e.value), i)
+ self.assertNotEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_unicode_type(self):
+ SummerMonth = Enum('SummerMonth', unicode('june july august'), type=int)
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate(unicode('june july august').split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programatic_function_unicode_type_from_subclass(self):
+ SummerMonth = IntEnum('SummerMonth', unicode('june july august'))
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate(unicode('june july august').split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(e, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_programmatic_function_unicode_class(self):
+ if PY2:
+ class_names = unicode('SummerMonth'), 'S\xfcmm\xe9rM\xf6nth'.decode('latin1')
+ else:
+ class_names = 'SummerMonth', 'S\xfcmm\xe9rM\xf6nth'
+ for i, class_name in enumerate(class_names):
+ if PY2 and i == 1:
+ self.assertRaises(TypeError, Enum, class_name, unicode('june july august'))
+ else:
+ SummerMonth = Enum(class_name, unicode('june july august'))
+ lst = list(SummerMonth)
+ self.assertEqual(len(lst), len(SummerMonth))
+ self.assertEqual(len(SummerMonth), 3, SummerMonth)
+ self.assertEqual(
+ [SummerMonth.june, SummerMonth.july, SummerMonth.august],
+ lst,
+ )
+ for i, month in enumerate(unicode('june july august').split()):
+ i += 1
+ e = SummerMonth(i)
+ self.assertEqual(e.value, i)
+ self.assertEqual(e.name, month)
+ self.assertTrue(e in SummerMonth)
+ self.assertTrue(type(e) is SummerMonth)
+
+ def test_subclassing(self):
+ if isinstance(Name, Exception):
+ raise Name
+ self.assertEqual(Name.BDFL, 'Guido van Rossum')
+ self.assertTrue(Name.BDFL, Name('Guido van Rossum'))
+ self.assertTrue(Name.BDFL is getattr(Name, 'BDFL'))
+ test_pickle_dump_load(self.assertTrue, Name.BDFL)
+
+ def test_extending(self):
+ def bad_extension():
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ class MoreColor(Color):
+ cyan = 4
+ magenta = 5
+ yellow = 6
+ self.assertRaises(TypeError, bad_extension)
+
+ def test_exclude_methods(self):
+ class whatever(Enum):
+ this = 'that'
+ these = 'those'
+ def really(self):
+ return 'no, not %s' % self.value
+ self.assertFalse(type(whatever.really) is whatever)
+ self.assertEqual(whatever.this.really(), 'no, not that')
+
+ def test_wrong_inheritance_order(self):
+ def wrong_inherit():
+ class Wrong(Enum, str):
+ NotHere = 'error before this point'
+ self.assertRaises(TypeError, wrong_inherit)
+
+ def test_intenum_transitivity(self):
+ class number(IntEnum):
+ one = 1
+ two = 2
+ three = 3
+ class numero(IntEnum):
+ uno = 1
+ dos = 2
+ tres = 3
+ self.assertEqual(number.one, numero.uno)
+ self.assertEqual(number.two, numero.dos)
+ self.assertEqual(number.three, numero.tres)
+
+ def test_introspection(self):
+ class Number(IntEnum):
+ one = 100
+ two = 200
+ self.assertTrue(Number.one._member_type_ is int)
+ self.assertTrue(Number._member_type_ is int)
+ class String(str, Enum):
+ yarn = 'soft'
+ rope = 'rough'
+ wire = 'hard'
+ self.assertTrue(String.yarn._member_type_ is str)
+ self.assertTrue(String._member_type_ is str)
+ class Plain(Enum):
+ vanilla = 'white'
+ one = 1
+ self.assertTrue(Plain.vanilla._member_type_ is object)
+ self.assertTrue(Plain._member_type_ is object)
+
+ def test_wrong_enum_in_call(self):
+ class Monochrome(Enum):
+ black = 0
+ white = 1
+ class Gender(Enum):
+ male = 0
+ female = 1
+ self.assertRaises(ValueError, Monochrome, Gender.male)
+
+ def test_wrong_enum_in_mixed_call(self):
+ class Monochrome(IntEnum):
+ black = 0
+ white = 1
+ class Gender(Enum):
+ male = 0
+ female = 1
+ self.assertRaises(ValueError, Monochrome, Gender.male)
+
+ def test_mixed_enum_in_call_1(self):
+ class Monochrome(IntEnum):
+ black = 0
+ white = 1
+ class Gender(IntEnum):
+ male = 0
+ female = 1
+ self.assertTrue(Monochrome(Gender.female) is Monochrome.white)
+
+ def test_mixed_enum_in_call_2(self):
+ class Monochrome(Enum):
+ black = 0
+ white = 1
+ class Gender(IntEnum):
+ male = 0
+ female = 1
+ self.assertTrue(Monochrome(Gender.male) is Monochrome.black)
+
+ def test_flufl_enum(self):
+ class Fluflnum(Enum):
+ def __int__(self):
+ return int(self.value)
+ class MailManOptions(Fluflnum):
+ option1 = 1
+ option2 = 2
+ option3 = 3
+ self.assertEqual(int(MailManOptions.option1), 1)
+
+ def test_no_such_enum_member(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaises(ValueError, Color, 4)
+ self.assertRaises(KeyError, Color.__getitem__, 'chartreuse')
+
+ def test_new_repr(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ def __repr__(self):
+ return "don't you just love shades of %s?" % self.name
+ self.assertEqual(
+ repr(Color.blue),
+ "don't you just love shades of blue?",
+ )
+
+ def test_inherited_repr(self):
+ class MyEnum(Enum):
+ def __repr__(self):
+ return "My name is %s." % self.name
+ class MyIntEnum(int, MyEnum):
+ this = 1
+ that = 2
+ theother = 3
+ self.assertEqual(repr(MyIntEnum.that), "My name is that.")
+
+ def test_multiple_mixin_mro(self):
+ class auto_enum(EnumMeta):
+ def __new__(metacls, cls, bases, classdict):
+ original_dict = classdict
+ temp_dict = metacls.__prepare__(cls, bases, {})
+ if hasattr(original_dict, '_member_names'):
+ for k in original_dict._member_names:
+ temp_dict[k] = original_dict[k]
+ sunders = [k for k in original_dict.keys() if aenum._is_sunder(k)]
+ else:
+ sunders = []
+ for k, v in original_dict.items():
+ if aenum._is_sunder(k):
+ sunders.append(k)
+ temp_dict[k] = v
+ classdict = metacls.__prepare__(cls, bases, {})
+ i = 0
+ for k in sunders:
+ classdict[k] = original_dict[k]
+ for k in temp_dict._member_names:
+ v = original_dict[k]
+ if v == ():
+ v = i
+ else:
+ i = v
+ i += 1
+ classdict[k] = v
+ for k, v in original_dict.items():
+ if k not in temp_dict._member_names and k not in sunders:
+ classdict[k] = v
+ return super(auto_enum, metacls).__new__(
+ metacls, cls, bases, classdict)
+
+ AutoNumberedEnum = auto_enum('AutoNumberedEnum', (Enum,), {})
+
+ AutoIntEnum = auto_enum('AutoIntEnum', (IntEnum,), {})
+
+ # class TestAutoNumber(AutoNumberedEnum):
+ # a = ()
+ # b = 3
+ # c = ()
+ # self.assertEqual(TestAutoNumber.b.value, 3)
+ #
+ # if pyver >= 3.0:
+ # self.assertEqual(
+ # [TestAutoNumber.a.value, TestAutoNumber.b.value, TestAutoNumber.c.value],
+ # [0, 3, 4],
+ # )
+ #
+ # class TestAutoInt(AutoIntEnum):
+ # a = ()
+ # b = 3
+ # c = ()
+ # self.assertEqual(TestAutoInt.b, 3)
+ #
+ # if pyver >= 3.0:
+ # self.assertEqual(
+ # [TestAutoInt.a.value, TestAutoInt.b.value, TestAutoInt.c.value],
+ # [0, 3, 4],
+ # )
+
+ def test_meta_reconfigure(self):
+
+ def identity(*args):
+ if len(args) == 1:
+ return args[0]
+ return args
+
+ JSONEnum = None
+
+ class JSONEnumMeta(EnumMeta):
+
+ @classmethod
+ def __prepare__(metacls, cls, bases, init=None, start=None, settings=()):
+ return {}
+
+ def __init__(cls, *args , **kwds):
+ super(JSONEnumMeta, cls).__init__(*args)
+
+ def __new__(metacls, cls, bases, clsdict, init=None, start=None, settings=()):
+ import json
+ members = []
+ if JSONEnum is not None:
+ if '_file' not in clsdict:
+ raise TypeError('_file is required')
+ if '_name' not in clsdict:
+ raise TypeError('_name is required')
+ if '_value' not in clsdict:
+ raise TypeError('_value is required')
+ name_spec = clsdict.pop('_name')
+ if not isinstance(name_spec, (tuple, list)):
+ name_spec = (name_spec, )
+ value_spec = clsdict.pop('_value')
+ file = clsdict.pop('_file')
+ with open(file) as f:
+ json_data = json.load(f)
+ for data in json_data:
+ values = []
+ name = data[name_spec[0]]
+ for piece in name_spec[1:]:
+ name = name[piece]
+ for order, (value_path, func) in sorted(value_spec.items()):
+ if not isinstance(value_path, (list, tuple)):
+ value_path = (value_path, )
+ value = data[value_path[0]]
+ for piece in value_path[1:]:
+ value = value[piece]
+ if func is not None:
+ value = func(value)
+ values.append(value)
+ values = tuple(values)
+ members.append(
+ (name, identity(*values))
+ )
+ # get the real EnumDict
+ enum_dict = super(JSONEnumMeta, metacls).__prepare__(cls, bases, init, start, settings)
+ # transfer the original dict content, _items first
+ items = list(clsdict.items())
+ items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p))
+ for name, value in items:
+ enum_dict[name] = value
+ # add the members
+ for name, value in members:
+ enum_dict[name] = value
+ return super(JSONEnumMeta, metacls).__new__(metacls, cls, bases, enum_dict, init, start, settings)
+
+ # for use with both Python 2/3
+ JSONEnum = JSONEnumMeta('JsonEnum', (Enum, ), {})
+
+ test_file = os.path.join(tempdir, 'test_json.json')
+ with open(test_file, 'w') as f:
+ f.write(
+ '[{"name":"Afghanistan","alpha-2":"AF","country-code":"004","notes":{"description":"pretty"}},'
+ '{"name":"Åland Islands","alpha-2":"AX","country-code":"248","notes":{"description":"serene"}},'
+ '{"name":"Albania","alpha-2":"AL","country-code":"008","notes":{"description":"exciting"}},'
+ '{"name":"Algeria","alpha-2":"DZ","country-code":"012","notes":{"description":"scarce"}}]')
+
+ class Country(JSONEnum):
+ _init_ = 'abbr code country_name description'
+ _file = test_file
+ _name = 'alpha-2'
+ _value = {
+ 1: ('alpha-2', None),
+ 2: ('country-code', lambda c: int(c)),
+ 3: ('name', None),
+ 4: (('notes','description'), lambda s: s.title()),
+ }
+
+ self.assertEqual([Country.AF, Country.AX, Country.AL, Country.DZ], list(Country))
+ self.assertEqual(Country.AF.abbr, 'AF')
+ self.assertEqual(Country.AX.code, 248)
+ self.assertEqual(Country.AL.country_name, 'Albania')
+ self.assertEqual(Country.DZ.description, 'Scarce')
+
+
+ def test_subclasses_with_getnewargs(self):
+ class NamedInt(int):
+ __qualname__ = 'NamedInt' # needed for pickle protocol 4
+ def __new__(cls, *args):
+ _args = args
+ if len(args) < 1:
+ raise TypeError("name and value must be specified")
+ name, args = args[0], args[1:]
+ self = int.__new__(cls, *args)
+ self._intname = name
+ self._args = _args
+ return self
+ def __getnewargs__(self):
+ return self._args
+ @property
+ def __name__(self):
+ return self._intname
+ def __repr__(self):
+ # repr() is updated to include the name and type info
+ return "%s(%r, %s)" % (type(self).__name__,
+ self.__name__,
+ int.__repr__(self))
+ def __str__(self):
+ # str() is unchanged, even if it relies on the repr() fallback
+ base = int
+ base_str = base.__str__
+ if base_str.__objclass__ is object:
+ return base.__repr__(self)
+ return base_str(self)
+ # for simplicity, we only define one operator that
+ # propagates expressions
+ def __add__(self, other):
+ temp = int(self) + int( other)
+ if isinstance(self, NamedInt) and isinstance(other, NamedInt):
+ return NamedInt(
+ '(%s + %s)' % (self.__name__, other.__name__),
+ temp )
+ else:
+ return temp
+
+ class NEI(NamedInt, Enum):
+ __qualname__ = 'NEI' # needed for pickle protocol 4
+ x = ('the-x', 1)
+ y = ('the-y', 2)
+
+ self.assertTrue(NEI.__new__ is Enum.__new__)
+ self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
+ globals()['NamedInt'] = NamedInt
+ globals()['NEI'] = NEI
+ NI5 = NamedInt('test', 5)
+ self.assertEqual(NI5, 5)
+ test_pickle_dump_load(self.assertTrue, NI5, 5)
+ self.assertEqual(NEI.y.value, 2)
+ test_pickle_dump_load(self.assertTrue, NEI.y)
+
+ def test_subclasses_with_reduce(self):
+ class NamedInt(int):
+ __qualname__ = 'NamedInt' # needed for pickle protocol 4
+ def __new__(cls, *args):
+ _args = args
+ if len(args) < 1:
+ raise TypeError("name and value must be specified")
+ name, args = args[0], args[1:]
+ self = int.__new__(cls, *args)
+ self._intname = name
+ self._args = _args
+ return self
+ def __reduce__(self):
+ return self.__class__, self._args
+ @property
+ def __name__(self):
+ return self._intname
+ def __repr__(self):
+ # repr() is updated to include the name and type info
+ return "%s(%r, %s)" % (type(self).__name__,
+ self.__name__,
+ int.__repr__(self))
+ def __str__(self):
+ # str() is unchanged, even if it relies on the repr() fallback
+ base = int
+ base_str = base.__str__
+ if base_str.__objclass__ is object:
+ return base.__repr__(self)
+ return base_str(self)
+ # for simplicity, we only define one operator that
+ # propagates expressions
+ def __add__(self, other):
+ temp = int(self) + int( other)
+ if isinstance(self, NamedInt) and isinstance(other, NamedInt):
+ return NamedInt(
+ '(%s + %s)' % (self.__name__, other.__name__),
+ temp )
+ else:
+ return temp
+
+ class NEI(NamedInt, Enum):
+ __qualname__ = 'NEI' # needed for pickle protocol 4
+ x = ('the-x', 1)
+ y = ('the-y', 2)
+
+
+ self.assertTrue(NEI.__new__ is Enum.__new__)
+ self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
+ globals()['NamedInt'] = NamedInt
+ globals()['NEI'] = NEI
+ NI5 = NamedInt('test', 5)
+ self.assertEqual(NI5, 5)
+ test_pickle_dump_load(self.assertEqual, NI5, 5)
+ self.assertEqual(NEI.y.value, 2)
+ test_pickle_dump_load(self.assertTrue, NEI.y)
+
+ def test_subclasses_with_reduce_ex(self):
+ class NamedInt(int):
+ __qualname__ = 'NamedInt' # needed for pickle protocol 4
+ def __new__(cls, *args):
+ _args = args
+ if len(args) < 1:
+ raise TypeError("name and value must be specified")
+ name, args = args[0], args[1:]
+ self = int.__new__(cls, *args)
+ self._intname = name
+ self._args = _args
+ return self
+ def __reduce_ex__(self, proto):
+ return self.__class__, self._args
+ @property
+ def __name__(self):
+ return self._intname
+ def __repr__(self):
+ # repr() is updated to include the name and type info
+ return "%s(%r, %s)" % (type(self).__name__,
+ self.__name__,
+ int.__repr__(self))
+ def __str__(self):
+ # str() is unchanged, even if it relies on the repr() fallback
+ base = int
+ base_str = base.__str__
+ if base_str.__objclass__ is object:
+ return base.__repr__(self)
+ return base_str(self)
+ # for simplicity, we only define one operator that
+ # propagates expressions
+ def __add__(self, other):
+ temp = int(self) + int( other)
+ if isinstance(self, NamedInt) and isinstance(other, NamedInt):
+ return NamedInt(
+ '(%s + %s)' % (self.__name__, other.__name__),
+ temp )
+ else:
+ return temp
+
+ class NEI(NamedInt, Enum):
+ __qualname__ = 'NEI' # needed for pickle protocol 4
+ x = ('the-x', 1)
+ y = ('the-y', 2)
+
+
+ self.assertTrue(NEI.__new__ is Enum.__new__)
+ self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
+ globals()['NamedInt'] = NamedInt
+ globals()['NEI'] = NEI
+ NI5 = NamedInt('test', 5)
+ self.assertEqual(NI5, 5)
+ test_pickle_dump_load(self.assertEqual, NI5, 5)
+ self.assertEqual(NEI.y.value, 2)
+ test_pickle_dump_load(self.assertTrue, NEI.y)
+
+ def test_subclasses_without_direct_pickle_support(self):
+ class NamedInt(int):
+ __qualname__ = 'NamedInt'
+ def __new__(cls, *args):
+ _args = args
+ name, args = args[0], args[1:]
+ if len(args) == 0:
+ raise TypeError("name and value must be specified")
+ self = int.__new__(cls, *args)
+ self._intname = name
+ self._args = _args
+ return self
+ @property
+ def __name__(self):
+ return self._intname
+ def __repr__(self):
+ # repr() is updated to include the name and type info
+ return "%s(%r, %s)" % (type(self).__name__,
+ self.__name__,
+ int.__repr__(self))
+ def __str__(self):
+ # str() is unchanged, even if it relies on the repr() fallback
+ base = int
+ base_str = base.__str__
+ if base_str.__objclass__ is object:
+ return base.__repr__(self)
+ return base_str(self)
+ # for simplicity, we only define one operator that
+ # propagates expressions
+ def __add__(self, other):
+ temp = int(self) + int( other)
+ if isinstance(self, NamedInt) and isinstance(other, NamedInt):
+ return NamedInt(
+ '(%s + %s)' % (self.__name__, other.__name__),
+ temp )
+ else:
+ return temp
+
+ class NEI(NamedInt, Enum):
+ __qualname__ = 'NEI'
+ x = ('the-x', 1)
+ y = ('the-y', 2)
+
+ self.assertTrue(NEI.__new__ is Enum.__new__)
+ self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
+ globals()['NamedInt'] = NamedInt
+ globals()['NEI'] = NEI
+ NI5 = NamedInt('test', 5)
+ self.assertEqual(NI5, 5)
+ self.assertEqual(NEI.y.value, 2)
+ test_pickle_exception(self.assertRaises, TypeError, NEI.x)
+ test_pickle_exception(self.assertRaises, PicklingError, NEI)
+
+ def test_subclasses_without_direct_pickle_support_using_name(self):
+ class NamedInt(int):
+ __qualname__ = 'NamedInt'
+ def __new__(cls, *args):
+ _args = args
+ name, args = args[0], args[1:]
+ if len(args) == 0:
+ raise TypeError("name and value must be specified")
+ self = int.__new__(cls, *args)
+ self._intname = name
+ self._args = _args
+ return self
+ @property
+ def __name__(self):
+ return self._intname
+ def __repr__(self):
+ # repr() is updated to include the name and type info
+ return "%s(%r, %s)" % (type(self).__name__,
+ self.__name__,
+ int.__repr__(self))
+ def __str__(self):
+ # str() is unchanged, even if it relies on the repr() fallback
+ base = int
+ base_str = base.__str__
+ if base_str.__objclass__ is object:
+ return base.__repr__(self)
+ return base_str(self)
+ # for simplicity, we only define one operator that
+ # propagates expressions
+ def __add__(self, other):
+ temp = int(self) + int( other)
+ if isinstance(self, NamedInt) and isinstance(other, NamedInt):
+ return NamedInt(
+ '(%s + %s)' % (self.__name__, other.__name__),
+ temp )
+ else:
+ return temp
+
+ class NEI(NamedInt, Enum):
+ __qualname__ = 'NEI'
+ x = ('the-x', 1)
+ y = ('the-y', 2)
+ def __reduce_ex__(self, proto):
+ return getattr, (self.__class__, self._name_)
+
+ self.assertTrue(NEI.__new__ is Enum.__new__)
+ self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
+ globals()['NamedInt'] = NamedInt
+ globals()['NEI'] = NEI
+ NI5 = NamedInt('test', 5)
+ self.assertEqual(NI5, 5)
+ self.assertEqual(NEI.y.value, 2)
+ test_pickle_dump_load(self.assertTrue, NEI.y)
+ test_pickle_dump_load(self.assertTrue, NEI)
+
+ def test_tuple_subclass(self):
+ class SomeTuple(tuple, Enum):
+ __qualname__ = 'SomeTuple'
+ first = (1, 'for the money')
+ second = (2, 'for the show')
+ third = (3, 'for the music')
+ self.assertTrue(type(SomeTuple.first) is SomeTuple)
+ self.assertTrue(isinstance(SomeTuple.second, tuple))
+ self.assertEqual(SomeTuple.third, (3, 'for the music'))
+ globals()['SomeTuple'] = SomeTuple
+ test_pickle_dump_load(self.assertTrue, SomeTuple.first)
+
+ # def test_duplicate_values_give_unique_enum_items(self):
+ # class NumericEnum(AutoNumberEnum):
+ # __order__ = 'enum_m enum_d enum_y'
+ # enum_m = ()
+ # enum_d = ()
+ # enum_y = ()
+ # def __int__(self):
+ # return int(self._value_)
+ # self.assertEqual(int(NumericEnum.enum_d), 2)
+ # self.assertEqual(NumericEnum.enum_y.value, 3)
+ # self.assertTrue(NumericEnum(1) is NumericEnum.enum_m)
+ # self.assertEqual(
+ # list(NumericEnum),
+ # [NumericEnum.enum_m, NumericEnum.enum_d, NumericEnum.enum_y],
+ # )
+
+ def test_inherited_new_from_enhanced_enum(self):
+ class AutoNumber2(Enum):
+ def __new__(cls):
+ value = len(cls.__members__) + 1
+ obj = object.__new__(cls)
+ obj._value_ = value
+ return obj
+ def __int__(self):
+ return int(self._value_)
+ class Color(AutoNumber2):
+ __order__ = 'red green blue'
+ red = ()
+ green = ()
+ blue = ()
+ self.assertEqual(len(Color), 3, "wrong number of elements: %d (should be %d)" % (len(Color), 3))
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+ if PY3:
+ self.assertEqual(list(map(int, Color)), [1, 2, 3])
+
+ def test_inherited_new_from_mixed_enum(self):
+ class AutoNumber3(IntEnum):
+ def __new__(cls):
+ value = len(cls.__members__) + 11
+ obj = int.__new__(cls, value)
+ obj._value_ = value
+ return obj
+ class Color(AutoNumber3):
+ __order__ = 'red green blue'
+ red = ()
+ green = ()
+ blue = ()
+ self.assertEqual(len(Color), 3, "wrong number of elements: %d (should be %d)" % (len(Color), 3))
+ Color.red
+ Color.green
+ Color.blue
+ self.assertEqual(Color.blue, 13)
+
+ def test_equality(self):
+ class AlwaysEqual:
+ def __eq__(self, other):
+ return True
+ class OrdinaryEnum(Enum):
+ a = 1
+ self.assertEqual(AlwaysEqual(), OrdinaryEnum.a)
+ self.assertEqual(OrdinaryEnum.a, AlwaysEqual())
+
+ def test_ordered_mixin(self):
+ class Grade(OrderedEnum):
+ __order__ = 'A B C D F'
+ A = 5
+ B = 4
+ C = 3
+ D = 2
+ F = 1
+ self.assertEqual(list(Grade), [Grade.A, Grade.B, Grade.C, Grade.D, Grade.F])
+ self.assertTrue(Grade.A > Grade.B)
+ self.assertTrue(Grade.F <= Grade.C)
+ 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, value):
+ 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_value_bad_input(self):
+ class Label(Enum):
+ AnyApple = 0
+ RedApple = 1
+ GreenApple = 2
+ @classmethod
+ def _missing_value_(cls, value):
+ return cls.AnyApple
+
+ self.assertEqual(Label.AnyApple, Label(4))
+ with self.assertRaises(KeyError):
+ Label[True]
+
+ 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):
+ def shade(self):
+ print(self.name)
+ class Color(Shade):
+ red = 1
+ green = 2
+ blue = 3
+ class MoreColor(Color):
+ cyan = 4
+ magenta = 5
+ yellow = 6
+ self.assertRaises(TypeError, bad_extension)
+
+ def test_extending3(self):
+ class Shade(Enum):
+ def shade(self):
+ return self.name
+ class Color(Shade):
+ def hex(self):
+ return '%s hexlified!' % self.value
+ class MoreColor(Color):
+ cyan = 4
+ magenta = 5
+ yellow = 6
+ self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!')
+
+ def test_extending5(self):
+ class Color(Enum):
+ _order_ = 'red green blue value'
+ red = 1
+ green = 2
+ blue = 3
+ value = 4
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.value])
+ self.assertEqual(Color.value.name, 'value')
+ self.assertEqual(Color.value.value, 4)
+ self.assertTrue(Color.value in Color)
+ self.assertEqual(Color(4), Color.value)
+ self.assertEqual(Color['value'], Color.value)
+ self.assertEqual(Color.red.value, 1)
+
+ CONTINUE = 100, 'Continue', 'Request received, please continue'
+ SWITCHING_PROTOCOLS = (101, 'Switching Protocols',
+ 'Switching to new protocol; obey Upgrade header')
+ PROCESSING = 102, 'Processing'
+
+ def test_no_duplicates(self):
+ def bad_duplicates():
+ class Color1(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ class Color2(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ grene = 2
+ self.assertRaises(ValueError, bad_duplicates)
+
+ def test_no_duplicates_kinda(self):
+ class Silly(UniqueEnum):
+ one = 1
+ two = 'dos'
+ name = 3
+ class Sillier(IntEnum, UniqueEnum):
+ single = 1
+ name = 2
+ triple = 3
+ value = 4
+
+ def test_init(self):
+ class Planet(Enum):
+ MERCURY = (3.303e+23, 2.4397e6)
+ VENUS = (4.869e+24, 6.0518e6)
+ EARTH = (5.976e+24, 6.37814e6)
+ MARS = (6.421e+23, 3.3972e6)
+ JUPITER = (1.9e+27, 7.1492e7)
+ SATURN = (5.688e+26, 6.0268e7)
+ URANUS = (8.686e+25, 2.5559e7)
+ NEPTUNE = (1.024e+26, 2.4746e7)
+ def __init__(self, mass, radius):
+ self.mass = mass # in kilograms
+ self.radius = radius # in meters
+ @property
+ def surface_gravity(self):
+ # universal gravitational constant (m3 kg-1 s-2)
+ G = 6.67300E-11
+ return G * self.mass / (self.radius * self.radius)
+ self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80)
+ self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6))
+
+ def test_init_and_shadowing_attribute(self):
+ class SelectionEnum(str, Enum):
+ _init_ = 'db user'
+ def __new__(cls, *args, **kwds):
+ count = len(cls.__members__)
+ obj = str.__new__(cls, args[0])
+ obj._count = count
+ obj._value_ = args
+ return obj
+ @staticmethod
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (name, ) + args
+ class DeviceTypeSource(SelectionEnum):
+ _order_ = 'user system'
+ user = "User controlled"
+ system = "System controlled"
+ self.assertEqual(DeviceTypeSource.system.db, 'system')
+ self.assertEqual(DeviceTypeSource.system.user, 'System controlled')
+ self.assertEqual(DeviceTypeSource.user.db, 'user')
+ self.assertEqual(DeviceTypeSource.user.user, 'User controlled')
+
+ def test_nonhash_value(self):
+ class AutoNumberInAList(Enum):
+ def __new__(cls):
+ value = [len(cls.__members__) + 1]
+ obj = object.__new__(cls)
+ obj._value_ = value
+ return obj
+ class ColorInAList(AutoNumberInAList):
+ __order__ = 'red green blue'
+ red = ()
+ green = ()
+ blue = ()
+ self.assertEqual(list(ColorInAList), [ColorInAList.red, ColorInAList.green, ColorInAList.blue])
+ self.assertEqual(ColorInAList.red.value, [1])
+ self.assertEqual(ColorInAList([1]), ColorInAList.red)
+
+ def test_number_reset_and_order_cleanup(self):
+ class Confused(Enum):
+ _order_ = 'ONE TWO THREE UNO DOS TRES FOUR'
+ ONE = auto()
+ TWO = auto()
+ THREE = auto()
+ UNO = 1
+ DOS = auto()
+ TRES = auto()
+ FOUR = auto()
+ self.assertEqual(list(Confused), [Confused.ONE, Confused.TWO, Confused.THREE, Confused.FOUR])
+ self.assertIs(Confused.TWO, Confused.DOS)
+ self.assertEqual(Confused.DOS._value_, 2)
+ self.assertEqual(Confused.TRES._value_, 3)
+ self.assertEqual(Confused.FOUR._value_, 4)
+
+ def test_conflicting_types_resolved_in_new(self):
+ class LabelledIntEnum(int, Enum):
+ def __new__(cls, *args):
+ value, label = args
+ obj = int.__new__(cls, value)
+ obj.label = label
+ obj._value_ = value
+ return obj
+
+ class LabelledList(LabelledIntEnum):
+ unprocessed = (1, "Unprocessed")
+ payment_complete = (2, "Payment Complete")
+
+ self.assertEqual(LabelledList.unprocessed, 1)
+ self.assertEqual(LabelledList(1), LabelledList.unprocessed)
+ self.assertEqual(list(LabelledList), [LabelledList.unprocessed, LabelledList.payment_complete])
+
+ def test_auto_number(self):
+ class Color(Enum):
+ _order_ = 'red blue green'
+ red = auto()
+ blue = auto()
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 1)
+ self.assertEqual(Color.blue.value, 2)
+ self.assertEqual(Color.green.value, 3)
+
+ def test_auto_name(self):
+ class Color(Enum):
+ _order_ = 'red blue green'
+ def _generate_next_value_(name, start, count, last):
+ return name
+ red = auto()
+ blue = auto()
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.green.value, 'green')
+
+ def test_auto_name_inherit(self):
+ class AutoNameEnum(Enum):
+ def _generate_next_value_(name, start, count, last):
+ return name
+ class Color(AutoNameEnum):
+ _order_ = 'red blue green'
+ red = auto()
+ blue = auto()
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.green.value, 'green')
+
+ def test_auto_garbage(self):
+ class Color(Enum):
+ _order_ = 'red blue'
+ red = 'red'
+ blue = auto()
+ self.assertEqual(Color.blue.value, 1)
+
+ def test_auto_garbage_corrected(self):
+ class Color(Enum):
+ _order_ = 'red blue green'
+ red = 'red'
+ blue = 2
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 2)
+ self.assertEqual(Color.green.value, 3)
+
+ def test_duplicate_auto(self):
+ #
+ class MoreDupes(Enum):
+ _order_ = 'A B C'
+ A = auto()
+ B = A,
+ C = auto()
+ self.assertEqual(list(MoreDupes), [MoreDupes.A, MoreDupes.B, MoreDupes.C])
+ self.assertEqual([m.value for m in MoreDupes], [1, (1, ), 2])
+ #
+ class Dupes(Enum):
+ _order_ = 'first second third'
+ first = primero = auto()
+ second = auto()
+ 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'
+ 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_auto_and_kwds(self):
+ class Item(Enum):
+ _order_ = 'A B'
+ A = auto(size=100, req={'red': True})
+ B = auto(size=200, req={'red': False})
+ #
+ def __new__(cls, value, size, req):
+ obj = object.__new__(cls)
+ obj._value_ = value
+ obj.size = size
+ obj.req= req
+ return obj
+ self.assertEqual((Item.A.value, Item.A.size, Item.A.req), (1, 100, {'red': True}))
+ self.assertEqual((Item.B.value, Item.B.size, Item.B.req), (2, 200, {'red': False}))
+
+ def test_empty_with_functional_api(self):
+ empty = aenum.IntEnum('Foo', {})
+ self.assertEqual(len(empty), 0)
+
+ def test_auto_init(self):
+ class Planet(Enum):
+ _init_ = 'mass radius'
+ MERCURY = (3.303e+23, 2.4397e6)
+ VENUS = (4.869e+24, 6.0518e6)
+ EARTH = (5.976e+24, 6.37814e6)
+ MARS = (6.421e+23, 3.3972e6)
+ JUPITER = (1.9e+27, 7.1492e7)
+ SATURN = (5.688e+26, 6.0268e7)
+ URANUS = (8.686e+25, 2.5559e7)
+ NEPTUNE = (1.024e+26, 2.4746e7)
+ @property
+ def surface_gravity(self):
+ # universal gravitational constant (m3 kg-1 s-2)
+ G = 6.67300E-11
+ return G * self.mass / (self.radius * self.radius)
+ self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80)
+ self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6))
+
+ def test_auto_init_with_value(self):
+ class Color(Enum):
+ _init_='value, rgb'
+ RED = 1, (1, 0, 0)
+ BLUE = 2, (0, 1, 0)
+ GREEN = 3, (0, 0, 1)
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.BLUE.value, 2)
+ self.assertEqual(Color.GREEN.value, 3)
+ self.assertEqual(Color.RED.rgb, (1, 0, 0))
+ self.assertEqual(Color.BLUE.rgb, (0, 1, 0))
+ self.assertEqual(Color.GREEN.rgb, (0, 0, 1))
+
+ def test_noalias(self):
+ class Settings(Enum):
+ _settings_ = NoAlias
+ red = 1
+ rojo = 1
+ self.assertFalse(Settings.red is Settings.rojo)
+ self.assertRaises(TypeError, Settings, 1)
+
+ def test_auto_and_init(self):
+ class Field(int, Enum):
+ _order_ = 'TYPE START'
+ _init_ = 'value __doc__'
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ self.assertEqual(Field.TYPE, 1)
+ self.assertEqual(Field.START, 2)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+
+ def test_auto_and_start(self):
+ class Field(IntEnum):
+ _order_ = 'TYPE START'
+ _start_ = 0
+ _init_ = 'value __doc__'
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ self.assertEqual(Field.TYPE, 0)
+ self.assertEqual(Field.START, 1)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+
+ def test_auto_and_init_and_some_values(self):
+ class Field(int, Enum):
+ _order_ = 'TYPE START BLAH BELCH'
+ _init_ = 'value __doc__'
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ BLAH = 5, "test blah"
+ BELCH = 'test belch'
+ self.assertEqual(Field.TYPE, 1)
+ self.assertEqual(Field.START, 2)
+ self.assertEqual(Field.BLAH, 5)
+ self.assertEqual(Field.BELCH, 6)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+ self.assertEqual(Field.BLAH.__doc__, 'test blah')
+ self.assertEqual(Field.BELCH.__doc__, 'test belch')
+
+ def test_auto_and_init_w_value_and_too_many_values(self):
+ with self.assertRaisesRegex(TypeError, r'Field\.BLAH: number of fields provided do not match init'):
+ class Field(int, Enum):
+ _order_ = 'TYPE START BLAH BELCH'
+ _init_ = 'value __doc__'
+ TYPE = 1, "Char, Date, Logical, etc."
+ START = 2, "Field offset in record"
+ BLAH = 5, 6, "test blah"
+ BELCH = 7, 'test belch'
+
+ def test_auto_and_init_and_some_complex_values(self):
+ class Field(int, Enum):
+ _order_ = 'TYPE START BLAH BELCH'
+ _init_ = 'value __doc__ help'
+ TYPE = "Char, Date, Logical, etc.", "fields composed of character data"
+ START = "Field offset in record", "where the data starts in the record"
+ BLAH = 5, "test blah", "some help"
+ BELCH = 'test belch', "some more help"
+ self.assertEqual(Field.TYPE, 1)
+ self.assertEqual(Field.START, 2)
+ self.assertEqual(Field.BLAH, 5)
+ self.assertEqual(Field.BELCH, 6)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+ self.assertEqual(Field.BLAH.__doc__, 'test blah')
+ self.assertEqual(Field.BELCH.__doc__, 'test belch')
+ self.assertEqual(Field.TYPE.help, "fields composed of character data")
+ self.assertEqual(Field.START.help, "where the data starts in the record")
+ self.assertEqual(Field.BLAH.help, "some help")
+ self.assertEqual(Field.BELCH.help, "some more help")
+
+ def test_auto_and_init_inherited(self):
+ class AutoEnum(IntEnum):
+ _start_ = 0
+ _init_ = 'value __doc__'
+ class Field(AutoEnum):
+ _order_ = 'TYPE START BLAH BELCH'
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ BLAH = 5, "test blah"
+ BELCH = 'test belch'
+ self.assertEqual(Field.TYPE, 0)
+ self.assertEqual(Field.START, 1)
+ self.assertEqual(Field.BLAH, 5)
+ self.assertEqual(Field.BELCH, 6)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+ self.assertEqual(Field.BLAH.__doc__, 'test blah')
+ self.assertEqual(Field.BELCH.__doc__, 'test belch')
+
+ def test_missing_value_error(self):
+ with self.assertRaisesRegex(TypeError, r"_value_ not set in __new__"):
+ class Combined(str, Enum):
+ #
+ _init_ = 'value sequence'
+ _order_ = lambda m: m.sequence
+ #
+ def __new__(cls, value, *args):
+ enum = str.__new__(cls, value)
+ if '(' in value:
+ fis_name, segment = value.split('(', 1)
+ segment = segment.strip(' )')
+ else:
+ fis_name = value
+ segment = None
+ enum.fis_name = fis_name
+ enum.segment = segment
+ return enum
+ #
+ def __repr__(self):
+ return "<%s.%s>" % (self.__class__.__name__, self._name_)
+ #
+ key_type = 'An$(1,2)', 0
+ company_id = 'An$(3,2)', 1
+ code = 'An$(5,1)', 2
+ description = 'Bn$', 3
+
+
+ def test_auto_and_enum(self):
+ class Foo(aenum.Flag):
+ _order_ = 'a b c'
+ a = aenum.auto()
+ b = a | aenum.auto()
+ c = 2
+
+ self.assertEqual([Foo.a, Foo.c], list(Foo))
+ self.assertEqual(Foo.a.value, 1)
+ self.assertEqual(Foo.b.value, 3)
+
+ def test_multiple_arg_auto(self):
+ class AutoName(Enum):
+ def _generate_next_value_(name, start, count, last, *args, **kwds):
+ return (name, ) + args
+ #
+ class Planet(AutoName):
+ _init_ = 'value mass radius'
+ MERCURY = auto(3.303e+23, 2.4397e6)
+ VENUS = auto(4.869e+24, 6.0518e6)
+ self.assertEqual(Planet.MERCURY.value, 'MERCURY')
+
+ def test_auto_w_multiple_arg(self):
+ class AutoName(Enum):
+ def _generate_next_value_(name, start, count, last, *args, **kwds):
+ return (name, ) + args
+ #
+ class Planet(AutoName):
+ _init_ = 'value mass radius'
+ MERCURY = auto(), 3.303e+23, 2.4397e6 # doesn't work
+ VENUS = auto(), 4.869e+24, 6.0518e6 # doesn't work
+ self.assertEqual(Planet.MERCURY.value, 'MERCURY')
+
+ def test_auto_gnv_and_init(self):
+ class AutoName(Enum):
+ def _generate_next_value_(name, start, count, last, *args, **kwds):
+ return (name, ) + args
+ #
+ class Planet(AutoName):
+ _init_ = 'value mass radius'
+ MERCURY = 3.303e+23, 2.4397e6 # doesn't work
+ VENUS = 4.869e+24, 6.0518e6 # doesn't work
+ self.assertEqual(Planet.MERCURY.value, 'MERCURY')
+
+ # def test_AutoNumberEnum_and_property(self):
+ # class Color(aenum.AutoNumberEnum):
+ # red = ()
+ # green = ()
+ # blue = ()
+ # @property
+ # def cap_name(self):
+ # return self.name.title()
+ # self.assertEqual(Color.blue.cap_name, 'Blue')
+
+ # def test_AutoNumberEnum(self):
+ # class Color(aenum.AutoNumberEnum):
+ # _order_ = 'red green blue'
+ # red = ()
+ # green = ()
+ # blue = ()
+ # self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+ # self.assertEqual(Color.red.value, 1)
+ # self.assertEqual(Color.green.value, 2)
+ # self.assertEqual(Color.blue.value, 3)
+
+ def test_MultiValue_with_init_wo_value(self):
+ class Color(Enum):
+ _init_ = 'color r g b'
+ _order_ = 'red green blue'
+ _settings_ = MultiValue
+ red = 'red', 1, 2, 3
+ green = 'green', 4, 5, 6
+ blue = 'blue', 7, 8, 9
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.red.color, 'red')
+ self.assertEqual(Color.red.r, 1)
+ self.assertEqual(Color.red.g, 2)
+ self.assertEqual(Color.red.b, 3)
+ self.assertEqual(Color.green.value, 'green')
+ self.assertEqual(Color.green.color, 'green')
+ self.assertEqual(Color.green.r, 4)
+ self.assertEqual(Color.green.g, 5)
+ self.assertEqual(Color.green.b, 6)
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.blue.color, 'blue')
+ self.assertEqual(Color.blue.r, 7)
+ self.assertEqual(Color.blue.g, 8)
+ self.assertEqual(Color.blue.b, 9)
+ self.assertIs(Color('red'), Color.red)
+ self.assertIs(Color(1), Color.red)
+ self.assertIs(Color(2), Color.red)
+ self.assertIs(Color(3), Color.red)
+ self.assertIs(Color('green'), Color.green)
+ self.assertIs(Color(4), Color.green)
+ self.assertIs(Color(5), Color.green)
+ self.assertIs(Color(6), Color.green)
+ self.assertIs(Color('blue'), Color.blue)
+ self.assertIs(Color(7), Color.blue)
+ self.assertIs(Color(8), Color.blue)
+ self.assertIs(Color(9), Color.blue)
+
+ def test_MultiValue_with_init_w_value(self):
+ class Color(Enum):
+ _init_ = 'value r g b'
+ _order_ = 'red green blue'
+ _settings_ = MultiValue
+ red = 'red', 1, 2, 3
+ green = 'green', 4, 5, 6
+ blue = 'blue', 7, 8, 9
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.red.r, 1)
+ self.assertEqual(Color.red.g, 2)
+ self.assertEqual(Color.red.b, 3)
+ self.assertEqual(Color.green.value, 'green')
+ self.assertEqual(Color.green.r, 4)
+ self.assertEqual(Color.green.g, 5)
+ self.assertEqual(Color.green.b, 6)
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.blue.r, 7)
+ self.assertEqual(Color.blue.g, 8)
+ self.assertEqual(Color.blue.b, 9)
+ self.assertIs(Color('red'), Color.red)
+ self.assertIs(Color(1), Color.red)
+ self.assertIs(Color(2), Color.red)
+ self.assertIs(Color(3), Color.red)
+ self.assertIs(Color('green'), Color.green)
+ self.assertIs(Color(4), Color.green)
+ self.assertIs(Color(5), Color.green)
+ self.assertIs(Color(6), Color.green)
+ self.assertIs(Color('blue'), Color.blue)
+ self.assertIs(Color(7), Color.blue)
+ self.assertIs(Color(8), Color.blue)
+ self.assertIs(Color(9), Color.blue)
+
+ def test_MultiValue_with_init_wo_value_w_autonumber(self):
+ class Color(AutoNumberEnum):
+ _init_ = 'color r g b'
+ _order_ = 'red green blue'
+ _settings_ = MultiValue
+ red = 'red', 10, 20, 30
+ green = 'green', 40, 50, 60
+ blue = 'blue', 70, 80, 90
+ self.assertEqual(Color.red.value, 1)
+ self.assertEqual(Color.red.color, 'red')
+ self.assertEqual(Color.red.r, 10)
+ self.assertEqual(Color.red.g, 20)
+ self.assertEqual(Color.red.b, 30)
+ self.assertEqual(Color.green.value, 2)
+ self.assertEqual(Color.green.color, 'green')
+ self.assertEqual(Color.green.r, 40)
+ self.assertEqual(Color.green.g, 50)
+ self.assertEqual(Color.green.b, 60)
+ self.assertEqual(Color.blue.value, 3)
+ self.assertEqual(Color.blue.color, 'blue')
+ self.assertEqual(Color.blue.r, 70)
+ self.assertEqual(Color.blue.g, 80)
+ self.assertEqual(Color.blue.b, 90)
+ self.assertIs(Color(1), Color.red)
+ self.assertIs(Color('red'), Color.red)
+ self.assertIs(Color(10), Color.red)
+ self.assertIs(Color(20), Color.red)
+ self.assertIs(Color(30), Color.red)
+ self.assertIs(Color(2), Color.green)
+ self.assertIs(Color('green'), Color.green)
+ self.assertIs(Color(40), Color.green)
+ self.assertIs(Color(50), Color.green)
+ self.assertIs(Color(60), Color.green)
+ self.assertIs(Color(3), Color.blue)
+ self.assertIs(Color('blue'), Color.blue)
+ self.assertIs(Color(70), Color.blue)
+ self.assertIs(Color(80), Color.blue)
+ self.assertIs(Color(90), Color.blue)
+
+ def test_multivalue_and_autonumber_wo_init_wo_value(self):
+ class Day(Enum):
+ _settings_ = MultiValue, AddValue
+ _order_ = 'one two three'
+ _start_ = 7
+ one = "21", "one"
+ two = "22", "two"
+ three = "23", "three"
+ self.assertEqual(Day.one.value, 7)
+ self.assertEqual(Day.two.value, 8)
+ self.assertEqual(Day.three.value, 9)
+ self.assertEqual(Day('21'), Day.one)
+ self.assertEqual(Day('one'), Day.one)
+
+ def test_multivalue_and_autonumber_wo_init_w_some_value(self):
+ class Color(Enum):
+ _settings_ = MultiValue, Unique
+ _order_ = 'BLACK RED BLUE YELLOW GREEN MAGENTA'
+ _init_ = "value description"
+ BLACK = -1, "Text0"
+ RED = -50, "Text1"
+ BLUE = auto(), "Text2"
+ YELLOW = auto(), "Text3"
+ GREEN = -70, "Text4"
+ MAGENTA = auto(), "Text5"
+ self.assertEqual(Color.BLACK.value, -1)
+ self.assertEqual(Color.RED.value, -50)
+ self.assertEqual(Color.BLUE.value, -49)
+ self.assertEqual(Color.YELLOW.value, -48)
+ self.assertEqual(Color.GREEN.value, -70)
+ self.assertEqual(Color.MAGENTA.value, -69)
+ self.assertEqual(Color(-1), Color.BLACK)
+ self.assertEqual(Color('Text2'), Color.BLUE)
+
+ def test_combine_new_settings_with_old_settings(self):
+ class Auto(Enum):
+ _settings_ = Unique
+ with self.assertRaises(ValueError):
+ class AutoUnique(Auto):
+ BLAH = auto()
+ BLUH = auto()
+ ICK = 1
+
+ def test_timedelta(self):
+ class Period(timedelta, Enum):
+ '''
+ different lengths of time
+ '''
+ _init_ = 'value period'
+ _settings_ = NoAlias
+ _ignore_ = 'Period i'
+ Period = vars()
+ for i in range(31):
+ Period['day_%d' % i] = i, 'day'
+ for i in range(15):
+ Period['week_%d' % i] = i*7, 'week'
+ for i in range(12):
+ Period['month_%d' % i] = i*30, 'month'
+ OneDay = day_1
+ OneWeek = week_1
+ self.assertFalse(hasattr(Period, '_ignore_'))
+ self.assertFalse(hasattr(Period, 'Period'))
+ self.assertFalse(hasattr(Period, 'i'))
+ self.assertTrue(isinstance(Period.day_1, timedelta))
+
+ def test_skip(self):
+ class enumA(Enum):
+ @skip
+ class enumB(Enum):
+ elementA = 'a'
+ elementB = 'b'
+ @skip
+ class enumC(Enum):
+ elementC = 'c'
+ elementD = 'd'
+ self.assertIs(enumA.enumB, enumA.__dict__['enumB'])
+
+ def test_nonmember(self):
+ class enumA(Enum):
+ @nonmember
+ class enumB(Enum):
+ elementA = 'a'
+ elementB = 'b'
+ @nonmember
+ class enumC(Enum):
+ elementC = 'c'
+ elementD = 'd'
+ self.assertIs(enumA.enumB, enumA.__dict__['enumB'])
+
+ def test_member_with_external_functions(self):
+ class Func(Enum):
+ _order_ = 'an_int a_str'
+ an_int = member(int)
+ a_str = member(str)
+ @classproperty
+ def types(cls):
+ return [m.value for m in list(cls)]
+ def __repr__(self):
+ return "<%s.%s>" % (self.__class__.__name__, self.name, )
+ def __call__(self, *args, **kwds):
+ return self.value(*args, **kwds)
+ #
+ self.assertEqual([Func.an_int, Func.a_str], list(Func))
+ self.assertEqual([int, str], Func.types)
+ self.assertEqual(Func.an_int(7), 7)
+ self.assertEqual(Func.a_str('BlahBlah'), 'BlahBlah')
+
+ def test_member_with_internal_functions(self):
+ class Func(Enum):
+ _order_ = 'haha hehe'
+ @member
+ def haha():
+ return 'haha'
+ @member
+ def hehe(name):
+ return 'hehe -- what a name! %s!' % name
+ @classproperty
+ def types(cls):
+ return [m.value for m in list(cls)]
+ def __repr__(self):
+ return "<%s.%s>" % (self.__class__.__name__, self.name, )
+ def __call__(self, *args, **kwds):
+ return self.value(*args, **kwds)
+ #
+ self.assertEqual([Func.haha, Func.hehe], list(Func))
+ self.assertEqual([Func.haha.value, Func.hehe.value], Func.types)
+ self.assertEqual(Func.haha(), 'haha')
+ self.assertEqual(Func.hehe('BlahBlah'), 'hehe -- what a name! BlahBlah!')
+
+ 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, r'cannot rebind constant', setattr, Universe, 'PI', 9)
+ self.assertRaisesRegex(AttributeError, r'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)
+
+ def test_constant_with_auto_is_updated(self):
+ class Fruit(Flag):
+ _order_ = 'apple banana lemon orange'
+ apple = auto()
+ banana = auto()
+ lemon = auto()
+ orange = auto()
+ CitrusTypes = constant(lemon | orange)
+ self.assertEqual(list(Fruit), [Fruit.apple, Fruit.banana, Fruit.lemon, Fruit.orange])
+ self.assertEqual(list(Fruit.CitrusTypes), [Fruit.lemon, Fruit.orange])
+ self.assertTrue(Fruit.orange in Fruit.CitrusTypes)
+
+
+ def test_order_as_function(self):
+ # first with _init_
+ class TestSequence(Enum):
+ _init_ = 'value, sequence'
+ _order_ = lambda member: member.sequence
+ item_id = 'An$(1,6)', 0 # Item Code
+ company_id = 'An$(7,2)', 1 # Company Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ for i, member in enumerate(TestSequence):
+ self.assertEqual(i, member.sequence)
+ ts = TestSequence
+ self.assertEqual(ts.item_id.name, 'item_id')
+ self.assertEqual(ts.item_id.value, 'An$(1,6)')
+ self.assertEqual(ts.item_id.sequence, 0)
+ self.assertEqual(ts.company_id.name, 'company_id')
+ self.assertEqual(ts.company_id.value, 'An$(7,2)')
+ self.assertEqual(ts.company_id.sequence, 1)
+ self.assertEqual(ts.warehouse_no.name, 'warehouse_no')
+ self.assertEqual(ts.warehouse_no.value, 'An$(9,4)')
+ self.assertEqual(ts.warehouse_no.sequence, 2)
+ self.assertEqual(ts.company.name, 'company')
+ self.assertEqual(ts.company.value, 'Hn$(13,6)')
+ self.assertEqual(ts.company.sequence, 3)
+ self.assertEqual(ts.key_type.name, 'key_type')
+ self.assertEqual(ts.key_type.value, 'Cn$(19,3)')
+ self.assertEqual(ts.key_type.sequence, 4)
+ self.assertEqual(ts.available.name, 'available')
+ self.assertEqual(ts.available.value, 'Zn$(1,1)')
+ self.assertEqual(ts.available.sequence, 5)
+ self.assertEqual(ts.contract_item.name, 'contract_item')
+ self.assertEqual(ts.contract_item.value, 'Bn(2,1)')
+ self.assertEqual(ts.contract_item.sequence, 6)
+ self.assertEqual(ts.sales_category.name, 'sales_category')
+ self.assertEqual(ts.sales_category.value, 'Fn')
+ self.assertEqual(ts.sales_category.sequence, 7)
+ self.assertEqual(ts.gl_category.name, 'gl_category')
+ self.assertEqual(ts.gl_category.value, 'Rn$(5,1)')
+ self.assertEqual(ts.gl_category.sequence, 8)
+ self.assertEqual(ts.warehouse_category.name, 'warehouse_category')
+ self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)')
+ self.assertEqual(ts.warehouse_category.sequence, 9)
+ self.assertEqual(ts.inv_units.name, 'inv_units')
+ self.assertEqual(ts.inv_units.value, 'Qn$(7,2)')
+ self.assertEqual(ts.inv_units.sequence, 10)
+ # and then without
+ class TestSequence(Enum):
+ _order_ = lambda member: member.value[1]
+ item_id = 'An$(1,6)', 0 # Item Code
+ company_id = 'An$(7,2)', 1 # Company Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ for i, member in enumerate(TestSequence):
+ self.assertEqual(i, member.value[1])
+ ts = TestSequence
+ self.assertEqual(ts.item_id.name, 'item_id')
+ self.assertEqual(ts.item_id.value, ('An$(1,6)', 0))
+ self.assertEqual(ts.company_id.name, 'company_id')
+ self.assertEqual(ts.company_id.value, ('An$(7,2)', 1))
+ self.assertEqual(ts.warehouse_no.name, 'warehouse_no')
+ self.assertEqual(ts.warehouse_no.value, ('An$(9,4)', 2))
+ self.assertEqual(ts.company.name, 'company')
+ self.assertEqual(ts.company.value, ('Hn$(13,6)', 3))
+ self.assertEqual(ts.key_type.name, 'key_type')
+ self.assertEqual(ts.key_type.value, ('Cn$(19,3)', 4))
+ self.assertEqual(ts.available.name, 'available')
+ self.assertEqual(ts.available.value, ('Zn$(1,1)', 5))
+ self.assertEqual(ts.contract_item.name, 'contract_item')
+ self.assertEqual(ts.contract_item.value, ('Bn(2,1)', 6))
+ self.assertEqual(ts.sales_category.name, 'sales_category')
+ self.assertEqual(ts.sales_category.value, ('Fn', 7))
+ self.assertEqual(ts.gl_category.name, 'gl_category')
+ self.assertEqual(ts.gl_category.value, ('Rn$(5,1)', 8))
+ self.assertEqual(ts.warehouse_category.name, 'warehouse_category')
+ self.assertEqual(ts.warehouse_category.value, ('Sn$(6,1)', 9))
+ self.assertEqual(ts.inv_units.name, 'inv_units')
+ self.assertEqual(ts.inv_units.value, ('Qn$(7,2)', 10))
+ # then with _init_ but without value
+ with self.assertRaises(TypeError):
+ class TestSequence(Enum):
+ _init_ = 'sequence'
+ _order_ = lambda member: member.sequence
+ item_id = 'An$(1,6)', 0 # Item Code
+ company_id = 'An$(7,2)', 1 # Company Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ # finally, out of order so Python 3 barfs
+ with self.assertRaises(TypeError):
+ class TestSequence(Enum):
+ _init_ = 'sequence'
+ _order_ = lambda member: member.sequence
+ item_id = 'An$(1,6)', 0 # Item Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ company_id = 'An$(7,2)', 1 # Company Code
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+
+ def test_order_as_function_in_subclass(self):
+ #
+ class Parent(Enum):
+ _init_ = 'value sequence'
+ _order_ = lambda m: m.sequence
+ #
+ class Child(Parent):
+ item_id = 'An$(1,6)', 0 # Item Code
+ company_id = 'An$(7,2)', 1 # Company Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ #
+ for i, member in enumerate(Child):
+ self.assertEqual(i, member.sequence)
+ #
+ ts = Child
+ self.assertEqual(ts.item_id.name, 'item_id')
+ self.assertEqual(ts.item_id.value, 'An$(1,6)')
+ self.assertEqual(ts.item_id.sequence, 0)
+ self.assertEqual(ts.company_id.name, 'company_id')
+ self.assertEqual(ts.company_id.value, 'An$(7,2)')
+ self.assertEqual(ts.company_id.sequence, 1)
+ self.assertEqual(ts.warehouse_no.name, 'warehouse_no')
+ self.assertEqual(ts.warehouse_no.value, 'An$(9,4)')
+ self.assertEqual(ts.warehouse_no.sequence, 2)
+ self.assertEqual(ts.company.name, 'company')
+ self.assertEqual(ts.company.value, 'Hn$(13,6)')
+ self.assertEqual(ts.company.sequence, 3)
+ self.assertEqual(ts.key_type.name, 'key_type')
+ self.assertEqual(ts.key_type.value, 'Cn$(19,3)')
+ self.assertEqual(ts.key_type.sequence, 4)
+ self.assertEqual(ts.available.name, 'available')
+ self.assertEqual(ts.available.value, 'Zn$(1,1)')
+ self.assertEqual(ts.available.sequence, 5)
+ self.assertEqual(ts.contract_item.name, 'contract_item')
+ self.assertEqual(ts.contract_item.value, 'Bn(2,1)')
+ self.assertEqual(ts.contract_item.sequence, 6)
+ self.assertEqual(ts.sales_category.name, 'sales_category')
+ self.assertEqual(ts.sales_category.value, 'Fn')
+ self.assertEqual(ts.sales_category.sequence, 7)
+ self.assertEqual(ts.gl_category.name, 'gl_category')
+ self.assertEqual(ts.gl_category.value, 'Rn$(5,1)')
+ self.assertEqual(ts.gl_category.sequence, 8)
+ self.assertEqual(ts.warehouse_category.name, 'warehouse_category')
+ self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)')
+ self.assertEqual(ts.warehouse_category.sequence, 9)
+ self.assertEqual(ts.inv_units.name, 'inv_units')
+ self.assertEqual(ts.inv_units.value, 'Qn$(7,2)')
+ self.assertEqual(ts.inv_units.sequence, 10)
+
+ pass
+
+ def test_multiple_mixin(self):
+ class MaxMixin(object):
+ @classproperty
+ def MAX(cls):
+ max = len(cls)
+ cls.MAX = max
+ return max
+ class StrMixin(object):
+ def __str__(self):
+ return self._name_.lower()
+ class SomeEnum(Enum):
+ def behavior(self):
+ return 'booyah'
+ class AnotherEnum(Enum):
+ def behavior(self):
+ return 'nuhuh!'
+ def social(self):
+ return "what's up?"
+ class Color(MaxMixin, Enum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 3)
+ self.assertEqual(Color.MAX, 3)
+ self.assertEqual(str(Color.BLUE), 'Color.BLUE')
+ class Color(MaxMixin, StrMixin, Enum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 3)
+ self.assertEqual(Color.MAX, 3)
+ self.assertEqual(str(Color.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue'))
+ class Color(StrMixin, MaxMixin, Enum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 3)
+ self.assertEqual(Color.MAX, 3)
+ self.assertEqual(str(Color.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue'))
+ class CoolColor(StrMixin, SomeEnum, Enum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(CoolColor.RED.value, 1)
+ self.assertEqual(CoolColor.GREEN.value, 2)
+ self.assertEqual(CoolColor.BLUE.value, 3)
+ self.assertEqual(str(CoolColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue'))
+ self.assertEqual(CoolColor.RED.behavior(), 'booyah')
+ class CoolerColor(StrMixin, AnotherEnum, Enum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(CoolerColor.RED.value, 1)
+ self.assertEqual(CoolerColor.GREEN.value, 2)
+ self.assertEqual(CoolerColor.BLUE.value, 3)
+ self.assertEqual(str(CoolerColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue'))
+ self.assertEqual(CoolerColor.RED.behavior(), 'nuhuh!')
+ self.assertEqual(CoolerColor.RED.social(), "what's up?")
+ class CoolestColor(StrMixin, SomeEnum, AnotherEnum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(CoolestColor.RED.value, 1)
+ self.assertEqual(CoolestColor.GREEN.value, 2)
+ self.assertEqual(CoolestColor.BLUE.value, 3)
+ self.assertEqual(str(CoolestColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue'))
+ self.assertEqual(CoolestColor.RED.behavior(), 'booyah')
+ self.assertEqual(CoolestColor.RED.social(), "what's up?")
+ class ConfusedColor(StrMixin, AnotherEnum, SomeEnum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(ConfusedColor.RED.value, 1)
+ self.assertEqual(ConfusedColor.GREEN.value, 2)
+ self.assertEqual(ConfusedColor.BLUE.value, 3)
+ self.assertEqual(str(ConfusedColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue'))
+ self.assertEqual(ConfusedColor.RED.behavior(), 'nuhuh!')
+ self.assertEqual(ConfusedColor.RED.social(), "what's up?")
+ class ReformedColor(StrMixin, IntEnum, SomeEnum, AnotherEnum):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(ReformedColor.RED.value, 1)
+ self.assertEqual(ReformedColor.GREEN.value, 2)
+ self.assertEqual(ReformedColor.BLUE.value, 3)
+ self.assertEqual(str(ReformedColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue'))
+ self.assertEqual(ReformedColor.RED.behavior(), 'booyah')
+ self.assertEqual(ConfusedColor.RED.social(), "what's up?")
+ self.assertTrue(issubclass(ReformedColor, int))
+
+ def test_multiple_inherited_mixin(self):
+ @unique
+ class Decision1(StrEnum):
+ REVERT = "REVERT"
+ REVERT_ALL = "REVERT_ALL"
+ RETRY = "RETRY"
+ class MyEnum(StrEnum):
+ pass
+ @unique
+ class Decision2(MyEnum):
+ REVERT = "REVERT"
+ REVERT_ALL = "REVERT_ALL"
+ RETRY = "RETRY"
+
+ def test_value_auto_assign(self):
+ class Some(Enum):
+ def __new__(cls, val):
+ return object.__new__(cls)
+ x = 1
+ y = 2
+ self.assertEqual(Some.x.value, 1)
+ self.assertEqual(Some.y.value, 2)
+
+ def test_enum_of_types(self):
+ """Support using Enum to refer to types deliberately."""
+ class MyTypes(Enum):
+ i = int
+ f = float
+ s = str
+ self.assertEqual(MyTypes.i.value, int)
+ self.assertEqual(MyTypes.f.value, float)
+ self.assertEqual(MyTypes.s.value, str)
+ class Foo:
+ pass
+ class Bar:
+ pass
+ class MyTypes2(Enum):
+ a = Foo
+ b = Bar
+ self.assertEqual(MyTypes2.a.value, Foo)
+ self.assertEqual(MyTypes2.b.value, Bar)
+ class SpamEnumNotInner:
+ pass
+ class SpamEnum(Enum):
+ spam = SpamEnumNotInner
+ self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner)
+
+ if PY2:
+ def test_nested_classes_in_enum_do_become_members(self):
+ # manually set __qualname__ to remove testing framework noise
+ class Outer(Enum):
+ _order_ = 'a b Inner'
+ __qualname__ = "Outer"
+ a = 1
+ b = 2
+ class Inner(Enum):
+ __qualname__ = "Outer.Inner"
+ foo = 10
+ bar = 11
+ self.assertTrue(isinstance(Outer.Inner, Outer))
+ self.assertEqual(Outer.a.value, 1)
+ self.assertEqual(Outer.Inner.value.foo.value, 10)
+ self.assertEqual(
+ list(Outer.Inner.value),
+ [Outer.Inner.value.foo, Outer.Inner.value.bar],
+ )
+ self.assertEqual(
+ list(Outer),
+ [Outer.a, Outer.b, Outer.Inner],
+ )
+
+ def test_really_nested_classes_in_enum_do_become_members(self):
+ class Outer(Enum):
+ _order_ = 'a b Inner'
+ a = 1
+ b = 2
+ class Inner(Enum):
+ foo = 10
+ bar = 11
+ self.assertTrue(isinstance(Outer.Inner, Outer))
+ self.assertEqual(Outer.a.value, 1)
+ self.assertEqual(Outer.Inner.value.foo.value, 10)
+ self.assertEqual(
+ list(Outer.Inner.value),
+ [Outer.Inner.value.foo, Outer.Inner.value.bar],
+ )
+ self.assertEqual(
+ list(Outer),
+ [Outer.a, Outer.b, Outer.Inner],
+ )
+
+ def test_nested_classes_in_enum_are_skipped_with_skip(self):
+ """Support locally-defined nested classes using @skip"""
+ # manually set __qualname__ to remove testing framework noise
+ class Outer(Enum):
+ __qualname__ = "Outer"
+ a = 1
+ b = 2
+ @skip
+ class Inner(Enum):
+ __qualname__ = "Outer.Inner"
+ foo = 10
+ bar = 11
+ self.assertTrue(isinstance(Outer.Inner, type))
+ self.assertEqual(Outer.a.value, 1)
+ self.assertEqual(Outer.Inner.foo.value, 10)
+ self.assertEqual(
+ list(Outer.Inner),
+ [Outer.Inner.foo, Outer.Inner.bar],
+ )
+ self.assertEqual(
+ list(Outer),
+ [Outer.a, Outer.b],
+ )
+
+ def test_really_nested_classes_in_enum_are_skipped_with_skip(self):
+ """Support locally-defined nested classes using @skip"""
+ class Outer(Enum):
+ a = 1
+ b = 2
+ @skip
+ class Inner(Enum):
+ foo = 10
+ bar = 11
+ self.assertTrue(isinstance(Outer.Inner, type))
+ self.assertEqual(Outer.a.value, 1)
+ self.assertEqual(Outer.Inner.foo.value, 10)
+ self.assertEqual(
+ list(Outer.Inner),
+ [Outer.Inner.foo, Outer.Inner.bar],
+ )
+ self.assertEqual(
+ list(Outer),
+ [Outer.a, Outer.b],
+ )
+
+ def test_enum_call_without_arg(self):
+ class Color(Enum):
+ black = 0
+ red = 1
+ green = 2
+ blue = 3
+ #
+ @classmethod
+ def _missing_value_(cls, value):
+ if value is no_arg:
+ return cls.black
+ self.assertTrue(Color.red is Color(1))
+ self.assertTrue(Color.black is Color())
+
+ def test_init_subclass(self):
+ class MyEnum(Enum):
+ def __init_subclass__(cls, **kwds):
+ super(MyEnum, cls).__init_subclass__(**kwds)
+ self.assertFalse(cls.__dict__.get('_test', False))
+ cls._test1 = 'MyEnum'
+ #
+ class TheirEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ super(TheirEnum, cls).__init_subclass__(**kwds)
+ cls._test2 = 'TheirEnum'
+ class WhoseEnum(TheirEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NoEnum(WhoseEnum):
+ ONE = 1
+ self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum')
+ self.assertFalse(NoEnum.__dict__.get('_test1', False))
+ self.assertFalse(NoEnum.__dict__.get('_test2', False))
+ #
+ class OurEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ cls._test2 = 'OurEnum'
+ class WhereEnum(OurEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NeverEnum(WhereEnum):
+ ONE = 'one'
+ self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum')
+ self.assertFalse(WhereEnum.__dict__.get('_test1', False))
+ self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum')
+ self.assertFalse(NeverEnum.__dict__.get('_test1', False))
+ self.assertFalse(NeverEnum.__dict__.get('_test2', False))
+
+
+class TestStrEnum(TestCase):
+
+ def test_set_name(self):
+ class Descriptor(object):
+ name = None
+ def __get__(self, instance, owner_class=None):
+ if instance is None:
+ return self
+ else:
+ return instance.__dict__[self.name]
+ def __set__(self, instance, value):
+ instance.__dict__[self.name] = value
+ def __set_name__(self, owner, name):
+ self.name = name
+ #
+ class AnEnum(Enum):
+ ONE = 'one'
+ two = Descriptor()
+ #
+ self.assertEqual(list(AnEnum), [AnEnum.ONE])
+ self.assertEqual(AnEnum.two.name, 'two')
+ AnEnum.ONE.two = 'three'
+ self.assertEqual(AnEnum.ONE.two, 'three')
+ self.assertEqual(AnEnum.ONE.__dict__['two'], 'three')
+
+ def test_private_names(self):
+ class Private(Enum):
+ __corporal = 'Radar'
+ __major_ = 'Hoolihan'
+ self.assertEqual(len(Private), 0)
+ self.assertEqual(Private._Private__corporal, 'Radar')
+ self.assertFalse(isinstance(Private._Private__corporal, Enum))
+ self.assertEqual(Private._Private__major_, 'Hoolihan')
+ self.assertFalse(isinstance(Private._Private__major_, Enum))
+
+ def test_strenum_inherited_methods(self):
+ class phy(StrEnum):
+ pi = 'Pi'
+ tau = 'Tau'
+ self.assertTrue(phy.pi < phy.tau)
+ self.assertEqual(phy.pi.upper(), 'PI')
+ self.assertEqual(phy.tau.count('a'), 1)
+
+ def test_strict_strenum(self):
+ for uhoh in (object, object(), [], Enum, 9):
+ with self.assertRaisesRegex(TypeError, r'values must be str'):
+ class Huh(StrEnum):
+ huh = uhoh
+ class Either(StrEnum):
+ _order_ = 'this that Those lower upper'
+ this = auto()
+ that = 'That'
+ Those = auto()
+ lower = 'lower'
+ upper = 'UPPER'
+ self.assertEqual([m.value for m in Either], ['this', 'That', 'those', 'lower', 'UPPER'])
+ #
+ with self.assertRaisesRegex(ValueError, r' is not lower-case'):
+ class Huh(LowerStrEnum):
+ huh = 'What'
+ #
+ class Lower(LowerStrEnum):
+ _order_ = 'this that Those lower upper'
+ this = auto()
+ that = 'that'
+ Those = auto()
+ lower = 'lower'
+ upper = 'upper'
+ self.assertEqual([m.value for m in Lower], ['this', 'that', 'those', 'lower', 'upper'])
+ #
+ with self.assertRaisesRegex(ValueError, r' is not upper-case'):
+ class Huh(UpperStrEnum):
+ huh = 'What'
+ #
+ class Upper(UpperStrEnum):
+ _order_ = 'this that Those lower upper'
+ this = auto()
+ that = 'THAT'
+ Those = auto()
+ lower = 'LOWER'
+ upper = 'UPPER'
+ self.assertEqual([m.value for m in Upper], ['THIS', 'THAT', 'THOSE', 'LOWER', 'UPPER'])
+
+ def test_init_subclass(self):
+ class MyEnum(StrEnum):
+ def __init_subclass__(cls, **kwds):
+ super(MyEnum, cls).__init_subclass__(**kwds)
+ self.assertFalse(cls.__dict__.get('_test', False))
+ cls._test1 = 'MyEnum'
+ #
+ class TheirEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ super(TheirEnum, cls).__init_subclass__(**kwds)
+ cls._test2 = 'TheirEnum'
+ class WhoseEnum(TheirEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NoEnum(WhoseEnum):
+ ONE = 'one'
+ self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum')
+ self.assertFalse(NoEnum.__dict__.get('_test1', False))
+ self.assertFalse(NoEnum.__dict__.get('_test2', False))
+ #
+ class OurEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ cls._test2 = 'OurEnum'
+ class WhereEnum(OurEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NeverEnum(WhereEnum):
+ ONE = 'one'
+ self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum')
+ self.assertFalse(WhereEnum.__dict__.get('_test1', False))
+ self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum')
+ self.assertFalse(NeverEnum.__dict__.get('_test1', False))
+ self.assertFalse(NeverEnum.__dict__.get('_test2', False))
+
+
+class TestFlag(TestCase):
+ """Tests of the Flags."""
+
+ def setUp(self):
+ class Perm(Flag):
+ _order_ = 'R W X'
+ R, W, X = 4, 2, 1
+ self.Perm = Perm
+ #
+ class Color(Flag):
+ BLACK = 0
+ RED = 1
+ ROJO = 1
+ GREEN = 2
+ BLUE = 4
+ PURPLE = RED|BLUE
+ WHITE = RED|GREEN|BLUE
+ BLANCO = RED|GREEN|BLUE
+ self.Color = Color
+ #
+ class Fun(Flag):
+ _order_ = 'ONE TWO FOUR EIGHT'
+ ONE = auto()
+ TWO = auto()
+ THREE = ONE | TWO
+ FOUR = auto()
+ FIVE = FOUR | ONE
+ SIX = FOUR | TWO
+ SEVEN = FOUR | TWO | ONE
+ EIGHT = auto()
+ self.Fun = Fun
+ #
+ class TermColor(str, Flag):
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ #
+ @classmethod
+ def _create_pseudo_member_values_(cls, members, *values):
+ code = ';'.join(m.code for m in members)
+ return values + (code, )
+ #
+ AllReset = '0' # ESC [ 0 m # reset all (colors and brightness)
+ Bright = '1' # ESC [ 1 m # bright
+ Dim = '2' # ESC [ 2 m # dim (looks same as normal brightness)
+ Underline = '4'
+ Normal = '22' # ESC [ 22 m # normal brightness
+ #
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Yellow = '33' # ESC [ 33 m # yellow
+ FG_Blue = '34' # ESC [ 34 m # blue
+ FG_Magenta = '35' # ESC [ 35 m # magenta
+ FG_Cyan = '36' # ESC [ 36 m # cyan
+ FG_White = '37' # ESC [ 37 m # white
+ FG_Reset = '39' # ESC [ 39 m # reset
+ #
+ BG_Black = '40' # ESC [ 30 m # black
+ BG_Red = '41' # ESC [ 31 m # red
+ BG_Green = '42' # ESC [ 32 m # green
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Blue = '44' # ESC [ 34 m # blue
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ BG_Reset = '49' # ESC [ 39 m # reset
+ #
+ __str__ = str.__str__
+ #
+ def __repr__(self):
+ if self._name_ is not None:
+ return '<%s.%s>' % (self.__class__.__name__, self._name_)
+ else:
+ return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in Flag.__iter__(self)]))
+ #
+ def __enter__(self):
+ print(self.AllReset, end='', verbose=0)
+ return self
+ #
+ def __exit__(self, *args):
+ print(self.AllReset, end='', verbose=0)
+ self.TermColor = TermColor
+ #
+ class Open(Flag):
+ RO = 0
+ WO = 1
+ RW = 2
+ AC = 3
+ CE = 1<<19
+ self.Open = Open
+
+ def test_set_name(self):
+ class Descriptor(object):
+ name = None
+ def __get__(self, instance, owner_class=None):
+ if instance is None:
+ return self
+ else:
+ return instance.__dict__[self.name]
+ def __set__(self, instance, value):
+ instance.__dict__[self.name] = value
+ def __set_name__(self, owner, name):
+ self.name = name
+ #
+ class AnEnum(Enum):
+ ONE = 1
+ two = Descriptor()
+ #
+ self.assertEqual(list(AnEnum), [AnEnum.ONE])
+ self.assertEqual(AnEnum.two.name, 'two')
+ AnEnum.ONE.two = 'three'
+ self.assertEqual(AnEnum.ONE.two, 'three')
+ self.assertEqual(AnEnum.ONE.__dict__['two'], 'three')
+
+ def test_new_with_keywords(self):
+ class Huh(IntFlag):
+ __order__ = 'PLAIN BOLD_ITALIC HIGHLIGHT'
+ def __new__(cls, docstring, open=None, close=None):
+ if cls.__members__:
+ value = 2 ** (len(cls.__members__)-1)
+ else:
+ value = 0
+ member = int.__new__(cls, value)
+ if open and close is None:
+ close = open
+ member.open = open
+ member.close = close
+ member.__doc__ = docstring
+ member._value_ = value
+ return member
+ PLAIN = 'normal'
+ BOLD_ITALIC = '***really super important***', '***'
+ HIGHLIGHT = 'please ==take notice==', '==', '=='
+ p = Huh.PLAIN
+ self.assertTrue(type(p) is Huh, type(p))
+ self.assertEqual(
+ (p.value, p.__doc__, p.open, p.close),
+ (0, 'normal', None, None),
+ )
+ bi = Huh.BOLD_ITALIC
+ self.assertEqual(
+ (bi.value, bi.__doc__, bi.open, bi.close),
+ (1, '***really super important***', '***', '***'),
+ )
+ h = Huh.HIGHLIGHT
+ self.assertEqual(
+ (h.value, h.__doc__, h.open, h.close),
+ (2, 'please ==take notice==', '==', '=='),
+ )
+
+ def test_private_names(self):
+ class Private(Enum):
+ __corporal = 'Radar'
+ __major_ = 'Hoolihan'
+ self.assertEqual(len(Private), 0)
+ self.assertEqual(Private._Private__corporal, 'Radar')
+ self.assertFalse(isinstance(Private._Private__corporal, Enum))
+ self.assertEqual(Private._Private__major_, 'Hoolihan')
+ self.assertFalse(isinstance(Private._Private__major_, Enum))
+
+ def test_auto_alias(self):
+ Fun = self.Fun
+ self.assertEqual(
+ list(Fun),
+ [Fun.ONE, Fun.TWO, Fun.FOUR, Fun.EIGHT],
+ )
+ self.assertEqual(Fun.THREE._value_, 3)
+ self.assertEqual(repr(Fun.SEVEN), '<Fun.SEVEN: 7>')
+ self.assertEqual(list(Fun.SEVEN), [Fun.ONE, Fun.TWO, Fun.FOUR])
+
+ def test_str_is_str_str(self):
+ red, white = self.TermColor.FG_Red, self.TermColor.BG_White
+ barber = red | white
+ self.assertEqual(barber, '\x1b[31;47m')
+ self.assertEqual(barber.value, red.value | white.value)
+ self.assertEqual(barber.code, ';'.join([red.code, white.code]))
+ self.assertEqual(repr(barber), '<TermColor.FG_Red|BG_White>')
+ self.assertEqual(str(barber), '\x1b[31;47m')
+
+ def test_membership(self):
+ Color = self.Color
+ Open = self.Open
+ self.assertRaises(TypeError, lambda: 'BLACK' in Color)
+ self.assertRaises(TypeError, lambda: 'RO' in Open)
+ self.assertTrue(Color.BLACK in Color)
+ self.assertTrue(Open.RO in Open)
+ self.assertFalse(Color.BLACK in Open)
+ self.assertFalse(Open.RO in Color)
+ self.assertRaises(TypeError, lambda: 0 in Color)
+ self.assertRaises(TypeError, lambda: 0 in Open)
+
+ def test_member_contains(self):
+ Color = self.Color
+ self.assertRaises(TypeError, lambda: 'test' in Color.BLUE)
+ self.assertRaises(TypeError, lambda: 2 in Color.BLUE)
+ self.assertTrue(Color.BLUE in Color.BLUE)
+ self.assertTrue(Color.BLUE in Color['RED|GREEN|BLUE'])
+
+ def test_member_length(self):
+ self.assertEqual(self.Color.__len__(self.Color.BLACK), 0)
+ self.assertEqual(self.Color.__len__(self.Color.GREEN), 1)
+ self.assertEqual(self.Color.__len__(self.Color.PURPLE), 2)
+ self.assertEqual(self.Color.__len__(self.Color.BLANCO), 3)
+
+ def test_number_reset_and_order_cleanup(self):
+ class Confused(Flag):
+ _order_ = 'ONE TWO FOUR DOS EIGHT SIXTEEN'
+ ONE = auto()
+ TWO = auto()
+ FOUR = auto()
+ DOS = 2
+ EIGHT = auto()
+ SIXTEEN = auto()
+ self.assertEqual(
+ list(Confused),
+ [Confused.ONE, Confused.TWO, Confused.FOUR, Confused.EIGHT, Confused.SIXTEEN])
+ self.assertIs(Confused.TWO, Confused.DOS)
+ self.assertEqual(Confused.DOS._value_, 2)
+ self.assertEqual(Confused.EIGHT._value_, 8)
+ self.assertEqual(Confused.SIXTEEN._value_, 16)
+
+ def test_str(self):
+ Perm = self.Perm
+ self.assertEqual(str(Perm.R), 'Perm.R')
+ self.assertEqual(str(Perm.W), 'Perm.W')
+ self.assertEqual(str(Perm.X), 'Perm.X')
+ self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W')
+ self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X')
+ self.assertEqual(str(Perm(0)), 'Perm(0)')
+ self.assertEqual(str(~Perm.R), 'Perm.W|X')
+ self.assertEqual(str(~Perm.W), 'Perm.R|X')
+ self.assertEqual(str(~Perm.X), 'Perm.R|W')
+ self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X')
+ self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)')
+ self.assertEqual(str(Perm(-1)), 'Perm.R|W|X')
+ self.assertEqual(str(Perm(~0)), 'Perm.R|W|X')
+
+ Open = self.Open
+ self.assertEqual(str(Open.RO), 'Open.RO')
+ self.assertEqual(str(Open.WO), 'Open.WO')
+ self.assertEqual(str(Open.AC), 'Open.AC')
+ self.assertEqual(str(Open.RO | Open.CE), 'Open.CE')
+ self.assertEqual(str(Open.WO | Open.CE), 'Open.WO|CE')
+ self.assertEqual(str(~Open.RO), 'Open.WO|RW|CE')
+ self.assertEqual(str(~Open.WO), 'Open.RW|CE')
+ self.assertEqual(str(~Open.AC), 'Open.CE')
+ self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC')
+ self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW')
+
+ def test_repr(self):
+ Perm = self.Perm
+ self.assertEqual(repr(Perm.R), '<Perm.R: 4>')
+ self.assertEqual(repr(Perm.W), '<Perm.W: 2>')
+ self.assertEqual(repr(Perm.X), '<Perm.X: 1>')
+ self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>')
+ self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>')
+ self.assertEqual(repr(Perm(0)), '<Perm: 0>')
+ self.assertEqual(repr(~Perm.R), '<Perm.W|X: 3>')
+ self.assertEqual(repr(~Perm.W), '<Perm.R|X: 5>')
+ self.assertEqual(repr(~Perm.X), '<Perm.R|W: 6>')
+ self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X: 1>')
+ self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm: 0>')
+ self.assertEqual(repr(Perm(~0)), '<Perm.R|W|X: 7>')
+
+ Open = self.Open
+ self.assertEqual(repr(Open.RO), '<Open.RO: 0>')
+ self.assertEqual(repr(Open.WO), '<Open.WO: 1>')
+ self.assertEqual(repr(Open.AC), '<Open.AC: 3>')
+ self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>')
+ self.assertEqual(repr(Open.WO | Open.CE), '<Open.WO|CE: 524289>')
+ self.assertEqual(repr(~Open.RO), '<Open.WO|RW|CE: 524291>')
+ self.assertEqual(repr(~Open.WO), '<Open.RW|CE: 524290>')
+ self.assertEqual(repr(~Open.AC), '<Open.CE: 524288>')
+ self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>')
+ self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>')
+
+ def test_name_lookup(self):
+ Color = self.Color
+ self.assertTrue(Color.RED is Color['RED'])
+ self.assertTrue(Color.RED|Color.GREEN is Color['RED|GREEN'])
+ self.assertTrue(Color.PURPLE is Color['RED|BLUE'])
+
+ def test_or(self):
+ Perm = self.Perm
+ for i in Perm:
+ for j in Perm:
+ self.assertEqual((i | j), Perm(i.value | j.value))
+ self.assertEqual((i | j).value, i.value | j.value)
+ self.assertIs(type(i | j), Perm)
+ for i in Perm:
+ self.assertIs(i | i, i)
+ Open = self.Open
+ self.assertIs(Open.RO | Open.CE, Open.CE)
+
+ def test_and(self):
+ Perm = self.Perm
+ RW = Perm.R | Perm.W
+ RX = Perm.R | Perm.X
+ WX = Perm.W | Perm.X
+ RWX = Perm.R | Perm.W | Perm.X
+ values = list(Perm) + [RW, RX, WX, RWX, Perm(0)]
+ for i in values:
+ for j in values:
+ self.assertEqual((i & j).value, i.value & j.value)
+ self.assertIs(type(i & j), Perm)
+ for i in Perm:
+ self.assertIs(i & i, i)
+ self.assertIs(i & RWX, i)
+ self.assertIs(RWX & i, i)
+ Open = self.Open
+ self.assertIs(Open.RO & Open.CE, Open.RO)
+
+ def test_xor(self):
+ Perm = self.Perm
+ for i in Perm:
+ for j in Perm:
+ self.assertEqual((i ^ j).value, i.value ^ j.value)
+ self.assertIs(type(i ^ j), Perm)
+ for i in Perm:
+ self.assertIs(i ^ Perm(0), i)
+ self.assertIs(Perm(0) ^ i, i)
+ Open = self.Open
+ self.assertIs(Open.RO ^ Open.CE, Open.CE)
+ self.assertIs(Open.CE ^ Open.CE, Open.RO)
+
+ def test_invert(self):
+ Perm = self.Perm
+ RW = Perm.R | Perm.W
+ RX = Perm.R | Perm.X
+ WX = Perm.W | Perm.X
+ RWX = Perm.R | Perm.W | Perm.X
+ values = list(Perm) + [RW, RX, WX, RWX, Perm(0)]
+ for i in values:
+ self.assertIs(type(~i), Perm)
+ self.assertEqual(~~i, i)
+ for i in Perm:
+ self.assertIs(~~i, i)
+ Open = self.Open
+ self.assertIs(Open.WO & ~Open.WO, Open.RO)
+ self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE)
+
+ def test_bool(self):
+ Perm = self.Perm
+ for f in Perm:
+ self.assertTrue(f)
+ Open = self.Open
+ for f in Open:
+ self.assertEqual(bool(f.value), bool(f))
+
+ def test_doc_flag(self):
+ class DocFlag(Flag):
+ _init_ = 'value __doc__'
+ _start_ = 0
+ # def __new__(cls, value, doc=None):
+ # # if doc is None and isinstance(value, basestring):
+ # # value, doc = doc, value
+ # # if value is None:
+ # # if not len(cls):
+ # # value = 0
+ # # else:
+ # # value = 2 ** (len(cls) -1)
+ # # if not isinstance(value, baseinteger):
+ # # raise TypeError("%r is not a valid %s value" % (value, cls.__name__))
+ # obj = object.__new__(cls)
+ # # if doc is None, don't mess with the value
+ # if doc:
+ # value = value >> 1
+ # obj._value_ = value
+ # obj.__doc__ = doc
+ # return obj
+ #
+ class AddressSegment(DocFlag):
+ _order_ = 'UNKNOWN PO PO_TYPE NUMBER PREORD NAME STREET POSTORD SECONDARY_TYPE SECONDARY_NUMBER AND'
+ UNKNOWN = "unable to determine address element type"
+ PO = "post office delivery"
+ PO_TYPE = "box or drawer"
+ NUMBER = "main unit designator"
+ PREORD = "N S E W etc"
+ NAME = "street name"
+ STREET = "st ave blvd etc"
+ POSTORD = "N S E W etc"
+ SECONDARY_TYPE = "apt bldg floor etc"
+ SECONDARY_NUMBER = "secondary unit designator"
+ AND = "& indicates a corner address"
+ AS = AddressSegment
+ self.assertEqual(AS.NAME._value_, 16)
+ self.assertEqual(AS.STREET._value_, 32)
+ self.assertEqual(AS.SECONDARY_TYPE._value_, 128)
+ self.assertEqual((AS.NAME | AS.STREET)._value_, 48, "%r is not 48" % (AS.NAME | AS.STREET))
+
+ def test_iteration(self):
+ C = self.Color
+ self.assertEqual(list(C), [C.RED, C.GREEN, C.BLUE])
+ self.assertEqual(list(C.PURPLE), [C.RED, C.BLUE])
+
+ def test_member_iteration(self):
+ C = self.Color
+ self.assertEqual(list(C.BLACK), [])
+ self.assertEqual(list(C.RED), [C.RED])
+ self.assertEqual(list(C.PURPLE), [C.RED, C.BLUE])
+
+ def test_programatic_function_string(self):
+ Perm = Flag('Perm', 'R W X')
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<i
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_string_with_start(self):
+ Perm = Flag('Perm', 'R W X', start=8)
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 8<<i
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_string_list(self):
+ Perm = Flag('Perm', ['R', 'W', 'X'])
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<i
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_iterable(self):
+ Perm = Flag('Perm', (('R', 2), ('W', 8), ('X', 32)))
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<(2*i+1)
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_from_dict(self):
+ Perm = Flag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32))))
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<(2*i+1)
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_empty_list(self):
+ Perm = IntFlag('Perm', [])
+ self.assertEqual(len(list(Perm)), len(Perm))
+ self.assertEqual(len(Perm), 0)
+ Thing = Enum('Thing', [])
+ self.assertEqual(len(list(Thing)), len(Thing))
+ self.assertEqual(len(Thing), 0)
+
+ def test_programatic_function_empty_tuple(self):
+ Perm = IntFlag('Perm', ())
+ self.assertEqual(len(list(Perm)), len(Perm))
+ self.assertEqual(len(Perm), 0)
+ Thing = Enum('Thing', ())
+ self.assertEqual(len(list(Thing)), len(Thing))
+ self.assertEqual(len(Thing), 0)
+
+ def test_pickle(self):
+ if isinstance(FlagStooges, Exception):
+ raise FlagStooges
+ test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE)
+ test_pickle_dump_load(self.assertIs, FlagStooges)
+
+ def test_containment(self):
+ Perm = self.Perm
+ R, W, X = Perm
+ RW = R | W
+ RX = R | X
+ WX = W | X
+ RWX = R | W | X
+ self.assertTrue(R in RW)
+ self.assertTrue(R in RX)
+ self.assertTrue(R in RWX)
+ self.assertTrue(W in RW)
+ self.assertTrue(W in WX)
+ self.assertTrue(W in RWX)
+ self.assertTrue(X in RX)
+ self.assertTrue(X in WX)
+ self.assertTrue(X in RWX)
+ self.assertFalse(R in WX)
+ self.assertFalse(W in RX)
+ self.assertFalse(X in RW)
+
+ def test_auto_number(self):
+ class Color(Flag):
+ _order_ = 'red blue green'
+ red = auto()
+ blue = auto()
+ green = auto()
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 1)
+ self.assertEqual(Color.blue.value, 2)
+ self.assertEqual(Color.green.value, 4)
+
+ def test_auto_number_garbage(self):
+ with self.assertRaisesRegex(TypeError, r'invalid Flag value: .not an int.'):
+ class Color(Flag):
+ _order_ = 'red blue'
+ red = 'not an int'
+ blue = auto()
+
+ def test_auto_w_pending(self):
+ class Required(Flag):
+ _order_ = 'TO_S FROM_S'
+ NONE = 0
+ TO_S = auto()
+ FROM_S = auto()
+ BOTH = TO_S | FROM_S
+ self.assertEqual(Required.TO_S.value, 1)
+ self.assertEqual(Required.FROM_S.value, 2)
+ self.assertEqual(Required.BOTH.value, 3)
+
+ def test_duplicate_auto(self):
+ class Dupes(Enum):
+ _order_ = 'first second third'
+ first = primero = auto()
+ second = auto()
+ third = auto()
+ self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
+
+ def test_bizarre(self):
+ with self.assertRaisesRegex(TypeError, r"invalid Flag 'Bizarre' -- missing values: 1, 2"):
+ class Bizarre(Flag):
+ b = 3
+ c = 4
+ d = 6
+
+ def test_multiple_mixin(self):
+ class AllMixin(object):
+ @classproperty
+ def ALL(cls):
+ members = list(cls)
+ all_value = None
+ if members:
+ all_value = members[0]
+ for member in members[1:]:
+ all_value |= member
+ cls.ALL = all_value
+ return all_value
+ class StrMixin(object):
+ def __str__(self):
+ return self._name_.lower()
+ class Color(AllMixin, Flag):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 4)
+ self.assertEqual(Color.ALL.value, 7)
+ self.assertEqual(str(Color.BLUE), 'Color.BLUE')
+ class Color(AllMixin, StrMixin, Flag):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 4)
+ self.assertEqual(Color.ALL.value, 7)
+ self.assertEqual(str(Color.BLUE), 'blue')
+ class Color(StrMixin, AllMixin, Flag):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 4)
+ self.assertEqual(Color.ALL.value, 7)
+ self.assertEqual(str(Color.BLUE), 'blue')
+
+ @unittest.skipUnless(threading, 'Threading required for this test.')
+ def test_unique_composite(self):
+ # override __eq__ to be identity only
+ class TestFlag(Flag):
+ _order_ = 'one two three four five six seven eight'
+ one = auto()
+ two = auto()
+ three = auto()
+ four = auto()
+ five = auto()
+ six = auto()
+ seven = auto()
+ eight = auto()
+ def __eq__(self, other):
+ return self is other
+ def __hash__(self):
+ return hash(self._value_)
+ # have multiple threads competing to complete the composite members
+ seen = set()
+ failed = [False]
+ def cycle_enum():
+ # nonlocal failed
+ try:
+ for i in range(256):
+ seen.add(TestFlag(i))
+ except Exception:
+ failed[0] = True
+ threads = [
+ threading.Thread(target=cycle_enum)
+ for _ in range(8)
+ ]
+ for t in threads:
+ t.start()
+ for t in threads:
+ t.join()
+ # check that only 248 members were created (8 were created originally)
+ self.assertFalse(
+ failed[0],
+ 'at least one thread failed while creating composite members')
+ self.assertEqual(256, len(seen), 'too many composite members created')
+
+ def test_init_with_autovalue_and_generate_next_value(self):
+ class Color(Flag):
+ _init_ = 'value code'
+ def _generate_next_value_(name, start, count, last_values, *args, **kwds):
+ if not count:
+ return ((1, start)[start is not None], ) + args
+ error = False
+ for last_value in reversed(last_values):
+ try:
+ high_bit = aenum._high_bit(last_value)
+ break
+ except Exception:
+ error = True
+ break
+ if error:
+ raise TypeError('Invalid Flag value: %r' % (last_value, ))
+ return (2 ** (high_bit+1), ) + args
+ # TODO: actually test _create_pseudo_member
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ members = list(cls._iter_member_(value))
+ pseudo_member = super(Color, cls)._create_pseudo_member_(value)
+ pseudo_member.code = ';'.join(m.code for m in members)
+ return pseudo_member
+ AllReset = '0' # ESC [ 0 m # reset all (colors and brightness)
+ Bright = '1' # ESC [ 1 m # bright
+ Dim = '2' # ESC [ 2 m # dim (looks same as normal brightness)
+ Underline = '4'
+ Normal = '22' # ESC [ 22 m # normal brightness
+ # if we got here, we're good
+
+ def test_autovalue_and_generate_next_value(self):
+ class Color(str, Flag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ @staticmethod
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (2 ** count, ) + args
+ # TODO: actually test _create_pseudo_member
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ # calculate the code
+ members = list(cls._iter_member_(value))
+ code = ';'.join(m.code for m in members)
+ pseudo_member = super(Color, cls)._create_pseudo_member_(value, code)
+ return pseudo_member
+ #
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Blue = '34' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ # if we got here, we're good
+
+ def test_subclass(self):
+ class Color(str, Flag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ @staticmethod
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (2 ** count, ) + args
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ # calculate the code
+ members = list(cls._iter_member_(value))
+ code = ';'.join(m.code for m in members)
+ pseudo_member = super(Color, cls)._create_pseudo_member_(value, code)
+ return pseudo_member
+ #
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Blue = '34' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ self.assertTrue(isinstance(Color.FG_Black, Color))
+ self.assertTrue(isinstance(Color.FG_Black, str))
+ self.assertEqual(Color.FG_Black, '\x1b[30m')
+ self.assertEqual(Color.FG_Black.code, '30')
+
+ def test_sub_subclass_1(self):
+ class StrFlag(str, Flag):
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ # calculate the code
+ members = list(cls._iter_member_(value))
+ code = ';'.join(m.code for m in members)
+ pseudo_member = super(Color, cls)._create_pseudo_member_(value, code)
+ return pseudo_member
+ class Color(StrFlag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Blue = '34' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ self.assertTrue(isinstance(Color.FG_Black, Color))
+ self.assertTrue(isinstance(Color.FG_Black, str))
+ self.assertEqual(Color.FG_Black, '\x1b[30m')
+ self.assertEqual(Color.FG_Black.code, '30')
+
+ def test_sub_subclass_2(self):
+ class StrFlag(str, Flag):
+ @staticmethod
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (2 ** count, ) + args
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ # calculate the code
+ members = list(cls._iter_member_(value))
+ code = ';'.join(m.code for m in members)
+ pseudo_member = super(Color, cls)._create_pseudo_member_(value, code)
+ return pseudo_member
+ #
+ class Color(StrFlag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Blue = '34' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ self.assertTrue(isinstance(Color.FG_Black, Color))
+ self.assertTrue(isinstance(Color.FG_Black, str))
+ self.assertEqual(Color.FG_Black, '\x1b[30m')
+ self.assertEqual(Color.FG_Black.code, '30')
+
+ def test_sub_subclass_3(self):
+ class StrFlag(str, Flag):
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ # calculate the code
+ members = list(cls._iter_member_(value))
+ code = ';'.join(m.code for m in members)
+ pseudo_member = super(Color, cls)._create_pseudo_member_(value, code)
+ return pseudo_member
+ #
+ class Color(StrFlag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Blue = '34' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ self.assertTrue(isinstance(Color.FG_Black, Color))
+ self.assertTrue(isinstance(Color.FG_Black, str))
+ self.assertEqual(Color.FG_Black, '\x1b[30m')
+ self.assertEqual(Color.FG_Black.code, '30')
+
+ def test_sub_subclass_4(self):
+ class StrFlag(str, Flag):
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ @classmethod
+ def _create_pseudo_member_values_(cls, members, *values):
+ code = ';'.join(m.code for m in members)
+ return values + (code, )
+ #
+ class Color(StrFlag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Blue = '34' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ #
+ def __repr__(self):
+ return '<%s.%s>' % (self.__class__.__name__, self._name_)
+ self.assertTrue(isinstance(Color.FG_Black, Color))
+ self.assertTrue(isinstance(Color.FG_Black, str))
+ self.assertEqual(Color.FG_Black, '\x1b[30m')
+ self.assertEqual(Color.FG_Black.code, '30')
+ colors = Color.BG_Magenta | Color.FG_Black
+ self.assertTrue(isinstance(colors, Color))
+ self.assertTrue(isinstance(colors, str))
+ self.assertEqual(colors, '\x1b[30;45m')
+ self.assertEqual(colors.code, '30;45')
+ self.assertEqual(repr(colors), '<Color.FG_Black|BG_Magenta>')
+
+ def test_sub_subclass_with_new_new(self):
+ class StrFlag(str, Flag):
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ # calculate the code
+ members = list(cls._iter_member_(value))
+ code = ';'.join(m.code for m in members)
+ pseudo_member = super(StrFlag, cls)._create_pseudo_member_(value, code)
+ return pseudo_member
+ #
+ class Color(StrFlag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ def __new__(cls, value, string, abbr):
+ str_value = (abbr or '').title()
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = string
+ obj.abbr = abbr
+ return obj
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30', 'blk' # ESC [ 30 m # black
+ FG_Red = '31', 'red' # ESC [ 31 m # red
+ FG_Green = '32', 'grn' # ESC [ 32 m # green
+ FG_Blue = '34', 'blu' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43', 'ylw' # ESC [ 33 m # yellow
+ BG_Magenta = '45', 'mag' # ESC [ 35 m # magenta
+ BG_Cyan = '46', 'cyn' # ESC [ 36 m # cyan
+ BG_White = '47', 'wht' # ESC [ 37 m # white
+ #
+ def __repr__(self):
+ if self._name_ is not None:
+ return '<%s.%s>' % (self.__class__.__name__, self._name_)
+ else:
+ return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in self]))
+ self.assertTrue(isinstance(Color.FG_Black, Color))
+ self.assertTrue(isinstance(Color.FG_Black, str))
+ self.assertEqual(Color.FG_Black, 'Blk', str.__repr__(Color.FG_Black))
+ self.assertEqual(Color.FG_Black.abbr, 'blk')
+
+ def test_subclass_with_default_new(self):
+ class MyFlag(str, Flag):
+ _order_ = 'this these theother'
+ this = 'that'
+ these = 'those'
+ theother = 'thingimibobs'
+ self.assertEqual(MyFlag.this, 'that')
+ self.assertEqual(MyFlag.this.value, 1)
+ self.assertEqual(MyFlag.these, 'those')
+ self.assertEqual(MyFlag.these.value, 2)
+ self.assertEqual(MyFlag.theother, 'thingimibobs')
+ self.assertEqual(MyFlag.theother.value, 4)
+
+ def test_subclass_a_bunch(self):
+ class Color(str, Flag):
+ _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White'
+ def __new__(cls, value, code):
+ str_value = '\x1b[%sm' % code
+ obj = str.__new__(cls, str_value)
+ obj._value_ = value
+ obj.code = code
+ return obj
+ @staticmethod
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return (2 ** count, ) + args
+ @classmethod
+ def _create_pseudo_member_(cls, value):
+ # calculate the code
+ members = list(cls._iter_member_(value))
+ code = ';'.join(m.code for m in members)
+ pseudo_member = super(Color, cls)._create_pseudo_member_(value, code)
+ return pseudo_member
+ #
+ # # FOREGROUND - 30s BACKGROUND - 40s:
+ FG_Black = '30' # ESC [ 30 m # black
+ FG_Red = '31' # ESC [ 31 m # red
+ FG_Green = '32' # ESC [ 32 m # green
+ FG_Blue = '34' # ESC [ 34 m # blue
+ #
+ BG_Yellow = '43' # ESC [ 33 m # yellow
+ BG_Magenta = '45' # ESC [ 35 m # magenta
+ BG_Cyan = '46' # ESC [ 36 m # cyan
+ BG_White = '47' # ESC [ 37 m # white
+ #
+ def __repr__(self):
+ if self._name_ is not None:
+ return '<%s.%s>' % (self.__class__.__name__, self._name_)
+ else:
+ return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in self]))
+ #
+ Purple = Color.BG_Magenta | Color.FG_Blue
+ self.assertTrue(isinstance(Purple, Color))
+ self.assertTrue(isinstance(Purple, str))
+ self.assertIs(Purple, Color.BG_Magenta | Color.FG_Blue)
+ self.assertEqual(Purple, '\x1b[34;45m')
+ self.assertEqual(Purple.code, '34;45')
+ self.assertEqual(Purple.name, 'FG_Blue|BG_Magenta')
+
+ def test_init_subclass(self):
+ class MyEnum(Flag):
+ def __init_subclass__(cls, **kwds):
+ super(MyEnum, cls).__init_subclass__(**kwds)
+ self.assertFalse(cls.__dict__.get('_test', False))
+ cls._test1 = 'MyEnum'
+ #
+ class TheirEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ super(TheirEnum, cls).__init_subclass__(**kwds)
+ cls._test2 = 'TheirEnum'
+ class WhoseEnum(TheirEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NoEnum(WhoseEnum):
+ ONE = 1
+ self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum')
+ self.assertFalse(NoEnum.__dict__.get('_test1', False))
+ self.assertFalse(NoEnum.__dict__.get('_test2', False))
+ #
+ class OurEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ cls._test2 = 'OurEnum'
+ class WhereEnum(OurEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NeverEnum(WhereEnum):
+ ONE = 1
+ self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum')
+ self.assertFalse(WhereEnum.__dict__.get('_test1', False))
+ self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum')
+ self.assertFalse(NeverEnum.__dict__.get('_test1', False))
+ self.assertFalse(NeverEnum.__dict__.get('_test2', False))
+
+ def test_int_long_conversion(self):
+ class Perm(Flag):
+ EXEC = 1 << 0
+ WRITE = 1 << 1
+ READ = 1 << 2
+ MSB32 = 1 << 31
+ MSB64 = 1 << 63
+
+ # 32-bit system test
+ self.assertEqual(Perm.MSB32, Perm(0x80000000))
+ self.assertEqual(Perm.WRITE|Perm.MSB32, Perm(0x80000002))
+
+ # 64-bit system test
+ self.assertEqual(Perm.MSB64, Perm(0x8000000000000000))
+ self.assertEqual(Perm.MSB64|Perm.WRITE, Perm(0x8000000000000002))
+
+
+class TestIntFlag(TestCase):
+ """Tests of the IntFlags."""
+
+ def setUp(self):
+ #
+ class Perm(IntFlag):
+ _order_ = 'R W X'
+ R = 1 << 2
+ W = 1 << 1
+ X = 1 << 0
+ #
+ class Color(IntFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ PURPLE = RED|BLUE
+ #
+ class Open(IntFlag):
+ "not a good flag candidate"
+ RO = 0
+ WO = 1
+ RW = 2
+ AC = 3
+ CE = 1<<19
+ #
+ self.Perm = Perm
+ self.Color = Color
+ self.Open = Open
+
+ def test_set_name(self):
+ class Descriptor(object):
+ name = None
+ def __get__(self, instance, owner_class=None):
+ if instance is None:
+ return self
+ else:
+ return instance.__dict__[self.name]
+ def __set__(self, instance, value):
+ instance.__dict__[self.name] = value
+ def __set_name__(self, owner, name):
+ self.name = name
+ #
+ class AnEnum(Enum):
+ ONE = 1
+ two = Descriptor()
+ #
+ self.assertEqual(list(AnEnum), [AnEnum.ONE])
+ self.assertEqual(AnEnum.two.name, 'two')
+ AnEnum.ONE.two = 'three'
+ self.assertEqual(AnEnum.ONE.two, 'three')
+ self.assertEqual(AnEnum.ONE.__dict__['two'], 'three')
+
+ def test_private_names(self):
+ class Private(Enum):
+ __corporal = 'Radar'
+ __major_ = 'Hoolihan'
+ self.assertEqual(len(Private), 0)
+ self.assertEqual(Private._Private__corporal, 'Radar')
+ self.assertFalse(isinstance(Private._Private__corporal, Enum))
+ self.assertEqual(Private._Private__major_, 'Hoolihan')
+ self.assertFalse(isinstance(Private._Private__major_, Enum))
+
+ def test_membership(self):
+ Color = self.Color
+ Open = self.Open
+ self.assertRaises(TypeError, lambda: 'GREEN' in Color)
+ self.assertRaises(TypeError, lambda: 'RW' in Open)
+ self.assertTrue(Color.GREEN in Color)
+ self.assertTrue(Open.RW in Open)
+ self.assertFalse(Color.GREEN in Open)
+ self.assertFalse(Open.RW in Color)
+ self.assertRaises(TypeError, lambda: 2 in Color)
+ self.assertRaises(TypeError, lambda: 2 in Open)
+
+ def test_member_contains(self):
+ Color = self.Color
+ self.assertRaises(TypeError, lambda: 'test' in Color.RED)
+ self.assertRaises(TypeError, lambda: 1 in Color.RED)
+ self.assertTrue(Color.RED in Color.RED)
+ self.assertTrue(Color.RED in Color.PURPLE)
+
+ def test_name_lookup(self):
+ Color = self.Color
+ self.assertTrue(Color.RED is Color['RED'])
+ self.assertTrue(Color.RED|Color.GREEN is Color['RED|GREEN'])
+ self.assertTrue(Color.PURPLE is Color['RED|BLUE'])
+
+ def test_type(self):
+ Perm = self.Perm
+ Open = self.Open
+ for f in Perm:
+ self.assertTrue(isinstance(f, Perm))
+ self.assertEqual(f, f.value)
+ self.assertTrue(isinstance(Perm.W | Perm.X, Perm))
+ self.assertEqual(Perm.W | Perm.X, 3)
+ for f in Open:
+ self.assertTrue(isinstance(f, Open))
+ self.assertEqual(f, f.value)
+ self.assertTrue(isinstance(Open.WO | Open.RW, Open))
+ self.assertEqual(Open.WO | Open.RW, 3)
+
+
+ def test_str(self):
+ Perm = self.Perm
+ self.assertEqual(str(Perm.R), '4')
+ self.assertEqual(str(Perm.W), '2')
+ self.assertEqual(str(Perm.X), '1')
+ self.assertEqual(str(Perm.R | Perm.W), '6')
+ self.assertEqual(str(Perm.R | Perm.W | Perm.X), '7')
+ self.assertEqual(str(Perm(0)), '0')
+ self.assertEqual(str(~Perm.R), '3')
+ self.assertEqual(str(~Perm.W), '5')
+ self.assertEqual(str(~Perm.X), '6')
+ self.assertEqual(str(~(Perm.R | Perm.W)), '1')
+ self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), '0')
+ self.assertEqual(str(Perm(~0)), '7')
+
+ Open = self.Open
+ self.assertEqual(str(Open.RO), '0')
+ self.assertEqual(str(Open.WO), '1')
+ self.assertEqual(str(Open.AC), '3')
+ self.assertEqual(str(Open.RO | Open.CE), '524288')
+ self.assertEqual(str(Open.WO | Open.CE), '524289')
+ self.assertEqual(str(~Open.RO), '524291')
+ self.assertEqual(str(~Open.WO), '524290')
+ self.assertEqual(str(~Open.AC), '524288')
+ self.assertEqual(str(~(Open.RO | Open.CE)), '3')
+ self.assertEqual(str(~(Open.WO | Open.CE)), '2')
+
+ def test_repr_strict(self):
+ class Perm(IntFlag):
+ _order_ = 'R W X'
+ R = 1 << 2
+ W = 1 << 1
+ X = 1 << 0
+ Perm._boundary_ = aenum.STRICT
+ self.assertEqual(repr(Perm.R), '<Perm.R: 4>')
+ self.assertEqual(repr(Perm.W), '<Perm.W: 2>')
+ self.assertEqual(repr(Perm.X), '<Perm.X: 1>')
+ self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>')
+ self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>')
+ self.assertEqual(repr(Perm(0)), '<Perm: 0>')
+ self.assertEqual(repr(~Perm.R), '<Perm.W|X: 3>')
+ self.assertEqual(repr(~Perm.W), '<Perm.R|X: 5>')
+ self.assertEqual(repr(~Perm.X), '<Perm.R|W: 6>')
+ self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X: 1>')
+ self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm: 0>')
+ #
+ with self.assertRaisesRegex(ValueError, r'invalid value: 12'):
+ repr(Perm.R | 8)
+ with self.assertRaisesRegex(ValueError, r'invalid value: 12'):
+ repr(~(Perm.R | 8))
+ with self.assertRaisesRegex(ValueError, r'invalid value: -9'):
+ repr(Perm(~8))
+
+ def test_repr_conform(self):
+ class Perm(IntFlag):
+ _order_ = 'R W X'
+ R = 1 << 2
+ W = 1 << 1
+ X = 1 << 0
+ Perm._boundary_ = aenum.CONFORM
+ self.assertEqual(repr(Perm.R), '<Perm.R: 4>')
+ self.assertEqual(repr(Perm.W), '<Perm.W: 2>')
+ self.assertEqual(repr(Perm.X), '<Perm.X: 1>')
+ self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>')
+ self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>')
+ self.assertEqual(repr(Perm(0)), '<Perm: 0>')
+ self.assertEqual(repr(~Perm.R), '<Perm.W|X: 3>')
+ self.assertEqual(repr(~Perm.W), '<Perm.R|X: 5>')
+ self.assertEqual(repr(~Perm.X), '<Perm.R|W: 6>')
+ self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X: 1>')
+ self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm: 0>')
+ self.assertEqual(repr(Perm.R | 8), '<Perm.R: 4>')
+ self.assertEqual(repr(Perm(8)), '<Perm: 0>')
+ self.assertEqual(repr(~(Perm.R | 8)), '<Perm.W|X: 3>')
+ self.assertEqual(repr(Perm(~8)), '<Perm.R|W|X: 7>')
+
+ def test_repr_eject(self):
+ class Perm(IntFlag):
+ _order_ = 'R W X'
+ _boundary_ = EJECT
+ R = 1 << 2
+ W = 1 << 1
+ X = 1 << 0
+ self.assertEqual(repr(Perm.R), '<Perm.R: 4>')
+ self.assertEqual(repr(Perm.W), '<Perm.W: 2>')
+ self.assertEqual(repr(Perm.X), '<Perm.X: 1>')
+ self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>')
+ self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>')
+ self.assertEqual(repr(Perm(0)), '<Perm: 0>')
+ self.assertEqual(repr(~Perm.R), '<Perm.W|X: 3>')
+ self.assertEqual(repr(~Perm.W), '<Perm.R|X: 5>')
+ self.assertEqual(repr(~Perm.X), '<Perm.R|W: 6>')
+ self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X: 1>')
+ self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm: 0>')
+ self.assertEqual(repr(Perm.R | 8), '12')
+ self.assertEqual(repr(Perm(8)), '8')
+ self.assertEqual(repr(~(Perm.R | 8)), '-13')
+ self.assertEqual(repr(Perm(~8)), '-9')
+
+ def test_repr_open(self):
+ class Open(IntFlag):
+ "not a good flag candidate"
+ RO = 0
+ WO = 1
+ RW = 2
+ AC = 3
+ CE = 1<<19
+ Open._boundary_ = aenum.STRICT
+ self.assertEqual(repr(Open.RO), '<Open.RO: 0>')
+ self.assertEqual(repr(Open.WO), '<Open.WO: 1>')
+ self.assertEqual(repr(Open.AC), '<Open.AC: 3>')
+ self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>')
+ self.assertEqual(repr(Open.WO | Open.CE), '<Open.WO|CE: 524289>')
+ self.assertEqual(repr(~Open.RO), '<Open.WO|RW|CE: 524291>')
+ self.assertEqual(repr(~Open.WO), '<Open.RW|CE: 524290>')
+ self.assertEqual(repr(~Open.AC), '<Open.CE: 524288>')
+ self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>')
+ self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>')
+ with self.assertRaisesRegex(ValueError, r'invalid value: -5'):
+ repr(Open(~4))
+ with self.assertRaisesRegex(ValueError, r'invalid value: 4'):
+ repr(Open(4))
+ #
+ class Open(IntFlag):
+ "not a good flag candidate"
+ RO = 0
+ WO = 1
+ RW = 2
+ AC = 3
+ CE = 1<<19
+ Open._boundary_ = aenum.CONFORM
+ self.assertEqual(repr(Open.RO), '<Open.RO: 0>')
+ self.assertEqual(repr(Open.WO), '<Open.WO: 1>')
+ self.assertEqual(repr(Open.AC), '<Open.AC: 3>')
+ self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>')
+ self.assertEqual(repr(Open.WO | Open.CE), '<Open.WO|CE: 524289>')
+ self.assertEqual(repr(~Open.RO), '<Open.WO|RW|CE: 524291>')
+ self.assertEqual(repr(~Open.WO), '<Open.RW|CE: 524290>')
+ self.assertEqual(repr(~Open.AC), '<Open.CE: 524288>')
+ self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>')
+ self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>')
+ self.assertEqual(repr(Open(~4)), '<Open.WO|RW|CE: 524291>')
+ self.assertEqual(repr(Open(4)), '<Open.RO: 0>')
+ #
+ class Open(IntFlag):
+ "not a good flag candidate"
+ RO = 0
+ WO = 1
+ RW = 2
+ AC = 3
+ CE = 1<<19
+ Open._boundary_ = aenum.EJECT
+ self.assertEqual(repr(Open.RO), '<Open.RO: 0>')
+ self.assertEqual(repr(Open.WO), '<Open.WO: 1>')
+ self.assertEqual(repr(Open.AC), '<Open.AC: 3>')
+ self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>')
+ self.assertEqual(repr(Open.WO | Open.CE), '<Open.WO|CE: 524289>')
+ self.assertEqual(repr(~Open.RO), '<Open.WO|RW|CE: 524291>')
+ self.assertEqual(repr(~Open.WO), '<Open.RW|CE: 524290>')
+ self.assertEqual(repr(~Open.AC), '<Open.CE: 524288>')
+ self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>')
+ self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>')
+ self.assertEqual(repr(Open(~4)), '-5')
+ self.assertEqual(repr(Open(4)), '4')
+
+ def test_or(self):
+ Perm = self.Perm
+ for i in Perm:
+ for j in Perm:
+ self.assertEqual(i | j, i.value | j.value)
+ self.assertEqual((i | j).value, i.value | j.value)
+ self.assertIs(type(i | j), Perm)
+ for j in range(8):
+ self.assertEqual(i | j, i.value | j)
+ self.assertEqual((i | j).value, i.value | j)
+ self.assertIs(type(i | j), Perm)
+ self.assertEqual(j | i, j | i.value)
+ self.assertEqual((j | i).value, j | i.value)
+ self.assertIs(type(j | i), Perm)
+ for i in Perm:
+ self.assertIs(i | i, i)
+ self.assertIs(i | 0, i)
+ self.assertIs(0 | i, i)
+ Open = self.Open
+ self.assertIs(Open.RO | Open.CE, Open.CE)
+
+ def test_and(self):
+ Perm = self.Perm
+ RW = Perm.R | Perm.W
+ RX = Perm.R | Perm.X
+ WX = Perm.W | Perm.X
+ RWX = Perm.R | Perm.W | Perm.X
+ values = list(Perm) + [RW, RX, WX, RWX, Perm(0)]
+ for i in values:
+ for j in values:
+ self.assertEqual(i & j, i.value & j.value, 'i is %r, j is %r' % (i, j))
+ self.assertEqual((i & j).value, i.value & j.value, 'i is %r, j is %r' % (i, j))
+ self.assertIs(type(i & j), Perm, 'i is %r, j is %r' % (i, j))
+ for j in range(8):
+ self.assertEqual(i & j, i.value & j)
+ self.assertEqual((i & j).value, i.value & j)
+ self.assertIs(type(i & j), Perm)
+ self.assertEqual(j & i, j & i.value)
+ self.assertEqual((j & i).value, j & i.value)
+ self.assertIs(type(j & i), Perm)
+ for i in Perm:
+ self.assertIs(i & i, i)
+ self.assertIs(i & 7, i)
+ self.assertIs(7 & i, i)
+ Open = self.Open
+ self.assertIs(Open.RO & Open.CE, Open.RO)
+
+ def test_xor(self):
+ Perm = self.Perm
+ for i in Perm:
+ for j in Perm:
+ self.assertEqual(i ^ j, i.value ^ j.value)
+ self.assertEqual((i ^ j).value, i.value ^ j.value)
+ self.assertIs(type(i ^ j), Perm)
+ for j in range(8):
+ self.assertEqual(i ^ j, i.value ^ j)
+ self.assertEqual((i ^ j).value, i.value ^ j)
+ self.assertIs(type(i ^ j), Perm)
+ self.assertEqual(j ^ i, j ^ i.value)
+ self.assertEqual((j ^ i).value, j ^ i.value)
+ self.assertIs(type(j ^ i), Perm)
+ for i in Perm:
+ self.assertIs(i ^ 0, i)
+ self.assertIs(0 ^ i, i)
+ Open = self.Open
+ self.assertIs(Open.RO ^ Open.CE, Open.CE)
+ self.assertIs(Open.CE ^ Open.CE, Open.RO)
+
+ def test_invert(self):
+ Perm = self.Perm
+ RW = Perm.R | Perm.W
+ RX = Perm.R | Perm.X
+ WX = Perm.W | Perm.X
+ RWX = Perm.R | Perm.W | Perm.X
+ values = list(Perm) + [RW, RX, WX, RWX, Perm(0)]
+ for i in values:
+ self.assertEqual(~i, (~i).value)
+ self.assertIs(type(~i), Perm)
+ self.assertEqual(~~i, i)
+ for i in Perm:
+ self.assertIs(~~i, i)
+ Open = self.Open
+ self.assertIs(Open.WO & ~Open.WO, Open.RO)
+ self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE)
+
+ def test_iter(self):
+ Perm = self.Perm
+ NoPerm = Perm.R ^ Perm.R
+ RWX = Perm.R | Perm.W | Perm.X
+ self.assertEqual(list(NoPerm), [])
+ self.assertEqual(list(Perm.R), [Perm.R])
+ self.assertEqual(list(RWX), [Perm.R, Perm.W, Perm.X])
+
+ def test_programatic_function_string(self):
+ Perm = IntFlag('Perm', 'R W X')
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<i
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e, v)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_string_with_start(self):
+ Perm = IntFlag('Perm', 'R W X', start=8)
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 8<<i
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e, v)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_string_list(self):
+ Perm = IntFlag('Perm', ['R', 'W', 'X'])
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<i
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e, v)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_iterable(self):
+ Perm = IntFlag('Perm', (('R', 2), ('W', 8), ('X', 32)))
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<(2*i+1)
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e, v)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_programatic_function_from_dict(self):
+ Perm = IntFlag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32))))
+ lst = list(Perm)
+ self.assertEqual(len(lst), len(Perm))
+ self.assertEqual(len(Perm), 3, Perm)
+ self.assertEqual(lst, [Perm.R, Perm.W, Perm.X])
+ for i, n in enumerate('R W X'.split()):
+ v = 1<<(2*i+1)
+ e = Perm(v)
+ self.assertEqual(e.value, v)
+ self.assertEqual(type(e.value), int)
+ self.assertEqual(e, v)
+ self.assertEqual(e.name, n)
+ self.assertIn(e, Perm)
+ self.assertIs(type(e), Perm)
+
+ def test_containment(self):
+ Perm = self.Perm
+ R, W, X = Perm
+ RW = R | W
+ RX = R | X
+ WX = W | X
+ RWX = R | W | X
+ self.assertTrue(R in RW)
+ self.assertTrue(R in RX)
+ self.assertTrue(R in RWX)
+ self.assertTrue(W in RW)
+ self.assertTrue(W in WX)
+ self.assertTrue(W in RWX)
+ self.assertTrue(X in RX)
+ self.assertTrue(X in WX)
+ self.assertTrue(X in RWX)
+ self.assertFalse(R in WX)
+ self.assertFalse(W in RX)
+ self.assertFalse(X in RW)
+
+ def test_bool(self):
+ Perm = self.Perm
+ for f in Perm:
+ self.assertTrue(f)
+ Open = self.Open
+ for f in Open:
+ self.assertEqual(bool(f.value), bool(f))
+
+ def test_multiple_mixin(self):
+ class AllMixin(object):
+ @classproperty
+ def ALL(cls):
+ members = list(cls)
+ all_value = None
+ if members:
+ all_value = members[0]
+ for member in members[1:]:
+ all_value |= member
+ cls.ALL = all_value
+ return all_value
+ class StrMixin(object):
+ def __str__(self):
+ return self._name_.lower()
+ class Color(AllMixin, IntFlag):
+ _order_ = 'RED GREEN BLUE'
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 4)
+ self.assertEqual(Color.ALL.value, 7)
+ self.assertEqual(str(Color.BLUE), '4')
+ class Color(AllMixin, StrMixin, IntFlag):
+ _order_ = 'RED GREEN BLUE'
+ __str__ = StrMixin.__str__
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 4)
+ self.assertEqual(Color.ALL.value, 7)
+ self.assertEqual(str(Color.BLUE), 'blue')
+ class Color(StrMixin, AllMixin, IntFlag):
+ _order_ = 'RED GREEN BLUE'
+ __str__ = StrMixin.__str__
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.GREEN.value, 2)
+ self.assertEqual(Color.BLUE.value, 4)
+ self.assertEqual(Color.ALL.value, 7)
+ self.assertEqual(str(Color.BLUE), 'blue')
+
+ @unittest.skipUnless(threading, 'Threading required for this test.')
+ def test_unique_composite(self):
+ # override __eq__ to be identity only
+ class TestFlag(IntFlag):
+ _order_ = 'one two three four five six seven eight'
+ one = auto()
+ two = auto()
+ three = auto()
+ four = auto()
+ five = auto()
+ six = auto()
+ seven = auto()
+ eight = auto()
+ def __eq__(self, other):
+ return self is other
+ def __hash__(self):
+ return hash(self._value_)
+ # have multiple threads competing to complete the composite members
+ seen = set()
+ failed = [False]
+ def cycle_enum():
+ # nonlocal failed
+ try:
+ for i in range(256):
+ seen.add(TestFlag(i))
+ except Exception:
+ failed[0] = True
+ threads = [
+ threading.Thread(target=cycle_enum)
+ for _ in range(8)
+ ]
+ for t in threads:
+ t.start()
+ for t in threads:
+ t.join()
+ # check that only 248 members were created (8 were created originally)
+ self.assertFalse(
+ failed[0],
+ 'at least one thread failed while creating composite members')
+ self.assertEqual(256, len(seen), 'too many composite members created')
+
+ def test_init_subclass(self):
+ class MyEnum(IntEnum):
+ def __init_subclass__(cls, **kwds):
+ super(MyEnum, cls).__init_subclass__(**kwds)
+ self.assertFalse(cls.__dict__.get('_test', False))
+ cls._test1 = 'MyEnum'
+ #
+ class TheirEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ super(TheirEnum, cls).__init_subclass__(**kwds)
+ cls._test2 = 'TheirEnum'
+ class WhoseEnum(TheirEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NoEnum(WhoseEnum):
+ ONE = 1
+ self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum')
+ self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum')
+ self.assertFalse(NoEnum.__dict__.get('_test1', False))
+ self.assertFalse(NoEnum.__dict__.get('_test2', False))
+ #
+ class OurEnum(MyEnum):
+ def __init_subclass__(cls, **kwds):
+ cls._test2 = 'OurEnum'
+ class WhereEnum(OurEnum):
+ def __init_subclass__(cls, **kwds):
+ pass
+ class NeverEnum(WhereEnum):
+ ONE = 1
+ self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum')
+ self.assertFalse(WhereEnum.__dict__.get('_test1', False))
+ self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum')
+ self.assertFalse(NeverEnum.__dict__.get('_test1', False))
+ self.assertFalse(NeverEnum.__dict__.get('_test2', False))
+
+
+
+class TestEmptyAndNonLatinStrings(unittest.TestCase):
+
+ def test_empty_string(self):
+ with self.assertRaises(ValueError):
+ empty_abc = Enum('empty_abc', ('', 'B', 'C'))
+
+ def test_non_latin_character_string(self):
+ greek_abc = Enum('greek_abc', ('\u03B1', 'B', 'C'))
+ item = getattr(greek_abc, '\u03B1')
+ self.assertEqual(item.value, 1)
+
+ def test_non_latin_number_string(self):
+ hebrew_123 = Enum('hebrew_123', ('\u05D0', '2', '3'))
+ item = getattr(hebrew_123, '\u05D0')
+ self.assertEqual(item.value, 1)
+
+
+class TestUnique(TestCase):
+ """2.4 doesn't allow class decorators, use function syntax."""
+
+ def test_unique_clean(self):
+ class Clean(Enum):
+ one = 1
+ two = 'dos'
+ tres = 4.0
+ unique(Clean)
+ class Cleaner(IntEnum):
+ single = 1
+ double = 2
+ triple = 3
+ unique(Cleaner)
+
+ def test_unique_dirty(self):
+ try:
+ class Dirty(Enum):
+ __order__ = 'one two'
+ one = 1
+ two = 'dos'
+ tres = 1
+ unique(Dirty)
+ except ValueError:
+ exc = sys.exc_info()[1]
+ message = exc.args[0]
+ self.assertTrue('tres -> one' in message)
+
+ try:
+ class Dirtier(IntEnum):
+ __order__ = 'single triple'
+ single = 1
+ double = 1
+ triple = 3
+ turkey = 3
+ unique(Dirtier)
+ except ValueError:
+ exc = sys.exc_info()[1]
+ message = exc.args[0]
+ self.assertTrue('double -> single' in message)
+ self.assertTrue('turkey -> triple' in message)
+
+ def test_unique_with_name(self):
+ @unique
+ class Silly(Enum):
+ one = 1
+ two = 'dos'
+ name = 3
+ @unique
+ class Sillier(IntEnum):
+ single = 1
+ name = 2
+ triple = 3
+ value = 4
+
+
+class TestNamedTuple(TestCase):
+
+ def test_explicit_indexing(self):
+ class Person(NamedTuple):
+ age = 0
+ first = 1
+ last = 2
+ p1 = Person(17, 'John', 'Doe')
+ p2 = Person(21, 'Jane', 'Doe')
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p2[0], 21)
+ self.assertEqual(p2[1], 'Jane')
+ self.assertEqual(p2[2], 'Doe')
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.first, 'John')
+ self.assertEqual(p1.last, 'Doe')
+ self.assertEqual(p2.age, 21)
+ self.assertEqual(p2.first, 'Jane')
+ self.assertEqual(p2.last, 'Doe')
+
+ def test_implicit_indexing(self):
+ class Person(NamedTuple):
+ __order__ = "age first last"
+ age = "person's age"
+ first = "person's first name"
+ last = "person's last name"
+ p1 = Person(17, 'John', 'Doe')
+ p2 = Person(21, 'Jane', 'Doe')
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p2[0], 21)
+ self.assertEqual(p2[1], 'Jane')
+ self.assertEqual(p2[2], 'Doe')
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.first, 'John')
+ self.assertEqual(p1.last, 'Doe')
+ self.assertEqual(p2.age, 21)
+ self.assertEqual(p2.first, 'Jane')
+ self.assertEqual(p2.last, 'Doe')
+
+ def test_mixed_indexing(self):
+ class Person(NamedTuple):
+ __order__ = "age last cars"
+ age = "person's age"
+ last = 2, "person's last name"
+ cars = "person's cars"
+ p1 = Person(17, 'John', 'Doe', 3)
+ p2 = Person(21, 'Jane', 'Doe', 9)
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p1[3], 3)
+ self.assertEqual(p2[0], 21)
+ self.assertEqual(p2[1], 'Jane')
+ self.assertEqual(p2[2], 'Doe')
+ self.assertEqual(p2[3], 9)
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.last, 'Doe')
+ self.assertEqual(p1.cars, 3)
+ self.assertEqual(p2.age, 21)
+ self.assertEqual(p2.last, 'Doe')
+ self.assertEqual(p2.cars, 9)
+
+ def test_issubclass(self):
+ class Person(NamedTuple):
+ age = 0
+ first = 1
+ last = 2
+ self.assertTrue(issubclass(Person, NamedTuple))
+ self.assertTrue(issubclass(Person, tuple))
+
+ def test_isinstance(self):
+ class Person(NamedTuple):
+ age = 0
+ first = 1
+ last = 2
+ p1 = Person(17, 'John', 'Doe')
+ self.assertTrue(isinstance(p1, Person))
+ self.assertTrue(isinstance(p1, NamedTuple))
+ self.assertTrue(isinstance(p1, tuple))
+
+ def test_explicit_indexing_after_functional_api(self):
+ Person = NamedTuple('Person', (('age', 0), ('first', 1), ('last', 2)))
+ p1 = Person(17, 'John', 'Doe')
+ p2 = Person(21, 'Jane', 'Doe')
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p2[0], 21)
+ self.assertEqual(p2[1], 'Jane')
+ self.assertEqual(p2[2], 'Doe')
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.first, 'John')
+ self.assertEqual(p1.last, 'Doe')
+ self.assertEqual(p2.age, 21)
+ self.assertEqual(p2.first, 'Jane')
+ self.assertEqual(p2.last, 'Doe')
+
+ def test_implicit_indexing_after_functional_api(self):
+ Person = NamedTuple('Person', 'age first last')
+ p1 = Person(17, 'John', 'Doe')
+ p2 = Person(21, 'Jane', 'Doe')
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p2[0], 21)
+ self.assertEqual(p2[1], 'Jane')
+ self.assertEqual(p2[2], 'Doe')
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.first, 'John')
+ self.assertEqual(p1.last, 'Doe')
+ self.assertEqual(p2.age, 21)
+ self.assertEqual(p2.first, 'Jane')
+ self.assertEqual(p2.last, 'Doe')
+
+ def test_mixed_indexing_after_functional_api(self):
+ Person = NamedTuple('Person', (('age', 0), ('last', 2), ('cars', 3)))
+ p1 = Person(17, 'John', 'Doe', 3)
+ p2 = Person(21, 'Jane', 'Doe', 9)
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p1[3], 3)
+ self.assertEqual(p2[0], 21)
+ self.assertEqual(p2[1], 'Jane')
+ self.assertEqual(p2[2], 'Doe')
+ self.assertEqual(p2[3], 9)
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.last, 'Doe')
+ self.assertEqual(p1.cars, 3)
+ self.assertEqual(p2.age, 21)
+ self.assertEqual(p2.last, 'Doe')
+ self.assertEqual(p2.cars, 9)
+
+ def test_issubclass_after_functional_api(self):
+ Person = NamedTuple('Person', 'age first last')
+ self.assertTrue(issubclass(Person, NamedTuple))
+ self.assertTrue(issubclass(Person, tuple))
+
+ def test_isinstance_after_functional_api(self):
+ Person = NamedTuple('Person', 'age first last')
+ p1 = Person(17, 'John', 'Doe')
+ self.assertTrue(isinstance(p1, Person))
+ self.assertTrue(isinstance(p1, NamedTuple))
+ self.assertTrue(isinstance(p1, tuple))
+
+ def test_creation_with_all_keywords(self):
+ Person = NamedTuple('Person', 'age first last')
+ p1 = Person(age=17, first='John', last='Doe')
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.first, 'John')
+ self.assertEqual(p1.last, 'Doe')
+
+ def test_creation_with_some_keywords(self):
+ Person = NamedTuple('Person', 'age first last')
+ p1 = Person(17, first='John', last='Doe')
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.first, 'John')
+ self.assertEqual(p1.last, 'Doe')
+ p1 = Person(17, last='Doe', first='John')
+ self.assertEqual(p1[0], 17)
+ self.assertEqual(p1[1], 'John')
+ self.assertEqual(p1[2], 'Doe')
+ self.assertEqual(p1.age, 17)
+ self.assertEqual(p1.first, 'John')
+ self.assertEqual(p1.last, 'Doe')
+
+ def test_custom_new(self):
+ class Book(NamedTuple):
+ title = 0
+ author = 1
+ genre = 2
+ def __new__(cls, string):
+ args = [s.strip() for s in string.split(';')]
+ return super(Book, cls).__new__(cls, *tuple(args))
+ b1 = Book('The Last Mohican; John Doe; Historical')
+ self.assertEqual(b1.title, 'The Last Mohican')
+ self.assertEqual(b1.author, 'John Doe')
+ self.assertEqual(b1.genre, 'Historical')
+
+ def test_defaults_in_class(self):
+ class Character(NamedTuple):
+ name = 0
+ gender = 1, None, 'male'
+ klass = 2, None, 'fighter'
+ for char in (
+ {'name':'John Doe'},
+ {'name':'William Pickney', 'klass':'scholar'},
+ {'name':'Sarah Doughtery', 'gender':'female'},
+ {'name':'Sissy Moonbeam', 'gender':'female', 'klass':'sorceress'},
+ ):
+ c = Character(**char)
+ for name, value in (('name', None), ('gender','male'), ('klass','fighter')):
+ if name in char:
+ value = char[name]
+ self.assertEqual(getattr(c, name), value)
+
+ def test_defaults_in_class_that_are_falsey(self):
+ class Point(NamedTuple):
+ x = 0, 'horizondal coordinate', 0
+ y = 1, 'vertical coordinate', 0
+ p = Point()
+ self.assertEqual(p.x, 0)
+ self.assertEqual(p.y, 0)
+
+ def test_pickle_namedtuple_with_module(self):
+ if isinstance(LifeForm, Exception):
+ raise LifeForm
+ lf = LifeForm('this', 'that', 'theother')
+ test_pickle_dump_load(self.assertEqual, lf)
+
+ def test_pickle_namedtuple_without_module(self):
+ if isinstance(DeathForm, Exception):
+ raise DeathForm
+ df = DeathForm('sickly green', '2x4', 'foul')
+ test_pickle_dump_load(self.assertEqual, df)
+
+ def test_subclassing(self):
+ if isinstance(ThatsIt, Exception):
+ raise ThatsIt
+ ti = ThatsIt('Henry', 'Weinhardt')
+ self.assertEqual(ti.blah, 'Henry')
+ self.assertTrue(ti.what(), 'Henry')
+ test_pickle_dump_load(self.assertEqual, ti)
+
+ def test_contains(self):
+ Book = NamedTuple('Book', 'title author genre')
+ b = Book('Teckla', 'Steven Brust', 'fantasy')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertTrue('fantasy' in b)
+
+ def test_fixed_size(self):
+ class Book(NamedTuple):
+ _size_ = TupleSize.fixed
+ title = 0
+ author = 1
+ genre = 2
+ b = Book('Teckla', 'Steven Brust', 'fantasy')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertTrue('fantasy' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertRaises(TypeError, Book, 'Teckla', 'Steven Brust')
+ self.assertRaises(TypeError, Book, 'Teckla')
+
+ def test_minimum_size(self):
+ class Book(NamedTuple):
+ _size_ = TupleSize.minimum
+ title = 0
+ author = 1
+ b = Book('Teckla', 'Steven Brust', 'fantasy')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertTrue('fantasy' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ b = Book('Teckla', 'Steven Brust')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertRaises(TypeError, Book, 'Teckla')
+
+ def test_variable_size(self):
+ class Book(NamedTuple):
+ _size_ = TupleSize.variable
+ title = 0
+ author = 1
+ genre = 2
+ b = Book('Teckla', 'Steven Brust', 'fantasy')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertTrue('fantasy' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertEqual(b.genre, 'fantasy')
+ b = Book('Teckla', 'Steven Brust')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertRaises(AttributeError, getattr, b, 'genre')
+ self.assertRaises(TypeError, Book, title='Teckla', genre='fantasy')
+ self.assertRaises(TypeError, Book, author='Steven Brust')
+
+ def test_combining_namedtuples(self):
+ class Point(NamedTuple):
+ x = 0, 'horizontal coordinate', 1
+ y = 1, 'vertical coordinate', -1
+ class Color(NamedTuple):
+ r = 0, 'red component', 11
+ g = 1, 'green component', 29
+ b = 2, 'blue component', 37
+ Pixel1 = NamedTuple('Pixel', Point+Color, module=__name__)
+ class Pixel2(Point, Color):
+ "a colored dot"
+ class Pixel3(Point):
+ r = 2, 'red component', 11
+ g = 3, 'green component', 29
+ b = 4, 'blue component', 37
+ self.assertEqual(Pixel1._fields_, 'x y r g b'.split())
+ self.assertEqual(Pixel1.x.__doc__, 'horizontal coordinate')
+ self.assertEqual(Pixel1.x.default, 1)
+ self.assertEqual(Pixel1.y.__doc__, 'vertical coordinate')
+ self.assertEqual(Pixel1.y.default, -1)
+ self.assertEqual(Pixel1.r.__doc__, 'red component')
+ self.assertEqual(Pixel1.r.default, 11)
+ self.assertEqual(Pixel1.g.__doc__, 'green component')
+ self.assertEqual(Pixel1.g.default, 29)
+ self.assertEqual(Pixel1.b.__doc__, 'blue component')
+ self.assertEqual(Pixel1.b.default, 37)
+ self.assertEqual(Pixel2._fields_, 'x y r g b'.split())
+ self.assertEqual(Pixel2.x.__doc__, 'horizontal coordinate')
+ self.assertEqual(Pixel2.x.default, 1)
+ self.assertEqual(Pixel2.y.__doc__, 'vertical coordinate')
+ self.assertEqual(Pixel2.y.default, -1)
+ self.assertEqual(Pixel2.r.__doc__, 'red component')
+ self.assertEqual(Pixel2.r.default, 11)
+ self.assertEqual(Pixel2.g.__doc__, 'green component')
+ self.assertEqual(Pixel2.g.default, 29)
+ self.assertEqual(Pixel2.b.__doc__, 'blue component')
+ self.assertEqual(Pixel2.b.default, 37)
+ self.assertEqual(Pixel3._fields_, 'x y r g b'.split())
+ self.assertEqual(Pixel3.x.__doc__, 'horizontal coordinate')
+ self.assertEqual(Pixel3.x.default, 1)
+ self.assertEqual(Pixel3.y.__doc__, 'vertical coordinate')
+ self.assertEqual(Pixel3.y.default, -1)
+ self.assertEqual(Pixel3.r.__doc__, 'red component')
+ self.assertEqual(Pixel3.r.default, 11)
+ self.assertEqual(Pixel3.g.__doc__, 'green component')
+ self.assertEqual(Pixel3.g.default, 29)
+ self.assertEqual(Pixel3.b.__doc__, 'blue component')
+ self.assertEqual(Pixel3.b.default, 37)
+
+ def test_function_api_type(self):
+ class Tester(NamedTuple):
+ def howdy(self):
+ return 'backwards', list(reversed(self))
+ Testee = NamedTuple('Testee', 'a c e', type=Tester)
+ t = Testee(1, 2, 3)
+ self.assertEqual(t.howdy(), ('backwards', [3, 2, 1]))
+
+ def test_asdict(self):
+ class Point(NamedTuple):
+ x = 0, 'horizontal coordinate', 1
+ y = 1, 'vertical coordinate', -1
+ class Color(NamedTuple):
+ r = 0, 'red component', 11
+ g = 1, 'green component', 29
+ b = 2, 'blue component', 37
+ Pixel = NamedTuple('Pixel', Point+Color, module=__name__)
+ pixel = Pixel(99, -101, 255, 128, 0)
+ self.assertEqual(pixel._asdict(), {'x':99, 'y':-101, 'r':255, 'g':128, 'b':0})
+
+ def test_make(self):
+ class Point(NamedTuple):
+ x = 0, 'horizontal coordinate', 1
+ y = 1, 'vertical coordinate', -1
+ self.assertEqual(Point(4, 5), (4, 5))
+ self.assertEqual(Point._make((4, 5)), (4, 5))
+
+ def test_replace(self):
+ class Color(NamedTuple):
+ r = 0, 'red component', 11
+ g = 1, 'green component', 29
+ b = 2, 'blue component', 37
+ purple = Color(127, 0, 127)
+ mid_gray = purple._replace(g=127)
+ self.assertEqual(mid_gray, (127, 127, 127))
+
+
+class TestNamedConstant(TestCase):
+
+ def test_constantness(self):
+ class K(NamedConstant):
+ PI = 3.141596
+ TAU = 2 * PI
+ self.assertEqual(K.PI, 3.141596)
+ self.assertEqual(K.TAU, 2 * K.PI)
+ with self.assertRaisesRegex(AttributeError, r'cannot rebind constant'):
+ K.PI = 9
+ with self.assertRaisesRegex(AttributeError, r'cannot delete constant'):
+ del K.PI
+ with self.assertRaisesRegex(AttributeError, r'cannot rebind constant'):
+ K('PI', 3)
+ self.assertTrue(K.PI in K)
+ self.assertTrue(K.TAU in K)
+
+ def test_duplicates(self):
+ class CardNumber(NamedConstant):
+ ACE = 11
+ TWO = 2
+ THREE = 3
+ FOUR = 4
+ FIVE = 5
+ SIX = 6
+ SEVEN = 7
+ EIGHT = 8
+ NINE = 9
+ TEN = 10
+ JACK = 10
+ QUEEN = 10
+ KING = 10
+ self.assertFalse(CardNumber.TEN is CardNumber.JACK)
+ self.assertEqual(CardNumber.TEN, CardNumber.JACK)
+ self.assertEqual(CardNumber.TEN, 10)
+
+ def test_extend_constants(self):
+ class CardSuit(NamedConstant):
+ HEARTS = 1
+ SPADES = 2
+ DIAMONTS = 3
+ CLUBS = 4
+ self.assertEqual(CardSuit.HEARTS, 1)
+ stars = CardSuit('STARS', 5)
+ self.assertIs(stars, CardSuit.STARS)
+ self.assertEqual(CardSuit.STARS, 5)
+ self.assertTrue(CardSuit.STARS in CardSuit)
+
+ def test_constant_with_docstring(self):
+ class Stuff(NamedConstant):
+ Artifact = constant(7, "lucky number!")
+ Bowling = 11
+ HillWomp = constant(29, 'blah blah')
+ self.assertEqual(Stuff.Artifact, 7)
+ self.assertEqual(Stuff.Artifact.__doc__, 'lucky number!')
+ self.assertEqual(Stuff.Bowling, 11)
+ self.assertEqual(Stuff.Bowling.__doc__, None)
+ self.assertEqual(Stuff.HillWomp, 29)
+ self.assertEqual(Stuff.HillWomp.__doc__, 'blah blah')
+
+ def test_deep_copy(self):
+ import copy
+ class APITypes(aenum.Constant):
+ STRING = "string"
+ INT = "int"
+ APITypes('string')
+ d = {"first": APITypes.STRING}
+ copy.deepcopy(d)
+ self.assertTrue(d['first'] is APITypes.STRING)
+
+ def test_subclass_w_same_value(self):
+ class Foo(aenum.Constant):
+ BLA = 'bla1'
+ ABA = 'aba1'
+ class Bar(aenum.Constant):
+ BLA = Foo.BLA
+ ABA = 'aba2'
+ self.assertEqual(Foo.BLA, Bar.BLA)
+ self.assertFalse(Foo.BLA is Bar.BLA)
+
+
+class TestStarImport(TestCase):
+
+ def test_all_exports_names(self):
+ scope = {}
+ exec('from aenum import *', scope, scope)
+ self.assertIn('Enum', scope)
+
+class TestStackoverflowAnswers(TestCase):
+
+ def test_self_referential_directions(self):
+ # https://stackoverflow.com/a/64000706/208880
+ class Directions(Enum):
+ _order_ = 'NORTH WEST SOUTH EAST'
+ #
+ NORTH = 1, 0
+ WEST = 0, 1
+ SOUTH = -1, 0
+ EAST = 0, -1
+ #
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+ if len(self.__class__):
+ # make links
+ all = list(self.__class__)
+ left, right = all[0], all[-1]
+ self.left = left
+ self.right = right
+ left.right = self
+ right.left = self
+ #
+ D = Directions
+ self.assertEqual(D.NORTH.value, (1, 0))
+ self.assertTrue(D.NORTH.left is D.WEST)
+ self.assertTrue(D.SOUTH.right is D.WEST)
+
+ def test_self_referential_rock_paper_scissors(self):
+ # https://stackoverflow.com/a/57085357/208880
+ class RPS(Enum):
+ _order_ = 'Rock, Paper, Scissors'
+ #
+ Rock = "rock"
+ Paper = "paper"
+ Scissors = "scissors"
+ #
+ def __init__(self, value):
+ if len(self.__class__):
+ # make links
+ all = list(self.__class__)
+ first, previous = all[0], all[-1]
+ first.beats = self
+ self.beats = previous
+ #
+ self.assertTrue(RPS.Rock.beats is RPS.Scissors)
+ self.assertTrue(RPS.Scissors.beats is RPS.Paper)
+ self.assertTrue(RPS.Paper.beats is RPS.Rock)
+
+ def test_arduino_headers(self):
+ # https://stackoverflow.com/q/65048495/208880
+ class CHeader(Enum):
+ def __init_subclass__(cls, **kwds):
+ # write Enums to C header file
+ cls_name = cls.__name__
+ header_path = getattr(cls, '_%s__header' % cls_name)
+ with open(header_path, 'w') as fh:
+ fh.write('initial header stuff here\n')
+ for enum in cls:
+ fh.write('#define %s %r\n' % (enum.name, enum.value))
+ class Arduino(CHeader):
+ _order_ = 'ONE TWO'
+ __header = os.path.join(tempdir, 'arduino.h')
+ ONE = 1
+ TWO = 2
+ with open(os.path.join(tempdir, 'arduino.h')) as fh:
+ data = fh.read()
+ self.assertEqual(textwrap.dedent("""\
+ initial header stuff here
+ #define ONE 1
+ #define TWO 2
+ """),
+ data,
+ )
+
+ def test_lowercase_compare(self):
+ # https://stackoverflow.com/q/65139026/208880
+ class CompareLowerCase(Enum):
+ def __init_subclass__(cls, **kwds):
+ super(CompareLowerCase, cls).__init_subclass__(**kwds)
+ cls.lowered_names = set([m.name.lower() for m in cls])
+ @classmethod
+ def has_name(cls, name):
+ return name.lower() in cls.lowered_names
+ #
+ class LabelEnum(CompareLowerCase, StrEnum):
+ ENUM_ONE = "Enum One"
+ ENUM_TWO = "Enum Two"
+ ENUM_THREE = "Enum Three"
+ FOUR = "FOUR"
+ FIVE = "FIVE"
+ SIX = "SIX"
+ #
+ self.assertTrue(LabelEnum.has_name('Enum_Three'))
+
+
+class TestExtendEnum(TestCase):
+
+ def test_extend_enum_plain(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(TypeError, 'already in use as', extend_enum, Color, 'blue', 5)
+ #
+ extend_enum(Color, 'brown', 4)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 4)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(4), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 5)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(5), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_enum_alias(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'rojo', 1)
+ self.assertEqual(Color.rojo.name, 'red')
+ self.assertEqual(Color.rojo.value, 1)
+ self.assertTrue(Color.rojo in Color)
+ self.assertEqual(Color(1), Color.rojo)
+ self.assertEqual(Color['rojo'], Color.red)
+ self.assertEqual(len(Color), 3)
+
+ def test_extend_enum_unique(self):
+ class Color(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(ValueError, r'<Color.rojo: 1> is a duplicate of <Color.red: 1>', extend_enum, Color, 'rojo', 1)
+ #
+ self.assertEqual(Color.red.name, 'red')
+ self.assertEqual(Color.red.value, 1)
+ self.assertTrue(Color.red in Color)
+ self.assertEqual(Color(1), Color.red)
+ self.assertEqual(Color['red'], Color.red)
+ self.assertEqual(Color.green.name, 'green')
+ self.assertEqual(Color.green.value, 2)
+ self.assertTrue(Color.green in Color)
+ self.assertEqual(Color(2), Color.green)
+ self.assertEqual(Color['blue'], Color.blue)
+ self.assertEqual(Color.blue.name, 'blue')
+ self.assertEqual(Color.blue.value, 3)
+ self.assertTrue(Color.blue in Color)
+ self.assertEqual(Color(3), Color.blue)
+ self.assertEqual(len(Color), 3)
+ #
+ extend_enum(Color, 'brown', 4)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 4)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(4), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+ #
+ self.assertRaisesRegex(ValueError, '', extend_enum, Color, 'verde', 2)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 5)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(5), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+
+ def test_extend_enum_shadow_property(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'value', 4)
+ self.assertEqual(Color.value.name, 'value')
+ self.assertEqual(Color.value.value, 4)
+ self.assertTrue(Color.value in Color)
+ self.assertEqual(Color(4), Color.value)
+ self.assertEqual(Color['value'], Color.value)
+ self.assertEqual(len(Color), 4)
+ self.assertEqual(Color.red.value, 1)
+
+ def test_extend_enum_shadow_base(self):
+ class hohum(object):
+ def cyan(self):
+ "cyanize a color"
+ return self.value
+ class Color(hohum, Enum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4)
+ self.assertEqual(len(Color), 3)
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+
+ def test_extend_enum_multivalue(self):
+ class Color(MultiValueEnum):
+ red = 1, 4, 7
+ green = 2, 5, 8
+ blue = 3, 6, 9
+ extend_enum(Color, 'brown', 10, 20)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 10)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(10), Color.brown)
+ self.assertEqual(Color(20), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+ #
+ self.assertRaisesRegex(ValueError, 'no values specified for MultiValue enum', extend_enum, Color, 'mauve')
+
+ def test_extend_enum_multivalue_alias(self):
+ class Color(MultiValueEnum):
+ red = 1, 4, 7
+ green = 2, 5, 8
+ blue = 3, 6, 9
+ self.assertRaisesRegex(ValueError, r'<Color.rojo: 7> is a duplicate of <Color.red: 1>', extend_enum, Color, 'rojo', 7)
+ self.assertEqual(Color.red.name, 'red')
+ self.assertEqual(Color.red.value, 1)
+ self.assertTrue(Color.red in Color)
+ self.assertEqual(Color(1), Color.red)
+ self.assertEqual(Color(4), Color.red)
+ self.assertEqual(Color(7), Color.red)
+ self.assertEqual(Color['red'], Color.red)
+ self.assertEqual(Color.green.name, 'green')
+ self.assertEqual(Color.green.value, 2)
+ self.assertTrue(Color.green in Color)
+ self.assertEqual(Color(2), Color.green)
+ self.assertEqual(Color(5), Color.green)
+ self.assertEqual(Color(8), Color.green)
+ self.assertEqual(Color['blue'], Color.blue)
+ self.assertEqual(Color.blue.name, 'blue')
+ self.assertEqual(Color.blue.value, 3)
+ self.assertTrue(Color.blue in Color)
+ self.assertEqual(Color(3), Color.blue)
+ self.assertEqual(Color(6), Color.blue)
+ self.assertEqual(Color(9), Color.blue)
+ self.assertEqual(len(Color), 3)
+
+ def test_extend_enum_multivalue_str(self):
+ class M(str, MultiValueEnum):
+ VALUE_1 = 'value_1', 'VALUE_1'
+ VALUE_2 = 'value_2', 'VALUE_2'
+ VALUE_3 = 'value_3', 'VALUE_3'
+ self.assertTrue(M._member_type_ is str)
+ extend_enum(M, 'VALUE_4', 'value_4', 'VALUE_4')
+ self.assertEqual(list(M), [M.VALUE_1, M.VALUE_2, M.VALUE_3, M.VALUE_4])
+ self.assertTrue(M('value_4') is M.VALUE_4)
+ self.assertTrue(M('VALUE_4') is M.VALUE_4)
+ self.assertTrue(M.VALUE_4.name == 'VALUE_4')
+ self.assertTrue(M.VALUE_4.value == 'value_4')
+
+ def test_extend_intenum(self):
+ class Index(IntEnum):
+ 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_extend_multi_init(self):
+ try:
+ from http import HTTPStatus
+ length = len(HTTPStatus)
+ except ImportError:
+ class HTTPStatus(IntEnum):
+ def __new__(cls, value, phrase, description):
+ obj = int.__new__(cls, value)
+ obj._value_ = value
+
+ obj.phrase = phrase
+ obj.description = description
+ return obj
+ CONTINUE = 100, 'Continue', 'Request received, please continue'
+ SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header'
+ PROCESSING = 102, 'Processing', ''
+ length = 3
+ extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train')
+ extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '')
+ self.assertEqual(len(HTTPStatus), length+2)
+ self.assertEqual(
+ list(HTTPStatus)[-2:],
+ [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS],
+ )
+ self.assertEqual(HTTPStatus.BAD_SPAM.value, 513)
+ self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM')
+ self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy')
+ self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train')
+ self.assertEqual(HTTPStatus.BAD_EGGS.value, 514)
+ self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS')
+ self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green')
+ self.assertEqual(HTTPStatus.BAD_EGGS.description, '')
+
+ def test_extend_flag(self):
+ class Color(Flag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+
+ def test_extend_flag_backwards(self):
+ class Color(Flag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+ #
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 16)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(16), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_intflag(self):
+ class Color(IntFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 16)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(16), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_intflag_backwards(self):
+ class Color(IntFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+ #
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 16)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(16), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_strenum(self):
+ class Color(StrEnum):
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ extend_enum(Color, 'BLACK')
+ self.assertEqual(Color.BLACK.name, 'BLACK')
+ self.assertEqual(Color.BLACK.value, 'black')
+ self.assertEqual(len(Color), 4)
+
+
+class TestIssues(TestCase):
+
+ def test_auto_multi_int(self):
+ class Measurement(int, MultiValueEnum, AddValueEnum):
+ _order_ = 'one two three'
+ _start_ = 0
+ one = "20110721"
+ two = "20120911"
+ three = "20110518"
+ self.assertEqual([m.value for m in Measurement], [0, 1, 2])
+ self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three'])
+ self.assertIs(Measurement('20110721'), Measurement.one)
+ self.assertIs(Measurement(0), Measurement.one)
+ self.assertIs(Measurement('20120911'), Measurement.two)
+ self.assertIs(Measurement(1), Measurement.two)
+ self.assertIs(Measurement('20110518'), Measurement.three)
+ self.assertIs(Measurement(2), Measurement.three)
+
+ def test_auto_kwds(self):
+ class Item(Enum):
+ _order_ = 'A B'
+ A = auto(size=100, requirements={})
+ B = auto(size=200, requirements={A: 1})
+ #
+ def __new__(cls, value, size, requirements):
+ obj = object.__new__(cls)
+ obj._value_ = value
+ obj.size = size
+ # fix requirements
+ new_requirements = {}
+ for k, v in requirements.items():
+ if isinstance(k, auto):
+ k = k.enum_member
+ new_requirements[k] = v
+ obj.requirements = new_requirements
+ return obj
+ self.assertEqual((Item.A.value, Item.A.size, Item.A.requirements), (1, 100, {}))
+ self.assertEqual((Item.B.value, Item.B.size, Item.B.requirements), (2, 200, {Item.A: 1}))
+
+ def test_extend_flag(self):
+ class FlagTest(Flag): # Or IntFlag
+ NONE = 0
+ LOW = 1
+ MID = 2
+ extend_enum(FlagTest, 'HIGH', 4)
+ self.assertEqual(FlagTest.LOW | FlagTest.HIGH, FlagTest(5))
+ self.assertEqual((FlagTest.LOW | FlagTest.HIGH).value, 5)
+
+ def test_extend_unhashable(self):
+ class TestEnum(Enum):
+ ABC = {
+ 'id': 0,
+ 'value': 'abc'
+ }
+ DEF = {
+ 'id': 1,
+ 'value': 'def'
+ }
+ rand = uuid.uuid4().hex
+ new_value = {
+ 'id': 99,
+ 'value': 'new',
+ }
+ extend_enum(TestEnum, rand, new_value)
+
+
+
+# Test conversion of global constants
+# These are unordered here on purpose to ensure that declaration order
+# makes no difference.
+CONVERT_TEST_NAME_D = 5
+CONVERT_TEST_NAME_C = 5
+CONVERT_TEST_NAME_B = 5
+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
+
+CONVERT_STRING_TEST_NAME_D = 5
+CONVERT_STRING_TEST_NAME_C = 5
+CONVERT_STRING_TEST_NAME_B = 5
+CONVERT_STRING_TEST_NAME_A = 5 # This one should sort first.
+CONVERT_STRING_TEST_NAME_E = 5
+CONVERT_STRING_TEST_NAME_F = 5
+
+# global names for StrEnum._convert_ test
+CONVERT_STR_TEST_2 = 'goodbye'
+CONVERT_STR_TEST_1 = 'hello'
+
+# We also need values that cannot be compared:
+UNCOMPARABLE_A = 5
+UNCOMPARABLE_C = (9, 1) # naming order is broken on purpose
+UNCOMPARABLE_B = 'value'
+
+COMPLEX_C = 1j
+COMPLEX_A = 2j
+COMPLEX_B = 3j
+
+
+class TestConvert(TestCase):
+
+ def tearDown(self):
+ # Reset the module-level test variables to their original integer
+ # values, otherwise the already created enum values get converted
+ # instead.
+ g = globals()
+ for suffix in ['A', 'B', 'C', 'D', 'E', 'F']:
+ g['CONVERT_TEST_NAME_%s' % suffix] = 5
+ g['CONVERT_STRING_TEST_NAME_%s' % suffix] = 5
+ for suffix, value in (('A', 5), ('B', (9, 1)), ('C', 'value')):
+ g['UNCOMPARABLE_%s' % suffix] = value
+ for suffix, value in (('A', 2j), ('B', 3j), ('C', 1j)):
+ g['COMPLEX_%s' % suffix] = value
+ for suffix, value in (('1', 'hello'), ('2', 'goodbye')):
+ g['CONVERT_STR_TEST_%s' % suffix] = value
+ g['CONVERT_TEST_SIGABRT'] = 4
+ g['CONVERT_TEST_SIGIOT'] = 4
+ g['CONVERT_TEST_EIO'] = 7
+ g['CONVERT_TEST_EBUS'] = 7
+
+ def test_convert_value_lookup_priority(self):
+ test_type = IntEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_TEST_'))
+ # We don't want the reverse lookup value to vary when there are
+ # 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_int(self):
+ test_type = IntEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_TEST_'))
+ # Ensure that test_type has all of the desired names and values.
+ self.assertEqual(test_type.CONVERT_TEST_NAME_F,
+ test_type.CONVERT_TEST_NAME_A)
+ self.assertEqual(test_type.CONVERT_TEST_NAME_B, 5)
+ self.assertEqual(test_type.CONVERT_TEST_NAME_C, 5)
+ self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5)
+ self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5)
+ # Ensure that test_type only picked up names matching the filter.
+ int_dir = dir(int) + [
+ 'CONVERT_TEST_NAME_A', 'CONVERT_TEST_NAME_B', 'CONVERT_TEST_NAME_C',
+ 'CONVERT_TEST_NAME_D', 'CONVERT_TEST_NAME_E', 'CONVERT_TEST_NAME_F',
+ 'CONVERT_TEST_SIGABRT', 'CONVERT_TEST_SIGIOT',
+ 'CONVERT_TEST_EIO', 'CONVERT_TEST_EBUS',
+ ]
+ extra = [name for name in dir(test_type) if name not in enum_dir(test_type)]
+ missing = [name for name in enum_dir(test_type) if name not in dir(test_type)]
+ self.assertEqual(
+ extra + missing,
+ [],
+ msg='extra names: %r; missing names: %r' % (extra, missing),
+ )
+
+ @unittest.skipUnless(PY3, 'everything is comparable on Python 2')
+ def test_convert_uncomparable(self):
+ uncomp = Enum._convert_(
+ 'Uncomparable',
+ MODULE,
+ filter=lambda x: x.startswith('UNCOMPARABLE_'))
+ # Should be ordered by `name` only:
+ self.assertEqual(
+ list(uncomp),
+ [uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C],
+ list(uncomp),
+ )
+
+ @unittest.skipUnless(PY3, 'everything is comparable on Python 2')
+ def test_convert_complex(self):
+ uncomp = Enum._convert_(
+ 'Uncomparable',
+ MODULE,
+ filter=lambda x: x.startswith('COMPLEX_'))
+ # Should be ordered by `name` only:
+ self.assertEqual(
+ list(uncomp),
+ [uncomp.COMPLEX_A, uncomp.COMPLEX_B, uncomp.COMPLEX_C],
+ )
+
+ def test_convert_str(self):
+ test_type = StrEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_STR_'),
+ as_global=True)
+ # Ensure that test_type has all of the desired names and values.
+ self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello')
+ self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye')
+ # Ensure that test_type only picked up names matching the filter.
+ extra = [name for name in dir(test_type) if name not in enum_dir(test_type)]
+ missing = [name for name in enum_dir(test_type) if name not in dir(test_type)]
+ self.assertEqual(
+ extra + missing,
+ [],
+ msg='extra names: %r; missing names: %r' % (extra, missing),
+ )
+ self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE)
+ self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
+ self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
+
+ def test_convert_repr_and_str(self):
+ test_type = IntEnum._convert_(
+ 'UnittestConvert',
+ MODULE,
+ filter=lambda x: x.startswith('CONVERT_STRING_TEST_'),
+ as_global=True)
+ self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE)
+ self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), '5')
+ self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5')
+
+# helpers
+
+def enum_dir(cls):
+ interesting = set(cls._member_names_ + [
+ '__class__', '__contains__', '__doc__', '__getitem__',
+ '__iter__', '__len__', '__members__', '__module__',
+ '__name__',
+ ])
+ if cls._new_member_ is not object.__new__:
+ interesting.add('__new__')
+ if cls.__init_subclass__ is not Enum.__init_subclass__:
+ interesting.add('__init_subclass__')
+ if hasattr(object, '__qualname__'):
+ interesting.add('__qualname__')
+ for method in ('__init__', '__format__', '__repr__', '__str__'):
+ if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)):
+ interesting.add(method)
+ if cls._member_type_ is object:
+ return sorted(interesting)
+ else:
+ # return whatever mixed-in data type has
+ return sorted(set(dir(cls._member_type_)) | interesting)
+
+def member_dir(member):
+ if member.__class__._member_type_ is object:
+ allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value'])
+ else:
+ allowed = set(dir(member))
+ for cls in member.__class__.mro():
+ for name, obj in cls.__dict__.items():
+ if name[0] == '_':
+ continue
+ if isinstance(obj, enum.property):
+ if obj.fget is not None or name not in member._member_map_:
+ allowed.add(name)
+ else:
+ allowed.discard(name)
+ else:
+ allowed.add(name)
+ return sorted(allowed)
+
+
+
+if __name__ == '__main__':
+ tempdir = tempfile.mkdtemp()
+ test = None
+ try:
+ if PY3:
+ test_v3.tempdir = tempdir
+ test = unittest.main(exit=False)
+ sys.stdout.flush()
+ for name, reason in test.result.skipped:
+ print("%s: %s" % (name, reason))
+ finally:
+ shutil.rmtree(tempdir, True)
+ if test:
+ sys.exit(len(test.result.errors or test.result.failures) and 1 or 0)
+
diff --git a/venv/Lib/site-packages/aenum/test_v3.py b/venv/Lib/site-packages/aenum/test_v3.py
new file mode 100644
index 00000000..62453df1
--- /dev/null
+++ b/venv/Lib/site-packages/aenum/test_v3.py
@@ -0,0 +1,1982 @@
+from . import EnumMeta, Enum, IntEnum, Flag, IntFlag, StrEnum, UniqueEnum, AutoEnum, AddValueEnum
+from . import NamedTuple, TupleSize, MagicValue, AddValue, NoAlias, Unique, MultiValue
+from . import AutoNumberEnum,MultiValueEnum, OrderedEnum, unique, skip, extend_enum, auto
+from . import StdlibEnumMeta, StdlibEnum, StdlibIntEnum, StdlibFlag, StdlibIntFlag, StdlibStrEnum
+from . import pyver, PY3_3, PY3_4, PY3_5, PY3_6, PY3_11
+from . import add_stdlib_integration, remove_stdlib_integration
+
+from collections import OrderedDict
+from datetime import timedelta
+from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
+from unittest import TestCase, main
+
+import os
+import sys
+import tempfile
+import textwrap
+import unittest
+
+try:
+ import pyparsing
+except (ImportError, SyntaxError):
+ pyparsing = None
+
+try:
+ RecursionError
+except NameError:
+ # python3.4
+ RecursionError = RuntimeError
+
+class TestEnumV3(TestCase):
+
+ def setUp(self):
+ class Season(Enum):
+ SPRING = 1
+ SUMMER = 2
+ AUTUMN = 3
+ WINTER = 4
+ self.Season = Season
+
+ class Konstants(float, Enum):
+ E = 2.7182818
+ PI = 3.1415926
+ TAU = 2 * PI
+ self.Konstants = Konstants
+
+ class Grades(IntEnum):
+ A = 5
+ B = 4
+ C = 3
+ D = 2
+ F = 0
+ self.Grades = Grades
+
+ class Directional(str, Enum):
+ EAST = 'east'
+ WEST = 'west'
+ NORTH = 'north'
+ SOUTH = 'south'
+ self.Directional = Directional
+
+ from datetime import date
+ class Holiday(date, Enum):
+ NEW_YEAR = 2013, 1, 1
+ IDES_OF_MARCH = 2013, 3, 15
+ self.Holiday = Holiday
+
+ @unittest.skipUnless(StdlibEnumMeta, 'Stdlib enum not available')
+ def test_stdlib_inheritence(self):
+ # 3.4
+ self.assertTrue(issubclass(self.Season, StdlibEnum))
+ self.assertTrue(isinstance(self.Season.SPRING, StdlibEnum))
+ #
+ if pyver >= PY3_6:
+ class AFlag(Flag):
+ one = 1
+ self.assertTrue(issubclass(AFlag, StdlibEnum))
+ self.assertTrue(isinstance(AFlag.one, StdlibEnum))
+ self.assertTrue(issubclass(AFlag, StdlibFlag))
+ self.assertTrue(isinstance(AFlag.one, StdlibFlag))
+ #
+ class AnIntFlag(IntFlag):
+ one = 1
+ self.assertTrue(issubclass(AnIntFlag, StdlibEnum))
+ self.assertTrue(isinstance(AnIntFlag.one, StdlibEnum))
+ self.assertTrue(issubclass(AnIntFlag, StdlibFlag))
+ self.assertTrue(isinstance(AnIntFlag.one, StdlibFlag))
+ self.assertTrue(issubclass(AnIntFlag, StdlibIntFlag))
+ self.assertTrue(isinstance(AnIntFlag.one, StdlibIntFlag))
+ #
+ if pyver >= PY3_11:
+ class AStrEnum(StrFlag):
+ one = '1'
+ self.assertTrue(issubclass(AStrEnum, StdlibEnum))
+ self.assertTrue(isinstance(AStrEnum.one, StdlibEnum))
+ self.assertTrue(issubclass(AStrEnum, StdlibStrEnum))
+ self.assertTrue(isinstance(AStrEnum.one, StdlibStrEnum))
+
+ @unittest.skipUnless(StdlibEnumMeta, 'Stdlib enum not available')
+ def test_stdlib_bad_getattribute(self):
+ class BadEnumType(StdlibEnumMeta):
+ def __getattribute__(cls, name):
+ obj = super().__getattribute__(name)
+ if isinstance(obj, cls):
+ obj.deprecate()
+ return obj
+ with self.assertRaisesRegex(RecursionError, 'endless recursion'):
+ class BaseEnum(StdlibEnum):
+ pass
+ class BadEnum(BaseEnum, metaclass=BadEnumType):
+ FOO = 'bar'
+ try:
+ remove_stdlib_integration()
+ class OkayEnum(StdlibEnum, metaclass=BadEnumType):
+ FOO = 'bar'
+ finally:
+ add_stdlib_integration()
+
+ @unittest.skipUnless(pyver >= PY3_5, '__qualname__ requires python 3.5 or greater')
+ def test_pickle_enum_function_with_qualname(self):
+ Theory = Enum('Theory', 'rule law supposition', qualname='spanish_inquisition')
+ globals()['spanish_inquisition'] = Theory
+ test_pickle_dump_load(self.assertTrue, Theory.rule)
+ test_pickle_dump_load(self.assertTrue, Theory)
+
+ def test_auto_init(self):
+ class Planet(Enum, init='mass radius'):
+ MERCURY = (3.303e+23, 2.4397e6)
+ VENUS = (4.869e+24, 6.0518e6)
+ EARTH = (5.976e+24, 6.37814e6)
+ MARS = (6.421e+23, 3.3972e6)
+ JUPITER = (1.9e+27, 7.1492e7)
+ SATURN = (5.688e+26, 6.0268e7)
+ URANUS = (8.686e+25, 2.5559e7)
+ NEPTUNE = (1.024e+26, 2.4746e7)
+ @property
+ def surface_gravity(self):
+ # universal gravitational constant (m3 kg-1 s-2)
+ G = 6.67300E-11
+ return G * self.mass / (self.radius * self.radius)
+ self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80)
+ self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6))
+
+ def test_auto_init_with_value(self):
+ class Color(Enum, init='value, rgb'):
+ RED = 1, (1, 0, 0)
+ BLUE = 2, (0, 1, 0)
+ GREEN = 3, (0, 0, 1)
+ self.assertEqual(Color.RED.value, 1)
+ self.assertEqual(Color.BLUE.value, 2)
+ self.assertEqual(Color.GREEN.value, 3)
+ self.assertEqual(Color.RED.rgb, (1, 0, 0))
+ self.assertEqual(Color.BLUE.rgb, (0, 1, 0))
+ self.assertEqual(Color.GREEN.rgb, (0, 0, 1))
+
+ def test_auto_turns_off(self):
+ with self.assertRaises(NameError):
+ class Color(Enum, settings=MagicValue):
+ red
+ green
+ blue
+ def hello(self):
+ print('Hello! My serial is %s.' % self.value)
+ rose
+ with self.assertRaises(NameError):
+ class Color(Enum, settings=MagicValue):
+ red
+ green
+ blue
+ def __init__(self, *args):
+ pass
+ rose
+
+ def test_magic(self):
+ class Color(Enum, settings=MagicValue):
+ red, green, blue
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+ self.assertEqual(Color.red.value, 1)
+
+ def test_ignore_not_overridden(self):
+ with self.assertRaisesRegex(TypeError, 'object is not callable'):
+ class Color(Flag):
+ _ignore_ = 'irrelevent'
+ _settings_ = MagicValue
+ @property
+ def shade(self):
+ print('I am light', self.name.lower())
+
+ def test_magic_start(self):
+ class Color(Enum, settings=MagicValue, start=0):
+ red, green, blue
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+ self.assertEqual(Color.red.value, 0)
+
+ def test_dir_on_class(self):
+ Season = self.Season
+ self.assertEqual(
+ set(dir(Season)),
+ set(['__class__', '__doc__', '__members__', '__module__',
+ 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER',
+ '__init_subclass__', '__name__', '__getitem__', '__len__',
+ '__contains__', '__iter__', '__qualname__',
+ ]))
+
+ def test_dir_on_item(self):
+ Season = self.Season
+ self.assertEqual(
+ set(dir(Season.WINTER)),
+ set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values']),
+ )
+
+ def test_dir_with_added_behavior(self):
+ class Test(Enum):
+ this = 'that'
+ these = 'those'
+ def wowser(self):
+ return ("Wowser! I'm %s!" % self.name)
+ self.assertEqual(
+ set(dir(Test)),
+ set([
+ '__class__', '__doc__', '__members__', '__module__', 'this', 'these',
+ '__init_subclass__', '__name__', '__getitem__', '__len__',
+ '__contains__', '__iter__', '__qualname__',
+ ]))
+ self.assertEqual(
+ set(dir(Test.this)),
+ set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values', 'wowser']),
+ )
+
+ def test_dir_on_sub_with_behavior_on_super(self):
+ # see issue22506
+ class SuperEnum(Enum):
+ def invisible(self):
+ return "did you see me?"
+ class SubEnum(SuperEnum):
+ sample = 5
+ self.assertEqual(
+ set(dir(SubEnum.sample)),
+ set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values', 'invisible']),
+ )
+
+ def test_members_are_always_ordered(self):
+ class AlwaysOrdered(Enum):
+ first = 1
+ second = 2
+ third = 3
+ self.assertTrue(type(AlwaysOrdered.__members__) is OrderedDict)
+
+ def test_comparisons(self):
+ def bad_compare():
+ Season.SPRING > 4
+ Season = self.Season
+ self.assertNotEqual(Season.SPRING, 1)
+ self.assertRaises(TypeError, bad_compare)
+
+ class Part(Enum):
+ SPRING = 1
+ CLIP = 2
+ BARREL = 3
+
+ self.assertNotEqual(Season.SPRING, Part.SPRING)
+ def bad_compare():
+ Season.SPRING < Part.CLIP
+ self.assertRaises(TypeError, bad_compare)
+
+ def test_duplicate_name(self):
+ with self.assertRaises(TypeError):
+ class Color1(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ red = 4
+
+ with self.assertRaises(TypeError):
+ class Color2(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ def red(self):
+ return 'red'
+
+ with self.assertRaises(TypeError):
+ class Color3(Enum):
+ @property
+ def red(self):
+ return 'redder'
+ red = 1
+ green = 2
+ blue = 3
+
+ def test_duplicate_value_with_unique(self):
+ with self.assertRaises(ValueError):
+ class Color(Enum, settings=Unique):
+ red = 1
+ green = 2
+ blue = 3
+ rojo = 1
+
+ def test_duplicate_value_with_noalias(self):
+ class Color(Enum, settings=NoAlias):
+ red = 1
+ green = 2
+ blue = 3
+ rojo = 1
+ self.assertFalse(Color.red is Color.rojo)
+ self.assertEqual(Color.red.value, 1)
+ self.assertEqual(Color.rojo.value, 1)
+ self.assertEqual(len(Color), 4)
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.rojo])
+
+ def test_noalias_value_lookup(self):
+ class Color(Enum, settings=NoAlias):
+ red = 1
+ green = 2
+ blue = 3
+ rojo = 1
+ self.assertRaises(TypeError, Color, 2)
+
+ def test_multivalue(self):
+ class Color(Enum, settings=MultiValue):
+ red = 1, 'red'
+ green = 2, 'green'
+ blue = 3, 'blue'
+ self.assertEqual(Color.red.value, 1)
+ self.assertIs(Color('green'), Color.green)
+ self.assertEqual(Color.blue.values, (3, 'blue'))
+
+ def test_multivalue_with_duplicate_values(self):
+ with self.assertRaises(ValueError):
+ class Color(Enum, settings=MultiValue):
+ red = 1, 'red'
+ green = 2, 'green'
+ blue = 3, 'blue', 'red'
+
+ def test_multivalue_with_duplicate_values_and_noalias(self):
+ with self.assertRaises(TypeError):
+ class Color(Enum, settings=(MultiValue, NoAlias)):
+ red = 1, 'red'
+ green = 2, 'green'
+ blue = 3, 'blue', 'red'
+
+ def test_multivalue_and_auto(self):
+ with self.assertRaisesRegex(TypeError, r'MultiValue and MagicValue are mutually exclusive'):
+ class Color(Enum, settings=(MultiValue, MagicValue)):
+ red
+ green = 3, 'green'
+ blue
+
+ def test_autonumber_and_init(self):
+ class Field(IntEnum, settings=AddValue, init='__doc__'):
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ self.assertEqual(Field.TYPE, 1)
+ self.assertEqual(Field.START, 2)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+ self.assertFalse(hasattr(Field, '_order_'))
+
+ def test_autovalue_and_init(self):
+ class Field(IntEnum, init='value __doc__'):
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ self.assertEqual(Field.TYPE, 1)
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+
+ def test_autonumber_and_start(self):
+ class Field(IntEnum, init='__doc__', settings=AddValue, start=0):
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ self.assertEqual(Field.TYPE, 0)
+ self.assertEqual(Field.START, 1)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+
+ def test_autonumber_and_init_and_some_values(self):
+ class Field(IntEnum, init='value __doc__'):
+ TYPE = "Char, Date, Logical, etc."
+ START = "Field offset in record"
+ BLAH = 5, "test blah"
+ BELCH = 'test belch'
+ self.assertEqual(Field.TYPE, 1)
+ self.assertEqual(Field.START, 2)
+ self.assertEqual(Field.BLAH, 5)
+ self.assertEqual(Field.BELCH, 6)
+ self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.')
+ self.assertEqual(Field.START.__doc__, 'Field offset in record')
+ self.assertEqual(Field.BLAH.__doc__, 'test blah')
+ self.assertEqual(Field.BELCH.__doc__, 'test belch')
+
+ def test_autonumber_with_irregular_values(self):
+ class Point(AutoNumberEnum, init='x y'):
+ first = 7, 9
+ second = 11, 13
+ self.assertEqual(Point.first.value, 1)
+ self.assertEqual(Point.first.x, 7)
+ self.assertEqual(Point.first.y, 9)
+ self.assertEqual(Point.second.value, 2)
+ self.assertEqual(Point.second.x, 11)
+ self.assertEqual(Point.second.y, 13)
+ with self.assertRaisesRegex(TypeError, '.*number of fields provided do not match init ...x., .y.. != .3, 11, 13..'):
+ class Point(AutoNumberEnum, init='x y'):
+ first = 7, 9
+ second = 3, 11, 13
+ class Color(AutoNumberEnum, init='__doc__'):
+ # interactions between AutoNumberEnum and _generate_next_value_ may not be pretty
+ red = ()
+ green = 'red'
+ blue = ()
+ self.assertTrue(Color.red.__doc__, 1)
+ self.assertEqual(Color.green.__doc__, 'red')
+ self.assertTrue(Color.blue.__doc__, 2)
+
+ def test_autonumber_and_property(self):
+ with self.assertRaises(TypeError):
+ class Color(AutoEnum):
+ _ignore_ = ()
+ red = ()
+ green = ()
+ blue = ()
+ @property
+ def cap_name(self) -> str:
+ return self.name.title()
+
+ def test_autoenum(self):
+ class Color(AutoEnum):
+ red
+ green
+ blue
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+ self.assertEqual([m.value for m in Color], [1, 2, 3])
+ self.assertEqual([m.name for m in Color], ['red', 'green', 'blue'])
+
+ def test_autoenum_with_str(self):
+ class Color(AutoEnum):
+ def _generate_next_value_(name, start, count, last_values):
+ return name
+ red
+ green
+ blue
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+ self.assertEqual([m.value for m in Color], ['red', 'green', 'blue'])
+ self.assertEqual([m.name for m in Color], ['red', 'green', 'blue'])
+
+ def test_autoenum_and_default_ignore(self):
+ class Color(AutoEnum):
+ red
+ green
+ blue
+ @property
+ def cap_name(self):
+ return self.name.title()
+ self.assertEqual(Color.blue.cap_name, 'Blue')
+
+ def test_autonumber_and_overridden_ignore(self):
+ with self.assertRaises(TypeError):
+ class Color(AutoEnum):
+ _ignore_ = 'staticmethod'
+ red
+ green
+ blue
+ @property
+ def cap_name(self) -> str:
+ return self.name.title()
+
+ def test_autonumber_and_multiple_assignment(self):
+ class Color(AutoEnum):
+ _ignore_ = 'property'
+ red
+ green
+ blue = cyan
+ @property
+ def cap_name(self) -> str:
+ return self.name.title()
+ self.assertEqual(Color.blue.cap_name, 'Cyan')
+
+ def test_multivalue_and_autonumber_inherited(self):
+ class Measurement(int, Enum, settings=(MultiValue, AddValue), start=0):
+ one = "20110721"
+ two = "20120911"
+ three = "20110518"
+ M = Measurement
+ self.assertEqual(M.one, 0)
+ self.assertTrue(M.one is M(0) is M('20110721'))
+
+ def test_combine_new_settings_with_old_settings(self):
+ class Auto(Enum, settings=Unique):
+ pass
+ with self.assertRaises(ValueError):
+ class AutoUnique(Auto, settings=MagicValue):
+ BLAH
+ BLUH
+ ICK = 1
+
+ def test_timedelta(self):
+ class Period(timedelta, Enum):
+ '''
+ different lengths of time
+ '''
+ _init_ = 'value period'
+ _settings_ = NoAlias
+ _ignore_ = 'Period i'
+ Period = vars()
+ for i in range(31):
+ Period['day_%d' % i] = i, 'day'
+ for i in range(15):
+ Period['week_%d' % i] = i*7, 'week'
+ for i in range(12):
+ Period['month_%d' % i] = i*30, 'month'
+ OneDay = day_1
+ OneWeek = week_1
+ self.assertFalse(hasattr(Period, '_ignore_'))
+ self.assertFalse(hasattr(Period, 'Period'))
+ self.assertFalse(hasattr(Period, 'i'))
+ self.assertTrue(isinstance(Period.day_1, timedelta))
+
+ def test_extend_enum_plain(self):
+ class Color(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'brown', 4)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 4)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(len(Color), 4)
+
+ def test_extend_enum_shadow(self):
+ class Color(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'value', 4)
+ self.assertEqual(Color.value.name, 'value')
+ self.assertEqual(Color.value.value, 4)
+ self.assertTrue(Color.value in Color)
+ self.assertEqual(len(Color), 4)
+ self.assertEqual(Color.red.value, 1)
+
+ def test_extend_enum_generate(self):
+ class Foo(AutoEnum):
+ def _generate_next_value_(name, start, count, values, *args, **kwds):
+ return name
+ a
+ b
+ #
+ extend_enum(Foo, 'c')
+ self.assertEqual(Foo.a.value, 'a')
+ self.assertEqual(Foo.b.value, 'b')
+ self.assertEqual(Foo.c.value, 'c')
+
+ def test_extend_enum_unique_with_duplicate(self):
+ with self.assertRaises(ValueError):
+ class Color(Enum, settings=Unique):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'value', 1)
+
+ def test_extend_enum_multivalue_with_duplicate(self):
+ with self.assertRaises(ValueError):
+ class Color(Enum, settings=MultiValue):
+ red = 1, 'rojo'
+ green = 2, 'verde'
+ blue = 3, 'azul'
+ extend_enum(Color, 'value', 2)
+
+ def test_extend_enum_noalias_with_duplicate(self):
+ class Color(Enum, settings=NoAlias):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'value', 3, )
+ self.assertRaises(TypeError, Color, 3)
+ self.assertFalse(Color.value is Color.blue)
+ self.assertTrue(Color.value.value, 3)
+
+ def test_no_duplicates(self):
+ def bad_duplicates():
+ class Color(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ class Color(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ grene = 2
+ self.assertRaises(ValueError, bad_duplicates)
+
+ def test_no_duplicates_kinda(self):
+ class Silly(UniqueEnum):
+ one = 1
+ two = 'dos'
+ name = 3
+ class Sillier(IntEnum, UniqueEnum):
+ single = 1
+ name = 2
+ triple = 3
+ value = 4
+
+ def test_auto_number(self):
+ class Color(Enum, settings=MagicValue):
+ red
+ blue
+ green
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 1)
+ self.assertEqual(Color.blue.value, 2)
+ self.assertEqual(Color.green.value, 3)
+
+ def test_auto_name(self):
+ class Color(Enum, settings=MagicValue):
+ def _generate_next_value_(name, start, count, last):
+ return name
+ red
+ blue
+ green
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.green.value, 'green')
+
+ def test_auto_name_inherit(self):
+ class AutoNameEnum(Enum):
+ def _generate_next_value_(name, start, count, last):
+ return name
+ class Color(AutoNameEnum, settings=MagicValue):
+ red
+ blue
+ green
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 'blue')
+ self.assertEqual(Color.green.value, 'green')
+
+ def test_auto_garbage(self):
+ class Color(Enum):
+ _settings_ = MagicValue
+ red = 'red'
+ blue
+ self.assertEqual(Color.blue.value, 1)
+
+ def test_auto_garbage_corrected(self):
+ class Color(Enum, settings=MagicValue):
+ red = 'red'
+ blue = 2
+ green
+
+ self.assertEqual(list(Color), [Color.red, Color.blue, Color.green])
+ self.assertEqual(Color.red.value, 'red')
+ self.assertEqual(Color.blue.value, 2)
+ self.assertEqual(Color.green.value, 3)
+
+ def test_duplicate_auto(self):
+ class Dupes(Enum, settings=MagicValue):
+ first = primero
+ second
+ third
+ self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
+
+ def test_order_as_function(self):
+ # first with _init_
+ class TestSequence(Enum):
+ _init_ = 'value, sequence'
+ _order_ = lambda member: member.sequence
+ item_id = 'An$(1,6)', 0 # Item Code
+ company_id = 'An$(7,2)', 1 # Company Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ for i, member in enumerate(TestSequence):
+ self.assertEqual(i, member.sequence)
+ ts = TestSequence
+ self.assertEqual(ts.item_id.name, 'item_id')
+ self.assertEqual(ts.item_id.value, 'An$(1,6)')
+ self.assertEqual(ts.item_id.sequence, 0)
+ self.assertEqual(ts.company_id.name, 'company_id')
+ self.assertEqual(ts.company_id.value, 'An$(7,2)')
+ self.assertEqual(ts.company_id.sequence, 1)
+ self.assertEqual(ts.warehouse_no.name, 'warehouse_no')
+ self.assertEqual(ts.warehouse_no.value, 'An$(9,4)')
+ self.assertEqual(ts.warehouse_no.sequence, 2)
+ self.assertEqual(ts.company.name, 'company')
+ self.assertEqual(ts.company.value, 'Hn$(13,6)')
+ self.assertEqual(ts.company.sequence, 3)
+ self.assertEqual(ts.key_type.name, 'key_type')
+ self.assertEqual(ts.key_type.value, 'Cn$(19,3)')
+ self.assertEqual(ts.key_type.sequence, 4)
+ self.assertEqual(ts.available.name, 'available')
+ self.assertEqual(ts.available.value, 'Zn$(1,1)')
+ self.assertEqual(ts.available.sequence, 5)
+ self.assertEqual(ts.contract_item.name, 'contract_item')
+ self.assertEqual(ts.contract_item.value, 'Bn(2,1)')
+ self.assertEqual(ts.contract_item.sequence, 6)
+ self.assertEqual(ts.sales_category.name, 'sales_category')
+ self.assertEqual(ts.sales_category.value, 'Fn')
+ self.assertEqual(ts.sales_category.sequence, 7)
+ self.assertEqual(ts.gl_category.name, 'gl_category')
+ self.assertEqual(ts.gl_category.value, 'Rn$(5,1)')
+ self.assertEqual(ts.gl_category.sequence, 8)
+ self.assertEqual(ts.warehouse_category.name, 'warehouse_category')
+ self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)')
+ self.assertEqual(ts.warehouse_category.sequence, 9)
+ self.assertEqual(ts.inv_units.name, 'inv_units')
+ self.assertEqual(ts.inv_units.value, 'Qn$(7,2)')
+ self.assertEqual(ts.inv_units.sequence, 10)
+ # and then without
+ class TestSequence(Enum):
+ _order_ = lambda member: member.value[1]
+ item_id = 'An$(1,6)', 0 # Item Code
+ company_id = 'An$(7,2)', 1 # Company Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ for i, member in enumerate(TestSequence):
+ self.assertEqual(i, member.value[1])
+ ts = TestSequence
+ self.assertEqual(ts.item_id.name, 'item_id')
+ self.assertEqual(ts.item_id.value, ('An$(1,6)', 0))
+ self.assertEqual(ts.company_id.name, 'company_id')
+ self.assertEqual(ts.company_id.value, ('An$(7,2)', 1))
+ self.assertEqual(ts.warehouse_no.name, 'warehouse_no')
+ self.assertEqual(ts.warehouse_no.value, ('An$(9,4)', 2))
+ self.assertEqual(ts.company.name, 'company')
+ self.assertEqual(ts.company.value, ('Hn$(13,6)', 3))
+ self.assertEqual(ts.key_type.name, 'key_type')
+ self.assertEqual(ts.key_type.value, ('Cn$(19,3)', 4))
+ self.assertEqual(ts.available.name, 'available')
+ self.assertEqual(ts.available.value, ('Zn$(1,1)', 5))
+ self.assertEqual(ts.contract_item.name, 'contract_item')
+ self.assertEqual(ts.contract_item.value, ('Bn(2,1)', 6))
+ self.assertEqual(ts.sales_category.name, 'sales_category')
+ self.assertEqual(ts.sales_category.value, ('Fn', 7))
+ self.assertEqual(ts.gl_category.name, 'gl_category')
+ self.assertEqual(ts.gl_category.value, ('Rn$(5,1)', 8))
+ self.assertEqual(ts.warehouse_category.name, 'warehouse_category')
+ self.assertEqual(ts.warehouse_category.value, ('Sn$(6,1)', 9))
+ self.assertEqual(ts.inv_units.name, 'inv_units')
+ self.assertEqual(ts.inv_units.value, ('Qn$(7,2)', 10))
+ # then with _init_ but without value
+ with self.assertRaises(TypeError):
+ class TestSequence(Enum):
+ _init_ = 'sequence'
+ _order_ = lambda member: member.sequence
+ item_id = 'An$(1,6)', 0 # Item Code
+ company_id = 'An$(7,2)', 1 # Company Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ # finally, out of order so Python 3 barfs
+ with self.assertRaises(TypeError):
+ class TestSequence(Enum):
+ _init_ = 'sequence'
+ _order_ = lambda member: member.sequence
+ item_id = 'An$(1,6)', 0 # Item Code
+ warehouse_no = 'An$(9,4)', 2 # Warehouse Number
+ company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY
+ company_id = 'An$(7,2)', 1 # Company Code
+ inv_units = 'Qn$(7,2)', 10 # Inv Units
+ available = 'Zn$(1,1)', 5 # Available?
+ contract_item = 'Bn(2,1)', 6 # Contract Item?
+ sales_category = 'Fn', 7 # Sales Category
+ key_type = 'Cn$(19,3)', 4 # Key Type = '1**'
+ gl_category = 'Rn$(5,1)', 8 # G/L Category
+ warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category
+
+ if pyver >= PY3_3:
+ def test_missing(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ @classmethod
+ def _missing_(cls, item):
+ if item == 'three':
+ return cls.blue
+ elif item == 'bad return':
+ # trigger internal error
+ return 5
+ elif item == 'error out':
+ raise ZeroDivisionError
+ else:
+ # trigger not found
+ return None
+ self.assertIs(Color('three'), Color.blue)
+ self.assertRaises(ValueError, Color, 7)
+ try:
+ Color('bad return')
+ except TypeError as exc:
+ self.assertTrue(isinstance(exc.__cause__, ValueError))
+ else:
+ raise Exception('Exception not raised.')
+ try:
+ Color('error out')
+ except ZeroDivisionError as exc:
+ self.assertTrue(isinstance(exc.__cause__, ValueError))
+ else:
+ raise Exception('Exception not raised.')
+
+ def test_enum_of_types(self):
+ """Support using Enum to refer to types deliberately."""
+ class MyTypes(Enum):
+ i = int
+ f = float
+ s = str
+ self.assertEqual(MyTypes.i.value, int)
+ self.assertEqual(MyTypes.f.value, float)
+ self.assertEqual(MyTypes.s.value, str)
+ class Foo:
+ pass
+ class Bar:
+ pass
+ class MyTypes2(Enum):
+ a = Foo
+ b = Bar
+ self.assertEqual(MyTypes2.a.value, Foo)
+ self.assertEqual(MyTypes2.b.value, Bar)
+ class SpamEnumNotInner:
+ pass
+ class SpamEnum(Enum):
+ spam = SpamEnumNotInner
+ self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner)
+
+ def test_nested_classes_in_enum_do_not_create_members(self):
+ """Support locally-defined nested classes."""
+ # manually set __qualname__ to remove testing framework noise
+ class Outer(Enum):
+ __qualname__ = "Outer"
+ a = 1
+ b = 2
+ class Inner(Enum):
+ __qualname__ = "Outer.Inner"
+ foo = 10
+ bar = 11
+ self.assertTrue(isinstance(Outer.Inner, type))
+ self.assertEqual(Outer.a.value, 1)
+ self.assertEqual(Outer.Inner.foo.value, 10)
+ self.assertEqual(
+ list(Outer.Inner),
+ [Outer.Inner.foo, Outer.Inner.bar],
+ )
+ self.assertEqual(
+ list(Outer),
+ [Outer.a, Outer.b],
+ )
+
+ if pyver == PY3_4:
+ def test_class_nested_enum_and_pickle_protocol_four(self):
+ # would normally just have this directly in the class namespace
+ class NestedEnum(Enum):
+ twigs = 'common'
+ shiny = 'rare'
+
+ self.__class__.NestedEnum = NestedEnum
+ self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__
+ test_pickle_exception(
+ self.assertRaises, PicklingError, self.NestedEnum.twigs,
+ protocol=(0, 3))
+ test_pickle_dump_load(self.assertTrue, self.NestedEnum.twigs,
+ protocol=(4, HIGHEST_PROTOCOL))
+
+ elif pyver >= PY3_5:
+ def test_class_nested_enum_and_pickle_protocol_four(self):
+ # would normally just have this directly in the class namespace
+ class NestedEnum(Enum):
+ twigs = 'common'
+ shiny = 'rare'
+
+ self.__class__.NestedEnum = NestedEnum
+ self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__
+ test_pickle_dump_load(self.assertTrue, self.NestedEnum.twigs,
+ protocol=(0, HIGHEST_PROTOCOL))
+
+ if pyver >= PY3_4:
+ def test_enum_injection(self):
+ class Color(Enum):
+ _order_ = 'BLACK WHITE'
+ BLACK = Color('black', '#000')
+ WHITE = Color('white', '#fff')
+
+ def __init__(self, label, hex):
+ self.label = label
+ self.hex = hex
+
+ self.assertEqual([Color.BLACK, Color.WHITE], list(Color))
+ self.assertEqual(Color.WHITE.hex, '#fff')
+ self.assertEqual(Color.BLACK.label, 'black')
+
+ def test_subclasses_with_getnewargs_ex(self):
+ class NamedInt(int):
+ __qualname__ = 'NamedInt' # needed for pickle protocol 4
+ def __new__(cls, *args):
+ _args = args
+ if len(args) < 2:
+ raise TypeError("name and value must be specified")
+ name, args = args[0], args[1:]
+ self = int.__new__(cls, *args)
+ self._intname = name
+ self._args = _args
+ return self
+ def __getnewargs_ex__(self):
+ return self._args, {}
+ @property
+ def __name__(self):
+ return self._intname
+ def __repr__(self):
+ # repr() is updated to include the name and type info
+ return "{}({!r}, {})".format(type(self).__name__,
+ self.__name__,
+ int.__repr__(self))
+ def __str__(self):
+ # str() is unchanged, even if it relies on the repr() fallback
+ base = int
+ base_str = base.__str__
+ if base_str.__objclass__ is object:
+ return base.__repr__(self)
+ return base_str(self)
+ # for simplicity, we only define one operator that
+ # propagates expressions
+ def __add__(self, other):
+ temp = int(self) + int( other)
+ if isinstance(self, NamedInt) and isinstance(other, NamedInt):
+ return NamedInt(
+ '({0} + {1})'.format(self.__name__, other.__name__),
+ temp )
+ else:
+ return temp
+
+ class NEI(NamedInt, Enum):
+ __qualname__ = 'NEI' # needed for pickle protocol 4
+ x = ('the-x', 1)
+ y = ('the-y', 2)
+
+
+ self.assertIs(NEI.__new__, Enum.__new__)
+ self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)")
+ globals()['NamedInt'] = NamedInt
+ globals()['NEI'] = NEI
+ NI5 = NamedInt('test', 5)
+ self.assertEqual(NI5, 5)
+ test_pickle_dump_load(self.assertEqual, NI5, 5, protocol=(4, HIGHEST_PROTOCOL))
+ self.assertEqual(NEI.y.value, 2)
+ test_pickle_dump_load(self.assertTrue, NEI.y, protocol=(4, HIGHEST_PROTOCOL))
+
+
+class TestOrderV3(TestCase):
+ """
+ Test definition order versus _order_ order.
+ """
+
+ def test_same_members(self):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+
+ def test_same_members_with_aliases(self):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ verde = green
+
+ def test_same_members_wrong_order(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ blue = 3
+ green = 2
+
+ def test_order_has_extra_members(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 3
+
+ def test_order_has_extra_members_with_aliases(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 3
+ verde = green
+
+ def test_enum_has_extra_members(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ purple = 4
+
+ def test_enum_has_extra_members_with_aliases(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Enum):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 3
+ purple = 4
+ verde = green
+
+ def test_same_members_flag(self):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+
+ def test_same_members_with_aliases_flag(self):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+ verde = green
+
+ def test_same_members_wrong_order_falg(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ blue = 4
+ green = 2
+
+ def test_order_has_extra_members_flag(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 4
+
+ def test_order_has_extra_members_with_aliases_flag(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue purple'
+ red = 1
+ green = 2
+ blue = 4
+ verde = green
+
+ def test_enum_has_extra_members_flag(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+ purple = 8
+
+ def test_enum_has_extra_members_with_aliases_flag(self):
+ with self.assertRaisesRegex(TypeError, 'member order does not match _order_'):
+ class Color(Flag):
+ _order_ = 'red green blue'
+ red = 1
+ green = 2
+ blue = 4
+ purple = 8
+ verde = green
+
+
+class TestNamedTupleV3(TestCase):
+
+ def test_fixed_size(self):
+ class Book(NamedTuple, size=TupleSize.fixed):
+ title = 0
+ author = 1
+ genre = 2
+ b = Book('Teckla', 'Steven Brust', 'fantasy')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertTrue('fantasy' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertRaises(TypeError, Book, 'Teckla', 'Steven Brust')
+ self.assertRaises(TypeError, Book, 'Teckla')
+
+ def test_minimum_size(self):
+ class Book(NamedTuple, size=TupleSize.minimum):
+ title = 0
+ author = 1
+ b = Book('Teckla', 'Steven Brust', 'fantasy')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertTrue('fantasy' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertEqual(b[2], 'fantasy')
+ b = Book('Teckla', 'Steven Brust')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertRaises(TypeError, Book, 'Teckla')
+
+ def test_variable_size(self):
+ class Book(NamedTuple, size=TupleSize.variable):
+ title = 0
+ author = 1
+ genre = 2
+ b = Book('Teckla', 'Steven Brust', 'fantasy')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertTrue('fantasy' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertEqual(b.genre, 'fantasy')
+ b = Book('Teckla', 'Steven Brust')
+ self.assertTrue('Teckla' in b)
+ self.assertTrue('Steven Brust' in b)
+ self.assertEqual(b.title, 'Teckla')
+ self.assertEqual(b.author, 'Steven Brust')
+ self.assertRaises(AttributeError, getattr, b, 'genre')
+ self.assertRaises(TypeError, Book, title='Teckla', genre='fantasy')
+ self.assertRaises(TypeError, Book, author='Steven Brust')
+
+
+
+class TestStackoverflowAnswersV3(TestCase):
+
+ def test_self_referential_directions(self):
+ # https://stackoverflow.com/a/64000706/208880
+ class Directions(Enum):
+ #
+ NORTH = 1, 0
+ WEST = 0, 1
+ SOUTH = -1, 0
+ EAST = 0, -1
+ #
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+ if len(self.__class__):
+ # make links
+ all = list(self.__class__)
+ left, right = all[0], all[-1]
+ self.left = left
+ self.right = right
+ left.right = self
+ right.left = self
+ #
+ D = Directions
+ self.assertEqual(D.NORTH.value, (1, 0))
+ self.assertTrue(D.NORTH.left is D.WEST)
+ self.assertTrue(D.SOUTH.right is D.WEST)
+
+ def test_self_referential_rock_paper_scissors(self):
+ # https://stackoverflow.com/a/57085357/208880
+ class RPS(Enum):
+ #
+ Rock = "rock"
+ Paper = "paper"
+ Scissors = "scissors"
+ #
+ def __init__(self, value):
+ if len(self.__class__):
+ # make links
+ all = list(self.__class__)
+ first, previous = all[0], all[-1]
+ first.beats = self
+ self.beats = previous
+ #
+ self.assertTrue(RPS.Rock.beats is RPS.Scissors)
+ self.assertTrue(RPS.Scissors.beats is RPS.Paper)
+ self.assertTrue(RPS.Paper.beats is RPS.Rock)
+
+ def test_arduino_headers(self):
+ # https://stackoverflow.com/q/65048495/208880
+ class CHeader(Enum):
+ def __init_subclass__(cls, **kwds):
+ # write Enums to C header file
+ cls_name = cls.__name__
+ header_path = getattr(cls, '_%s__header' % cls_name)
+ with open(header_path, 'w') as fh:
+ fh.write('initial header stuff here\n')
+ for enum in cls:
+ fh.write('#define %s %r\n' % (enum.name, enum.value))
+ class Arduino(CHeader):
+ __header = os.path.join(tempdir, 'arduino.h')
+ ONE = 1
+ TWO = 2
+ with open(os.path.join(tempdir, 'arduino.h')) as fh:
+ data = fh.read()
+ self.assertEqual(textwrap.dedent("""\
+ initial header stuff here
+ #define ONE 1
+ #define TWO 2
+ """),
+ data,
+ )
+
+ def test_create_C_like_Enum(self):
+ # https://stackoverflow.com/a/35965438/208880
+ class Id(Enum, settings=MagicValue, start=0):
+ #
+ NONE # 0x0
+ HEARTBEAT # 0x1
+ FLUID_TRANSFER_REQUEST
+ FLUID_TRANSFER_STATUS_MSG
+ FLUID_TRANSFER_ERROR_MSG
+ # ...
+ #
+ # Camera App Messages
+ START_SENDING_PICTURES = 0x010000
+ STOP_SENDING_PICTURES
+ START_RECORDING_VIDEO_REQ
+ STOP_RECORDING_VIDEO_REQ
+ # ...
+ #
+ # Sensor Calibration
+ VOLUME_REQUEST = 0x020000
+ START_CAL
+ CLI_COMMAND_REQUEST
+ CLI_COMMAND_RESPONSE
+ #
+ # File Mananger
+ NEW_DELIVERY_REQ = 0x30000
+ GET_DELIVERY_FILE_REQ
+ GET_FILE_REQ
+ #
+ ACK_NACK
+ RESPONSE
+ #
+ LAST_ID
+ #
+ self.assertEqual(Id.NONE.value, 0)
+ self.assertEqual(Id.FLUID_TRANSFER_ERROR_MSG.value, 4)
+ self.assertEqual(Id.START_SENDING_PICTURES.value, 0x010000)
+ self.assertEqual(Id.STOP_RECORDING_VIDEO_REQ.value, 0x010003)
+ self.assertEqual(Id.START_CAL.value, 0x020001)
+ self.assertEqual(Id.LAST_ID.value, 0x30005)
+
+
+ @unittest.skipUnless(pyparsing, 'pyparsing not installed')
+ def test_c_header_scanner(self):
+ # https://stackoverflow.com/questions/58732872/208880
+ with open(os.path.join(tempdir, 'c_plus_plus.h'), 'w') as fh:
+ fh.write("""
+ stuff before
+ enum hello {
+ Zero,
+ One,
+ Two,
+ Three,
+ Five=5,
+ Six,
+ Ten=10
+ };
+ in the middle
+ enum blah
+ {
+ alpha,
+ beta,
+ gamma = 10 ,
+ zeta = 50
+ };
+ at the end
+ """)
+ from pyparsing import Group, Optional, Suppress, Word, ZeroOrMore
+ from pyparsing import alphas, alphanums, nums
+ #
+ CPPEnum = None
+ class CPPEnumType(EnumMeta):
+ #
+ @classmethod
+ def __prepare__(metacls, clsname, bases, **kwds):
+ # return a standard dictionary for the initial processing
+ return {}
+ #
+ def __init__(clsname, *args , **kwds):
+ super(CPPEnumType, clsname).__init__(*args)
+ #
+ def __new__(metacls, clsname, bases, clsdict, **kwds):
+ if CPPEnum is None:
+ # first time through, ignore the rest
+ enum_dict = super(CPPEnumType, metacls).__prepare__(clsname, bases, **kwds)
+ enum_dict.update(clsdict)
+ return super(CPPEnumType, metacls).__new__(metacls, clsname, bases, enum_dict, **kwds)
+ members = []
+ #
+ # remove _file and _name using `pop()` as they will cause problems in EnumMeta
+ try:
+ file = clsdict.pop('_file')
+ except KeyError:
+ raise TypeError('_file not specified')
+ cpp_enum_name = clsdict.pop('_name', clsname.lower())
+ with open(file) as fh:
+ file_contents = fh.read()
+ #
+ # syntax we don't want to see in the final parse tree
+ LBRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,")
+ _enum = Suppress("enum")
+ identifier = Word(alphas, alphanums + "_")
+ integer = Word(nums)
+ enumValue = Group(identifier("name") + Optional(EQ + integer("value")))
+ enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue))
+ enum = _enum + identifier("enum") + LBRACE + enumList("names") + RBRACE
+ #
+ # find the cpp_enum_name ignoring other syntax and other enums
+ for item, start, stop in enum.scanString(file_contents):
+ if item.enum != cpp_enum_name:
+ continue
+ id = 0
+ for entry in item.names:
+ if entry.value != "":
+ id = int(entry.value)
+ members.append((entry.name.upper(), id))
+ id += 1
+ #
+ # get the real EnumDict
+ enum_dict = super(CPPEnumType, metacls).__prepare__(clsname, bases, **kwds)
+ # transfer the original dict content, names starting with '_' first
+ items = list(clsdict.items())
+ items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p))
+ for name, value in items:
+ enum_dict[name] = value
+ # add the members
+ for name, value in members:
+ enum_dict[name] = value
+ return super(CPPEnumType, metacls).__new__(metacls, clsname, bases, enum_dict, **kwds)
+ #
+ class CPPEnum(IntEnum, metaclass=CPPEnumType):
+ pass
+ #
+ class Hello(CPPEnum):
+ _file = os.path.join(tempdir, 'c_plus_plus.h')
+ #
+ class Blah(CPPEnum):
+ _file = os.path.join(tempdir, 'c_plus_plus.h')
+ _name = 'blah'
+ #
+ self.assertEqual(
+ list(Hello),
+ [Hello.ZERO, Hello.ONE, Hello.TWO, Hello.THREE, Hello.FIVE, Hello.SIX, Hello.TEN],
+ )
+ self.assertEqual(Hello.ZERO.value, 0)
+ self.assertEqual(Hello.THREE.value, 3)
+ self.assertEqual(Hello.SIX.value, 6)
+ self.assertEqual(Hello.TEN.value, 10)
+ #
+ self.assertEqual(
+ list(Blah),
+ [Blah.ALPHA, Blah.BETA, Blah.GAMMA, Blah.ZETA],
+ )
+ self.assertEqual(Blah.ALPHA.value, 0)
+ self.assertEqual(Blah.BETA.value, 1)
+ self.assertEqual(Blah.GAMMA.value, 10)
+ self.assertEqual(Blah.ZETA.value, 50)
+
+class TestIssuesV3(TestCase):
+ """
+ Problems that were stated in issues.
+ """
+
+ def test_auto_multi_int_1(self):
+ class Measurement(int, AddValueEnum, MultiValueEnum, start=0):
+ one = "20110721"
+ two = "20120911"
+ three = "20110518"
+ self.assertEqual([m.value for m in Measurement], [0, 1, 2])
+ self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three'])
+ self.assertIs(Measurement(0), Measurement.one)
+ self.assertIs(Measurement('20110721'), Measurement.one)
+ self.assertIs(Measurement(1), Measurement.two)
+ self.assertIs(Measurement('20120911'), Measurement.two)
+ self.assertIs(Measurement(2), Measurement.three)
+ self.assertIs(Measurement('20110518'), Measurement.three)
+
+ def test_auto_multi_int_2(self):
+ class Measurement(int, Enum, settings=(MultiValue, AddValue), start=0):
+ one = "20110721"
+ two = "20120911"
+ three = "20110518"
+ self.assertEqual([m.value for m in Measurement], [0, 1, 2])
+ self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three'])
+ self.assertIs(Measurement(0), Measurement.one)
+ self.assertIs(Measurement('20110721'), Measurement.one)
+ self.assertIs(Measurement(1), Measurement.two)
+ self.assertIs(Measurement('20120911'), Measurement.two)
+ self.assertIs(Measurement(2), Measurement.three)
+ self.assertIs(Measurement('20110518'), Measurement.three)
+
+ def test_extend_enum_with_init(self):
+ class Color(Enum, settings=MultiValue, init='foo bar'):
+ red = '1', 'yes'
+ green = '2', 'no'
+ blue = '3', 'maybe'
+ self.assertEqual(Color.red.value, '1')
+ self.assertEqual(Color.red.foo, '1')
+ self.assertEqual(Color.red.bar, 'yes')
+ extend_enum(Color, 'opacity', '4', 'never')
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.opacity])
+ self.assertEqual(Color.opacity.value, '4')
+ self.assertEqual(Color.opacity.name, 'opacity')
+ self.assertTrue(Color('4') is Color.opacity)
+ self.assertTrue(Color('never') is Color.opacity)
+
+class TestExtendEnumV3(TestCase):
+
+ def test_extend_enum_plain(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(TypeError, '.blue. already in use as property..Color.blue: 3.', extend_enum, Color, 'blue', 5)
+ #
+ extend_enum(Color, 'brown', 4)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 4)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(4), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 5)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(5), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_enum_alias(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'rojo', 1)
+ self.assertEqual(Color.rojo.name, 'red')
+ self.assertEqual(Color.rojo.value, 1)
+ self.assertTrue(Color.rojo in Color)
+ self.assertEqual(Color(1), Color.rojo)
+ self.assertEqual(Color['rojo'], Color.red)
+ self.assertEqual(len(Color), 3)
+
+ def test_extend_enum_unique(self):
+ class Color(UniqueEnum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(ValueError, r'<Color.rojo: 1> is a duplicate of <Color.red: 1>', extend_enum, Color, 'rojo', 1)
+ #
+ self.assertEqual(Color.red.name, 'red')
+ self.assertEqual(Color.red.value, 1)
+ self.assertTrue(Color.red in Color)
+ self.assertEqual(Color(1), Color.red)
+ self.assertEqual(Color['red'], Color.red)
+ self.assertEqual(Color.green.name, 'green')
+ self.assertEqual(Color.green.value, 2)
+ self.assertTrue(Color.green in Color)
+ self.assertEqual(Color(2), Color.green)
+ self.assertEqual(Color['blue'], Color.blue)
+ self.assertEqual(Color.blue.name, 'blue')
+ self.assertEqual(Color.blue.value, 3)
+ self.assertTrue(Color.blue in Color)
+ self.assertEqual(Color(3), Color.blue)
+ self.assertEqual(len(Color), 3)
+ #
+ extend_enum(Color, 'brown', 4)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 4)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(4), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+ #
+ self.assertRaisesRegex(ValueError, '<Color.verde: 2> is a duplicate of <Color.green: 2>', extend_enum, Color, 'verde', 2)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 5)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(5), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+
+ def test_extend_enum_shadow_property(self):
+ class Color(Enum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'value', 4)
+ self.assertEqual(Color.value.name, 'value')
+ self.assertEqual(Color.value.value, 4)
+ self.assertTrue(Color.value in Color)
+ self.assertEqual(Color(4), Color.value)
+ self.assertEqual(Color['value'], Color.value)
+ self.assertEqual(len(Color), 4)
+ self.assertEqual(Color.red.value, 1)
+
+ def test_extend_enum_shadow_base(self):
+ class hohum(object):
+ def cyan(self):
+ "cyanize a color"
+ return self.value
+ class Color(hohum, Enum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4)
+ self.assertEqual(len(Color), 3)
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+
+ def test_extend_enum_multivalue(self):
+ class Color(MultiValueEnum):
+ red = 1, 4, 7
+ green = 2, 5, 8
+ blue = 3, 6, 9
+ extend_enum(Color, 'brown', 10, 20)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 10)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(10), Color.brown)
+ self.assertEqual(Color(20), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+ #
+ self.assertRaisesRegex(ValueError, 'no values specified for MultiValue enum', extend_enum, Color, 'mauve')
+
+ def test_extend_enum_multivalue_alias(self):
+ class Color(MultiValueEnum):
+ red = 1, 4, 7
+ green = 2, 5, 8
+ blue = 3, 6, 9
+ self.assertRaisesRegex(ValueError, r'<Color.rojo: 7> is a duplicate of <Color.red: 1>', extend_enum, Color, 'rojo', 7)
+ self.assertEqual(Color.red.name, 'red')
+ self.assertEqual(Color.red.value, 1)
+ self.assertTrue(Color.red in Color)
+ self.assertEqual(Color(1), Color.red)
+ self.assertEqual(Color(4), Color.red)
+ self.assertEqual(Color(7), Color.red)
+ self.assertEqual(Color['red'], Color.red)
+ self.assertEqual(Color.green.name, 'green')
+ self.assertEqual(Color.green.value, 2)
+ self.assertTrue(Color.green in Color)
+ self.assertEqual(Color(2), Color.green)
+ self.assertEqual(Color(5), Color.green)
+ self.assertEqual(Color(8), Color.green)
+ self.assertEqual(Color['blue'], Color.blue)
+ self.assertEqual(Color.blue.name, 'blue')
+ self.assertEqual(Color.blue.value, 3)
+ self.assertTrue(Color.blue in Color)
+ self.assertEqual(Color(3), Color.blue)
+ self.assertEqual(Color(6), Color.blue)
+ self.assertEqual(Color(9), Color.blue)
+ self.assertEqual(len(Color), 3)
+
+ def test_extend_enum_multivalue_str(self):
+ class M(str, MultiValueEnum):
+ VALUE_1 = 'value_1', 'VALUE_1'
+ VALUE_2 = 'value_2', 'VALUE_2'
+ VALUE_3 = 'value_3', 'VALUE_3'
+ self.assertTrue(M._member_type_ is str)
+ extend_enum(M, 'VALUE_4', 'value_4', 'VALUE_4')
+ self.assertEqual(list(M), [M.VALUE_1, M.VALUE_2, M.VALUE_3, M.VALUE_4])
+ self.assertTrue(M('value_4') is M.VALUE_4)
+ self.assertTrue(M('VALUE_4') is M.VALUE_4)
+ self.assertTrue(M.VALUE_4.name == 'VALUE_4')
+ self.assertTrue(M.VALUE_4.value == 'value_4')
+
+ def test_extend_intenum(self):
+ class Index(IntEnum):
+ 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_extend_multi_init(self):
+ class HTTPStatus(IntEnum):
+ def __new__(cls, value, phrase, description):
+ obj = int.__new__(cls, value)
+ obj._value_ = value
+
+ obj.phrase = phrase
+ obj.description = description
+ return obj
+ CONTINUE = 100, 'Continue', 'Request received, please continue'
+ SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header'
+ PROCESSING = 102, 'Processing', ''
+ length = 3
+ extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train')
+ extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '')
+ self.assertEqual(len(HTTPStatus), length+2)
+ self.assertEqual(
+ list(HTTPStatus)[-2:],
+ [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS],
+ )
+ self.assertEqual(HTTPStatus.BAD_SPAM.value, 513)
+ self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM')
+ self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy')
+ self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train')
+ self.assertEqual(HTTPStatus.BAD_EGGS.value, 514)
+ self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS')
+ self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green')
+ self.assertEqual(HTTPStatus.BAD_EGGS.description, '')
+
+ def test_extend_flag(self):
+ class Color(Flag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+
+ def test_extend_flag_backwards(self):
+ class Color(Flag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+ #
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 16)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(16), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_intflag(self):
+ class Color(IntFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 16)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(16), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_intflag_backwards(self):
+ class Color(IntFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, Flag))
+ #
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 16)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(16), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ def test_extend_strenum(self):
+ class Color(StrEnum):
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ extend_enum(Color, 'BLACK')
+ self.assertEqual(Color.BLACK.name, 'BLACK')
+ self.assertEqual(Color.BLACK.value, 'black')
+ self.assertEqual(len(Color), 4)
+
+ @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available')
+ def test_extend_enum_stdlib(self):
+ class Color(StdlibEnum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertEqual(getattr(Color.red, '_values_', None), None)
+ extend_enum(Color, 'brown', 4)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 4)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(4), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+
+ @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available')
+ def test_extend_enum_plain_stdlib(self):
+ class Color(StdlibEnum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(TypeError, 'already in use as', extend_enum, Color, 'blue', 5)
+ #
+ extend_enum(Color, 'brown', 4)
+ self.assertEqual(Color.brown.name, 'brown')
+ self.assertEqual(Color.brown.value, 4)
+ self.assertTrue(Color.brown in Color)
+ self.assertEqual(Color(4), Color.brown)
+ self.assertEqual(Color['brown'], Color.brown)
+ self.assertEqual(len(Color), 4)
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.brown])
+ self.assertEqual([c.value for c in Color], [1, 2, 3, 4])
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 5)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(5), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), 5)
+
+ @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available')
+ def test_extend_enum_alias_stdlib(self):
+ class Color(StdlibEnum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'rojo', 1)
+ self.assertEqual(Color.rojo.name, 'red')
+ self.assertEqual(Color.rojo.value, 1)
+ self.assertTrue(Color.rojo in Color)
+ self.assertEqual(Color(1), Color.rojo)
+ self.assertEqual(Color['rojo'], Color.red)
+ self.assertEqual(len(Color), 3)
+
+ @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available')
+ def test_extend_enum_shadow_property_stdlib(self):
+ class Color(StdlibEnum):
+ red = 1
+ green = 2
+ blue = 3
+ extend_enum(Color, 'value', 4)
+ self.assertEqual(Color.value.name, 'value')
+ self.assertEqual(Color.value.value, 4)
+ self.assertTrue(Color.value in Color)
+ self.assertEqual(Color(4), Color.value)
+ self.assertEqual(Color['value'], Color.value)
+ self.assertEqual(len(Color), 4)
+ self.assertEqual(Color.red.value, 1)
+
+ @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available')
+ def test_extend_enum_shadow_base_stdlib(self):
+ class hohum(object):
+ def cyan(self):
+ "cyanize a color"
+ return self.value
+ class Color(hohum, StdlibEnum):
+ red = 1
+ green = 2
+ blue = 3
+ self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4)
+ self.assertEqual(len(Color), 3)
+ self.assertEqual(list(Color), [Color.red, Color.green, Color.blue])
+
+ @unittest.skipUnless(StdlibIntEnum, 'Stdlib IntEnum not available')
+ def test_extend_intenum_stdlib(self):
+ class Index(StdlibIntEnum):
+ 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)
+
+ @unittest.skipUnless(StdlibIntEnum, 'Stdlib IntEnum not available')
+ def test_extend_multi_init_stdlib(self):
+ class HTTPStatus(StdlibIntEnum):
+ def __new__(cls, value, phrase, description):
+ obj = int.__new__(cls, value)
+ obj._value_ = value
+
+ obj.phrase = phrase
+ obj.description = description
+ return obj
+ CONTINUE = 100, 'Continue', 'Request received, please continue'
+ SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header'
+ PROCESSING = 102, 'Processing', ''
+ length = 3
+ extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train')
+ extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '')
+ self.assertEqual(len(HTTPStatus), length+2)
+ self.assertEqual(
+ list(HTTPStatus)[-2:],
+ [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS],
+ )
+ self.assertEqual(HTTPStatus.BAD_SPAM.value, 513)
+ self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM')
+ self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy')
+ self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train')
+ self.assertEqual(HTTPStatus.BAD_EGGS.value, 514)
+ self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS')
+ self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green')
+ self.assertEqual(HTTPStatus.BAD_EGGS.description, '')
+
+ @unittest.skipUnless(StdlibFlag, 'Stdlib Flag not available')
+ def test_extend_flag_stdlib(self):
+ class Color(StdlibFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, StdlibFlag))
+
+ @unittest.skipUnless(StdlibFlag, 'Stdlib Flag not available')
+ def test_extend_flag_backwards_stdlib(self):
+ class Color(StdlibFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, StdlibFlag))
+ #
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(16) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value,16)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 32)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(32), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+
+ @unittest.skipUnless(StdlibIntFlag, 'Stdlib IntFlag not available')
+ def test_extend_intflag_stdlib(self):
+ class Color(StdlibIntFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(8) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, 8)
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, StdlibFlag))
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, 16)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(16), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+
+ @unittest.skipUnless(StdlibIntFlag, 'Stdlib IntFlag not available')
+ def test_extend_intflag_backwards_stdlib(self):
+ class Color(StdlibIntFlag):
+ BLACK = 0
+ RED = 1
+ GREEN = 2
+ BLUE = 4
+ if pyver >= PY3_11:
+ # flags make more sense in 3.11
+ length = 5
+ MAGENTA = 8
+ mauve = 16
+ else:
+ length = 7
+ MAGENTA = 16
+ mauve = 32
+ extend_enum(Color, 'PURPLE', 11)
+ self.assertTrue(Color(11) is Color.PURPLE)
+ self.assertTrue(isinstance(Color.PURPLE, Color))
+ self.assertEqual(Color.PURPLE.value, 11)
+ self.assertTrue(issubclass(Color, StdlibFlag))
+ #
+ extend_enum(Color, 'MAGENTA')
+ self.assertTrue(Color(MAGENTA) is Color.MAGENTA)
+ self.assertTrue(isinstance(Color.MAGENTA, Color))
+ self.assertEqual(Color.MAGENTA.value, MAGENTA)
+ #
+ extend_enum(Color, 'mauve')
+ self.assertEqual(Color.mauve.name, 'mauve')
+ self.assertEqual(Color.mauve.value, mauve)
+ self.assertTrue(Color.mauve in Color)
+ self.assertEqual(Color(mauve), Color.mauve)
+ self.assertEqual(Color['mauve'], Color.mauve)
+ self.assertEqual(len(Color), length, list(Color))
+
+ @unittest.skipUnless(StdlibStrEnum, 'Stdlib StrEnum not available')
+ def test_extend_strenum_stdlib(self):
+ class Color(StrEnum):
+ RED = auto()
+ GREEN = auto()
+ BLUE = auto()
+ extend_enum(Color, 'BLACK')
+ self.assertEqual(Color.BLACK.name, 'BLACK')
+ self.assertEqual(Color.BLACK.value, 'black')
+ self.assertEqual(len(Color), 4)
+
+
+if __name__ == '__main__':
+ raise RuntimeError("'test_v3.py' should not be run by itself; it's included in 'test.py'")
diff --git a/venv/Lib/site-packages/blendmodes/__init__.py b/venv/Lib/site-packages/blendmodes/__init__.py
new file mode 100644
index 00000000..2b6e91ad
--- /dev/null
+++ b/venv/Lib/site-packages/blendmodes/__init__.py
@@ -0,0 +1,2 @@
+"""Use this module to apply a number of blending modes to a background and foreground image
+"""
diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 00000000..da1d6a38
--- /dev/null
+++ b/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc
new file mode 100644
index 00000000..23184600
--- /dev/null
+++ b/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc
new file mode 100644
index 00000000..c599cfb8
--- /dev/null
+++ b/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/blendmodes/blend.py b/venv/Lib/site-packages/blendmodes/blend.py
new file mode 100644
index 00000000..4165bfa7
--- /dev/null
+++ b/venv/Lib/site-packages/blendmodes/blend.py
@@ -0,0 +1,511 @@
+"""Provide blending functions and types.
+
+Adapted from https://github.com/addisonElliott/pypdn/blob/master/pypdn/reader.py
+and https://gitlab.com/inklabapp/pyora/-/blob/master/pyora/BlendNonSep.py
+MIT License Copyright (c) 2020 FredHappyface
+
+Credits to:
+
+MIT License Copyright (c) 2019 Paul Jewell
+For implementing blending from the Open Raster Image Spec
+
+MIT License Copyright (c) 2018 Addison Elliott
+For implementing blending from Paint.NET
+
+MIT License Copyright (c) 2017 pashango
+For implementing a number of blending functions used by other popular image
+editors
+"""
+
+from __future__ import annotations
+
+import warnings
+
+import numpy as np
+from PIL import Image
+
+from .blendtype import BlendType
+
+
+def normal(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.NORMAL."""
+ del background # we don't care about this
+ return foreground
+
+
+def multiply(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.MULTIPLY."""
+ return np.clip(foreground * background, 0.0, 1.0)
+
+
+def additive(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.ADDITIVE."""
+ return np.minimum(background + foreground, 1.0)
+
+
+def colourburn(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.COLOURBURN."""
+ with np.errstate(divide="ignore"):
+ return np.where(
+ foreground != 0.0, np.maximum(1.0 - ((1.0 - background) / foreground), 0.0), 0.0
+ )
+
+
+def colourdodge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.COLOURDODGE."""
+ with np.errstate(divide="ignore"):
+ return np.where(foreground != 1.0, np.minimum(background / (1.0 - foreground), 1.0), 1.0)
+
+
+def reflect(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.REFLECT."""
+ with np.errstate(divide="ignore"):
+ return np.where(
+ foreground != 1.0, np.minimum((background ** 2) / (1.0 - foreground), 1.0), 1.0
+ )
+
+
+def glow(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.GLOW."""
+ with np.errstate(divide="ignore"):
+ return np.where(
+ background != 1.0, np.minimum((foreground ** 2) / (1.0 - background), 1.0), 1.0
+ )
+
+
+def overlay(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.OVERLAY."""
+ return np.where(
+ background < 0.5,
+ 2 * background * foreground,
+ 1.0 - (2 * (1.0 - background) * (1.0 - foreground)),
+ )
+
+
+def difference(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.DIFFERENCE."""
+ return np.abs(background - foreground)
+
+
+def negation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.NEGATION."""
+ return np.maximum(background - foreground, 0.0)
+
+
+def lighten(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.LIGHTEN."""
+ return np.maximum(background, foreground)
+
+
+def darken(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.DARKEN."""
+ return np.minimum(background, foreground)
+
+
+def screen(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.SCREEN."""
+ return background + foreground - background * foreground
+
+
+def xor(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.XOR."""
+ # XOR requires int values so convert to uint8
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ return imageIntToFloat(imageFloatToInt(background) ^ imageFloatToInt(foreground))
+
+
+def softlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.SOFTLIGHT."""
+ return (1.0 - background) * background * foreground + background * (
+ 1.0 - (1.0 - background) * (1.0 - foreground)
+ )
+
+
+def hardlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.HARDLIGHT."""
+ return np.where(
+ foreground < 0.5,
+ np.minimum(background * 2 * foreground, 1.0),
+ np.minimum(1.0 - ((1.0 - background) * (1.0 - (foreground - 0.5) * 2.0)), 1.0),
+ )
+
+
+def grainextract(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.GRAINEXTRACT."""
+ return np.clip(background - foreground + 0.5, 0.0, 1.0)
+
+
+def grainmerge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.GRAINMERGE."""
+ return np.clip(background + foreground - 0.5, 0.0, 1.0)
+
+
+def divide(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.DIVIDE."""
+ return np.minimum((256.0 / 255.0 * background) / (1.0 / 255.0 + foreground), 1.0)
+
+
+def pinlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.PINLIGHT."""
+ return np.minimum(background, 2 * foreground) * (foreground < 0.5) + np.maximum(
+ background, 2 * (foreground - 0.5)
+ ) * (foreground >= 0.5)
+
+
+def vividlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.VIVIDLIGHT."""
+ return colourburn(background, foreground * 2) * (foreground < 0.5) + colourdodge(
+ background, 2 * (foreground - 0.5)
+ ) * (foreground >= 0.5)
+
+
+def exclusion(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.EXCLUSION."""
+ return background + foreground - (2.0 * background * foreground)
+
+
+def _lum(colours: np.ndarray) -> np.ndarray:
+ """Luminosity.
+
+ :param colours: x by x by 3 matrix of rgb color components of pixels
+ :return: x by x by 3 matrix of luminosity of pixels
+ """
+ return (colours[:, :, 0] * 0.299) + (colours[:, :, 1] * 0.587) + (colours[:, :, 2] * 0.114)
+
+
+def _setLum(originalColours: np.ndarray, newLuminosity: np.ndarray) -> np.ndarray:
+ """Set a new luminosity value for the matrix of color."""
+ _colours = originalColours.copy()
+ _luminosity = _lum(_colours)
+ deltaLum = newLuminosity - _luminosity
+ _colours[:, :, 0] += deltaLum
+ _colours[:, :, 1] += deltaLum
+ _colours[:, :, 2] += deltaLum
+ _luminosity = _lum(_colours)
+ _minColours = np.min(_colours, axis=2)
+ _MaxColours = np.max(_colours, axis=2)
+ for i in range(_colours.shape[0]):
+ for j in range(_colours.shape[1]):
+ _colour = _colours[i][j]
+ newLuminosity = _luminosity[i, j]
+ minColour = _minColours[i, j]
+ maxColour = _MaxColours[i, j]
+ if minColour < 0:
+ _colours[i][j] = newLuminosity + (
+ ((_colour - newLuminosity) * newLuminosity) / (newLuminosity - minColour)
+ )
+ if maxColour > 1:
+ _colours[i][j] = newLuminosity + (
+ ((_colour - newLuminosity) * (1 - newLuminosity)) / (maxColour - newLuminosity)
+ )
+ return _colours
+
+
+def _sat(colours: np.ndarray) -> np.ndarray:
+ """Saturation.
+
+ :param colours: x by x by 3 matrix of rgb color components of pixels
+ :return: int of saturation of pixels
+ """
+ return np.max(colours, axis=2) - np.min(colours, axis=2)
+
+
+def _setSat(originalColours: np.ndarray, newSaturation: np.ndarray) -> np.ndarray:
+ """Set a new saturation value for the matrix of color.
+
+ The current implementation cannot be vectorized in an efficient manner,
+ so it is very slow,
+ O(m*n) at least. This might be able to be improved with openCL if that is
+ the direction that the lib takes.
+ :param c: x by x by 3 matrix of rgb color components of pixels
+ :param s: int of the new saturation value for the matrix
+ :return: x by x by 3 matrix of luminosity of pixels
+ """
+ _colours = originalColours.copy()
+ for i in range(_colours.shape[0]):
+ for j in range(_colours.shape[1]):
+ _colour = _colours[i][j]
+ minI = 0
+ midI = 1
+ maxI = 2
+ if _colour[midI] < _colour[minI]:
+ minI, midI = midI, minI
+ if _colour[maxI] < _colour[midI]:
+ midI, maxI = maxI, midI
+ if _colour[midI] < _colour[minI]:
+ minI, midI = midI, minI
+ if _colour[maxI] - _colour[minI] > 0.0:
+ _colours[i][j][midI] = ((_colour[midI] - _colour[minI]) * newSaturation[i, j]) / (
+ _colour[maxI] - _colour[minI]
+ )
+ _colours[i][j][maxI] = newSaturation[i, j]
+ else:
+ _colours[i][j][midI] = 0
+ _colours[i][j][maxI] = 0
+ _colours[i][j][minI] = 0
+ return _colours
+
+
+def hue(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.HUE."""
+ return _setLum(_setSat(foreground, _sat(background)), _lum(background))
+
+
+def saturation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.SATURATION."""
+ return _setLum(_setSat(background, _sat(foreground)), _lum(background))
+
+
+def colour(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.COLOUR."""
+ return _setLum(foreground, _lum(background))
+
+
+def luminosity(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.LUMINOSITY."""
+ return _setLum(background, _lum(foreground))
+
+
+def destin(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """'clip' composite mode.
+
+ All parts of 'layer above' which are alpha in 'layer below' will be made
+ also alpha in 'layer above'
+ (to whatever degree of alpha they were)
+
+ Destination which overlaps the source, replaces the source.
+
+ Fa = 0; Fb = αs
+ co = αb x Cb x αs
+ αo = αb x αs
+ """
+ del foregroundColour # Not used by function
+ outAlpha = backgroundAlpha * foregroundAlpha
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+ return outRGB, outAlpha
+
+
+def destout(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """Reverse 'Clip' composite mode.
+
+ All parts of 'layer below' which are alpha in 'layer above' will be made
+ also alpha in 'layer below'
+ (to whatever degree of alpha they were)
+
+ """
+ del foregroundColour # Not used by function
+ outAlpha = backgroundAlpha * (1 - foregroundAlpha)
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+ return outRGB, outAlpha
+
+
+def destatop(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """Place the layer below above the 'layer above' in places where the 'layer above' exists...
+
+ where 'layer below' does not exist, but 'layer above' does, place 'layer-above'
+
+ """
+ outAlpha = (foregroundAlpha * (1 - backgroundAlpha)) + (backgroundAlpha * foregroundAlpha)
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((foregroundAlpha * (1 - backgroundAlpha))[:, :, None], foregroundColour)
+ + np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+ return outRGB, outAlpha
+
+
+def srcatop(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """Place the layer below above the 'layer above' in places where the 'layer above' exists."""
+ outAlpha = (foregroundAlpha * backgroundAlpha) + (backgroundAlpha * (1 - foregroundAlpha))
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((foregroundAlpha * backgroundAlpha)[:, :, None], foregroundColour)
+ + np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+
+ return outRGB, outAlpha
+
+
+def imageIntToFloat(image: np.ndarray) -> np.ndarray:
+ """Convert a numpy array representing an image to an array of floats.
+
+ Args:
+ image (np.ndarray): numpy array of ints
+
+ Returns:
+ np.ndarray: numpy array of floats
+ """
+ return image / 255
+
+
+def imageFloatToInt(image: np.ndarray) -> np.ndarray:
+ """Convert a numpy array representing an image to an array of ints.
+
+ Args:
+ image (np.ndarray): numpy array of floats
+
+ Returns:
+ np.ndarray: numpy array of ints
+ """
+ return (image * 255).astype(np.uint8)
+
+
+def blend(background: np.ndarray, foreground: np.ndarray, blendType: BlendType) -> np.ndarray:
+ """Blend pixels.
+
+ Args:
+ background (np.ndarray): background
+ foreground (np.ndarray): foreground
+ blendType (BlendType): the blend type
+
+ Returns:
+ np.ndarray: new array representing the image
+
+ background: np.ndarray,
+ foreground: np.ndarray and the return are in the form
+
+ [[[0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]
+ ...
+ [0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]]
+
+ ...
+
+ [[0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]
+ ...
+ [0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]]]
+ """
+ blendLookup = {
+ BlendType.NORMAL: normal,
+ BlendType.MULTIPLY: multiply,
+ BlendType.COLOURBURN: colourburn,
+ BlendType.COLOURDODGE: colourdodge,
+ BlendType.REFLECT: reflect,
+ BlendType.OVERLAY: overlay,
+ BlendType.DIFFERENCE: difference,
+ BlendType.LIGHTEN: lighten,
+ BlendType.DARKEN: darken,
+ BlendType.SCREEN: screen,
+ BlendType.SOFTLIGHT: softlight,
+ BlendType.HARDLIGHT: hardlight,
+ BlendType.GRAINEXTRACT: grainextract,
+ BlendType.GRAINMERGE: grainmerge,
+ BlendType.DIVIDE: divide,
+ BlendType.HUE: hue,
+ BlendType.SATURATION: saturation,
+ BlendType.COLOUR: colour,
+ BlendType.LUMINOSITY: luminosity,
+ BlendType.XOR: xor,
+ BlendType.NEGATION: negation,
+ BlendType.PINLIGHT: pinlight,
+ BlendType.VIVIDLIGHT: vividlight,
+ BlendType.EXCLUSION: exclusion,
+ }
+
+ if blendType not in blendLookup:
+ return normal(background, foreground)
+ return blendLookup[blendType](background, foreground)
+
+
+def blendLayers(
+ background: Image.Image,
+ foreground: Image.Image,
+ blendType: BlendType | tuple[str, ...],
+ opacity: float = 1.0,
+) -> Image.Image:
+ """Blend layers using numpy array.
+
+ Args:
+ background (Image.Image): background layer
+ foreground (Image.Image): foreground layer (must be same size as background)
+ blendType (BlendType): The blendtype
+ opacity (float): The opacity of the foreground image
+
+ Returns:
+ Image.Image: combined image
+ """
+ # Convert the Image.Image to a numpy array
+ npForeground: np.ndarray = imageIntToFloat(np.array(foreground.convert("RGBA")))
+ npBackground: np.ndarray = imageIntToFloat(np.array(background.convert("RGBA")))
+
+ # Get the alpha from the layers
+ backgroundAlpha = npBackground[:, :, 3]
+ foregroundAlpha = npForeground[:, :, 3] * opacity
+ combinedAlpha = backgroundAlpha * foregroundAlpha
+
+ # Get the colour from the layers
+ backgroundColor = npBackground[:, :, 0:3]
+ foregroundColor = npForeground[:, :, 0:3]
+
+ # Some effects require alpha
+ alphaFunc = {
+ BlendType.DESTIN: destin,
+ BlendType.DESTOUT: destout,
+ BlendType.SRCATOP: srcatop,
+ BlendType.DESTATOP: destatop,
+ }
+
+ if blendType in alphaFunc:
+ return Image.fromarray(
+ imageFloatToInt(
+ np.clip(
+ np.dstack(
+ alphaFunc[blendType](
+ backgroundAlpha, foregroundAlpha, backgroundColor, foregroundColor
+ )
+ ),
+ a_min=0,
+ a_max=1,
+ )
+ )
+ )
+
+ # Get the colours and the alpha for the new image
+ colorComponents = (
+ (backgroundAlpha - combinedAlpha)[:, :, None] * backgroundColor
+ + (foregroundAlpha - combinedAlpha)[:, :, None] * foregroundColor
+ + combinedAlpha[:, :, None] * blend(backgroundColor, foregroundColor, blendType)
+ )
+ alphaComponent = backgroundAlpha + foregroundAlpha - combinedAlpha
+
+ return Image.fromarray(
+ imageFloatToInt(np.clip(np.dstack((colorComponents, alphaComponent)), a_min=0, a_max=1))
+ )
diff --git a/venv/Lib/site-packages/blendmodes/blendtype.py b/venv/Lib/site-packages/blendmodes/blendtype.py
new file mode 100644
index 00000000..1bde12a6
--- /dev/null
+++ b/venv/Lib/site-packages/blendmodes/blendtype.py
@@ -0,0 +1,72 @@
+"""Specify supported blend types."""
+
+from __future__ import annotations
+
+from aenum import MultiValueEnum
+
+
+class BlendType(str, MultiValueEnum):
+ """Specify supported blend types.
+
+ NORMAL = "bm:normal", "normal"
+ MULTIPLY = "bm:multiply", "multiply"
+ ADDITIVE = "bm:additive", "additive"
+ COLOURBURN = "bm:colourburn", "colourburn"
+ COLOURDODGE = "bm:colourdodge", "colourdodge"
+ REFLECT = "bm:reflect", "reflect"
+ GLOW = "bm:glow", "glow"
+ OVERLAY = "bm:overlay", "overlay"
+ DIFFERENCE = "bm:difference", "difference"
+ NEGATION = "bm:negation", "negation"
+ LIGHTEN = "bm:lighten", "lighten"
+ DARKEN = "bm:darken", "darken"
+ SCREEN = "bm:screen", "screen"
+ XOR = "bm:xor", "xor"
+ SOFTLIGHT = "bm:softlight", "softlight"
+ HARDLIGHT = "bm:hardlight", "hardlight"
+ GRAINEXTRACT = "bm:grainextract", "grainextract"
+ GRAINMERGE = "bm:grainmerge", "grainmerge"
+ DIVIDE = "bm:divide", "divide"
+ HUE = "bm:hue", "hue"
+ SATURATION = "bm:saturation", "saturation"
+ COLOUR = "bm:colour", "colour"
+ LUMINOSITY = "bm:luminosity", "luminosity"
+ PINLIGHT = "bm:pinlight", "pinlight"
+ VIVIDLIGHT = "bm:vividlight", "vividlight"
+ EXCLUSION = "bm:exclusion", "exclusion"
+ DESTIN = "bm:destin", "destin"
+ DESTOUT = "bm:destout", "destout"
+ SRCATOP = "bm:srcatop", "srcatop"
+ DESTATOP = "bm:destatop", "destatop"
+ """
+
+ NORMAL = "bm:normal", "normal"
+ MULTIPLY = "bm:multiply", "multiply"
+ ADDITIVE = "bm:additive", "additive"
+ COLOURBURN = "bm:colourburn", "colourburn"
+ COLOURDODGE = "bm:colourdodge", "colourdodge"
+ REFLECT = "bm:reflect", "reflect"
+ GLOW = "bm:glow", "glow"
+ OVERLAY = "bm:overlay", "overlay"
+ DIFFERENCE = "bm:difference", "difference"
+ NEGATION = "bm:negation", "negation"
+ LIGHTEN = "bm:lighten", "lighten"
+ DARKEN = "bm:darken", "darken"
+ SCREEN = "bm:screen", "screen"
+ XOR = "bm:xor", "xor"
+ SOFTLIGHT = "bm:softlight", "softlight"
+ HARDLIGHT = "bm:hardlight", "hardlight"
+ GRAINEXTRACT = "bm:grainextract", "grainextract"
+ GRAINMERGE = "bm:grainmerge", "grainmerge"
+ DIVIDE = "bm:divide", "divide"
+ HUE = "bm:hue", "hue"
+ SATURATION = "bm:saturation", "saturation"
+ COLOUR = "bm:colour", "colour"
+ LUMINOSITY = "bm:luminosity", "luminosity"
+ PINLIGHT = "bm:pinlight", "pinlight"
+ VIVIDLIGHT = "bm:vividlight", "vividlight"
+ EXCLUSION = "bm:exclusion", "exclusion"
+ DESTIN = "bm:destin", "destin"
+ DESTOUT = "bm:destout", "destout"
+ SRCATOP = "bm:srcatop", "srcatop"
+ DESTATOP = "bm:destatop", "destatop"
diff --git a/venv/Lib/site-packages/blendmodes/imagetools.py b/venv/Lib/site-packages/blendmodes/imagetools.py
new file mode 100644
index 00000000..834b7c86
--- /dev/null
+++ b/venv/Lib/site-packages/blendmodes/imagetools.py
@@ -0,0 +1,48 @@
+"""Do stuff to images to prepare them.
+"""
+from __future__ import annotations
+
+import warnings
+
+from deprecation import deprecated
+from PIL import Image
+
+
+@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset")
+def rasterImageOA( # pylint:disable=missing-function-docstring
+ image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0)
+) -> Image.Image:
+ warnings.warn(
+ "Call to deprecated function rasterImageOA.", category=DeprecationWarning, stacklevel=2
+ )
+ return renderWAlphaOffset(image, size, alpha, offsets)
+
+
+@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset")
+def rasterImageOffset( # pylint:disable=missing-function-docstring
+ image: Image.Image, size: tuple[int, int], offsets: tuple[int, int] = (0, 0)
+) -> Image.Image:
+ warnings.warn(
+ "Call to deprecated function rasterImageOffset.", category=DeprecationWarning, stacklevel=2
+ )
+ return renderWAlphaOffset(image, size, 1, offsets)
+
+
+def renderWAlphaOffset(
+ image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0)
+) -> Image.Image:
+ """Render an image with offset and alpha to a given size.
+
+ Args:
+ image (Image.Image): pil image to draw
+ size (tuple[int, int]): width, height as a tuple
+ alpha (float, optional): alpha transparency. Defaults to 1.0.
+ offsets (tuple[int, int], optional): x, y offsets as a tuple.
+ Defaults to (0, 0).
+
+ Returns:
+ Image.Image: new image
+ """
+ imageOffset = Image.new("RGBA", size)
+ imageOffset.paste(image.convert("RGBA"), offsets, image.convert("RGBA"))
+ return Image.blend(Image.new("RGBA", size), imageOffset, alpha)