mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	issue23591: add docs; code cleanup; more tests
This commit is contained in:
		
							parent
							
								
									c19f00b238
								
							
						
					
					
						commit
						65a5a47d79
					
				
					 3 changed files with 213 additions and 78 deletions
				
			
		|  | @ -23,9 +23,9 @@ by identity, and the enumeration itself can be iterated over. | |||
| Module Contents | ||||
| --------------- | ||||
| 
 | ||||
| This module defines two enumeration classes that can be used to define unique | ||||
| sets of names and values: :class:`Enum` and :class:`IntEnum`.  It also defines | ||||
| one decorator, :func:`unique`. | ||||
| This module defines four enumeration classes that can be used to define unique | ||||
| sets of names and values: :class:`Enum`, :class:`IntEnum`, and | ||||
| :class:`IntFlags`.  It also defines one decorator, :func:`unique`. | ||||
| 
 | ||||
| .. class:: Enum | ||||
| 
 | ||||
|  | @ -37,10 +37,23 @@ one decorator, :func:`unique`. | |||
|     Base class for creating enumerated constants that are also | ||||
|     subclasses of :class:`int`. | ||||
| 
 | ||||
| .. class:: IntFlag | ||||
| 
 | ||||
|     Base class for creating enumerated constants that can be combined using | ||||
|     the bitwise operators without losing their :class:`IntFlag` membership. | ||||
|     :class:`IntFlag` members are also subclasses of :class:`int`. | ||||
| 
 | ||||
| .. class:: Flag | ||||
| 
 | ||||
|     Base class for creating enumerated constants that can be combined using | ||||
|     the bitwise operations without losing their :class:`Flag` membership. | ||||
| 
 | ||||
| .. function:: unique | ||||
| 
 | ||||
|     Enum class decorator that ensures only one name is bound to any one value. | ||||
| 
 | ||||
| .. versionadded:: 3.6  ``Flag``, ``IntFlag`` | ||||
| 
 | ||||
| 
 | ||||
| Creating an Enum | ||||
| ---------------- | ||||
|  | @ -478,7 +491,7 @@ Derived Enumerations | |||
| IntEnum | ||||
| ^^^^^^^ | ||||
| 
 | ||||
| A variation of :class:`Enum` is provided which is also a subclass of | ||||
| The first variation of :class:`Enum` that is provided is also a subclass of | ||||
| :class:`int`.  Members of an :class:`IntEnum` can be compared to integers; | ||||
| by extension, integer enumerations of different types can also be compared | ||||
| to each other:: | ||||
|  | @ -521,13 +534,54 @@ However, they still can't be compared to standard :class:`Enum` enumerations:: | |||
|     >>> [i for i in range(Shape.square)] | ||||
|     [0, 1] | ||||
| 
 | ||||
| For the vast majority of code, :class:`Enum` is strongly recommended, | ||||
| since :class:`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 :class:`Enum` provided, :class:`IntFlag`, is also based | ||||
| on :class:`int`.  The difference being :class:`IntFlag` members can be combined | ||||
| using the bitwise operators (&, \|, ^, ~) and the result is still an | ||||
| :class:`IntFlag` member.  However, as the name implies, :class:`IntFlag` | ||||
| members also subclass :class:`int` and can be used wherever an :class:`int` is. | ||||
| Any operation on an :class:`IntFlag` member besides the bit-wise operations | ||||
| will lose the :class:`IntFlag` membership. | ||||
| 
 | ||||
|     >>> from enum import IntFlag | ||||
|     >>> class Perm(IntFlag): | ||||
|     ...     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 | ||||
| 
 | ||||
| .. versionadded:: 3.6 | ||||
| 
 | ||||
| 
 | ||||
| Flag | ||||
| ^^^^ | ||||
| 
 | ||||
| The last variation is :class:`Flag`.  Like :class:`IntFlag`, :class:`Flag` | ||||
| members can be combined using the bitwise operators (^, \|, ^, ~).  Unlike | ||||
| :class:`IntFlag`, they cannot be combined with, nor compared against, any | ||||
| other :class:`Flag` enumeration nor :class:`int`. | ||||
| 
 | ||||
| .. versionadded:: 3.6 | ||||
| 
 | ||||
| .. note:: | ||||
| 
 | ||||
|     For the majority of new code, :class:`Enum` and :class:`Flag` are strongly | ||||
|     recommended, since :class:`IntEnum` and :class:`IntFlag` break some | ||||
|     semantic promises of an enumeration (by being comparable to integers, and | ||||
|     thus by transitivity to other unrelated enumerations).  :class:`IntEnum` | ||||
|     and :class:`IntFlag` should be used only in cases where :class:`Enum` and | ||||
|     :class:`Flag` will not do; for example, when integer constants are replaced | ||||
|     with enumerations, or for interoperability with other systems. | ||||
| 
 | ||||
| 
 | ||||
| Others | ||||
|  | @ -567,10 +621,10 @@ Some rules: | |||
| Interesting examples | ||||
| -------------------- | ||||
| 
 | ||||
| While :class:`Enum` and :class:`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, or as examples for creating | ||||
| one's own. | ||||
| While :class:`Enum`, :class:`IntEnum`, :class:`IntFlag`, and :class:`Flag` 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, | ||||
| or as examples for creating one's own. | ||||
| 
 | ||||
| 
 | ||||
| AutoNumber | ||||
|  | @ -731,55 +785,33 @@ member instances. | |||
| Finer Points | ||||
| ^^^^^^^^^^^^ | ||||
| 
 | ||||
| :class:`Enum` members are instances of an :class:`Enum` class, and even | ||||
| though they are accessible as `EnumClass.member`, they should not be accessed | ||||
| directly from the member as that lookup may fail or, worse, return something | ||||
| besides the :class:`Enum` member you looking for:: | ||||
| Supported ``__dunder__`` names | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
|     >>> class FieldTypes(Enum): | ||||
|     ...     name = 0 | ||||
|     ...     value = 1 | ||||
|     ...     size = 2 | ||||
|     ... | ||||
|     >>> FieldTypes.value.size | ||||
|     <FieldTypes.size: 2> | ||||
|     >>> FieldTypes.size.value | ||||
|     2 | ||||
| :attr:`__members__` is an :class:`OrderedDict` of ``member_name``:``member`` | ||||
| items.  It is only available on the class. | ||||
| 
 | ||||
| .. versionchanged:: 3.5 | ||||
| :meth:`__new__`, if specified, must create and return the enum members; it is | ||||
| also a very good idea to set the member's :attr:`_value_` appropriately.  Once | ||||
| all the members are created it is no longer used. | ||||
| 
 | ||||
| Boolean evaluation: Enum classes that are mixed with non-Enum types (such as | ||||
| :class:`int`, :class:`str`, etc.) are evaluated according to the mixed-in | ||||
| type's rules; otherwise, all members evaluate as ``True``.  To make your own | ||||
| Enum's boolean evaluation depend on the member's value add the following to | ||||
| your class:: | ||||
| 
 | ||||
|     def __bool__(self): | ||||
|         return bool(self.value) | ||||
| Supported ``_sunder_`` names | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| The :attr:`__members__` attribute is only available on the class. | ||||
| - ``_name_`` -- name of the member | ||||
| - ``_value_`` -- value of the member; can be set / modified in ``__new__`` | ||||
| 
 | ||||
| If you give your :class:`Enum` subclass extra methods, like the `Planet`_ | ||||
| class above, those methods will show up in a :func:`dir` of the member, | ||||
| but not of the class:: | ||||
| - ``_missing_`` -- a lookup function used when a value is not found; may be | ||||
|   overridden | ||||
| - ``_order_`` -- used in Python 2/3 code to ensure member order is consistent | ||||
|   (class attribute, removed during class creation) | ||||
| 
 | ||||
|     >>> dir(Planet) | ||||
|     ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__'] | ||||
|     >>> dir(Planet.EARTH) | ||||
|     ['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value'] | ||||
| .. versionadded:: 3.6 ``_missing_``, ``_order_`` | ||||
| 
 | ||||
| The :meth:`__new__` method will only be used for the creation of the | ||||
| :class:`Enum` members -- after that it is replaced.  Any custom :meth:`__new__` | ||||
| method must create the object and set the :attr:`_value_` attribute | ||||
| appropriately. | ||||
| 
 | ||||
| If you wish to change how :class:`Enum` members are looked up you should either | ||||
| write a helper function or a :func:`classmethod` for the :class:`Enum` | ||||
| subclass. | ||||
| 
 | ||||
| To help keep Python 2 / Python 3 code in sync a user-specified :attr:`_order_`, | ||||
| if provided, will be checked to ensure the actual order of the enumeration | ||||
| matches:: | ||||
| To help keep Python 2 / Python 3 code in sync an :attr:`_order_` attribute can | ||||
| be provided.  It will be checked against the actual order of the enumeration | ||||
| and raise an error if the two do not match:: | ||||
| 
 | ||||
|     >>> class Color(Enum): | ||||
|     ...     _order_ = 'red green blue' | ||||
|  | @ -794,4 +826,53 @@ matches:: | |||
| .. note:: | ||||
| 
 | ||||
|     In Python 2 code the :attr:`_order_` attribute is necessary as definition | ||||
|     order is lost during class creation. | ||||
|     order is lost before it can be recorded. | ||||
| 
 | ||||
| ``Enum`` member type | ||||
| ~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| :class:`Enum` members are instances of an :class:`Enum` class, and even | ||||
| though they are accessible as `EnumClass.member`, they should not be accessed | ||||
| directly from the member as that lookup may fail or, worse, return something | ||||
| besides the ``Enum`` member you looking for:: | ||||
| 
 | ||||
|     >>> class FieldTypes(Enum): | ||||
|     ...     name = 0 | ||||
|     ...     value = 1 | ||||
|     ...     size = 2 | ||||
|     ... | ||||
|     >>> FieldTypes.value.size | ||||
|     <FieldTypes.size: 2> | ||||
|     >>> FieldTypes.size.value | ||||
|     2 | ||||
| 
 | ||||
| .. versionchanged:: 3.5 | ||||
| 
 | ||||
| 
 | ||||
| Boolean value of ``Enum`` classes and members | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| ``Enum`` members that are mixed with non-Enum types (such as | ||||
| :class:`int`, :class:`str`, etc.) are evaluated according to the mixed-in | ||||
| type's rules; otherwise, all members evaluate as :data:`True`.  To make your own | ||||
| Enum's boolean evaluation depend on the member's value add the following to | ||||
| your class:: | ||||
| 
 | ||||
|     def __bool__(self): | ||||
|         return bool(self.value) | ||||
| 
 | ||||
| ``Enum`` classes always evaluate as :data:`True`. | ||||
| 
 | ||||
| 
 | ||||
| ``Enum`` classes with methods | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| If you give your :class:`Enum` subclass extra methods, like the `Planet`_ | ||||
| class above, those methods will show up in a :func:`dir` of the member, | ||||
| but not of the class:: | ||||
| 
 | ||||
|     >>> dir(Planet) | ||||
|     ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__'] | ||||
|     >>> dir(Planet.EARTH) | ||||
|     ['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value'] | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ | |||
|     from collections import OrderedDict | ||||
| 
 | ||||
| 
 | ||||
| __all__ = ['EnumMeta', 'Enum', 'IntEnum', 'Flags', 'IntFlags', 'unique'] | ||||
| __all__ = ['EnumMeta', 'Enum', 'IntEnum', 'Flag', 'IntFlag', 'unique'] | ||||
| 
 | ||||
| 
 | ||||
| def _is_descriptor(obj): | ||||
|  | @ -104,7 +104,7 @@ def __prepare__(metacls, cls, bases): | |||
|             enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) | ||||
|         return enum_dict | ||||
| 
 | ||||
|     def __new__(metacls, cls, bases, classdict, **kwds): | ||||
|     def __new__(metacls, cls, bases, classdict): | ||||
|         # an Enum class is final once enumeration items have been defined; it | ||||
|         # cannot be mixed with other types (int, float, etc.) if it has an | ||||
|         # inherited __new__ unless a new __new__ is defined (or the resulting | ||||
|  | @ -614,7 +614,7 @@ class IntEnum(int, Enum): | |||
| def _reduce_ex_by_name(self, proto): | ||||
|     return self.name | ||||
| 
 | ||||
| class Flags(Enum): | ||||
| class Flag(Enum): | ||||
|     """Support for flags""" | ||||
|     @staticmethod | ||||
|     def _generate_next_value_(name, start, count, last_value): | ||||
|  | @ -736,7 +736,7 @@ def __invert__(self): | |||
|         return self.__class__(inverted) | ||||
| 
 | ||||
| 
 | ||||
| class IntFlags(int, Flags): | ||||
| class IntFlag(int, Flag): | ||||
|     """Support for integer-based Flags""" | ||||
| 
 | ||||
|     @classmethod | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| import pydoc | ||||
| import unittest | ||||
| from collections import OrderedDict | ||||
| from enum import Enum, IntEnum, EnumMeta, Flags, IntFlags, unique | ||||
| from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique | ||||
| from io import StringIO | ||||
| from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL | ||||
| from test import support | ||||
|  | @ -33,6 +33,14 @@ class FloatStooges(float, Enum): | |||
| except Exception as exc: | ||||
|     FloatStooges = exc | ||||
| 
 | ||||
| try: | ||||
|     class FlagStooges(Flag): | ||||
|         LARRY = 1 | ||||
|         CURLY = 2 | ||||
|         MOE = 3 | ||||
| except Exception as exc: | ||||
|     FlagStooges = exc | ||||
| 
 | ||||
| # for pickle test and subclass tests | ||||
| try: | ||||
|     class StrEnum(str, Enum): | ||||
|  | @ -1633,13 +1641,13 @@ class Color(Enum): | |||
|                 verde = green | ||||
| 
 | ||||
| 
 | ||||
| class TestFlags(unittest.TestCase): | ||||
| class TestFlag(unittest.TestCase): | ||||
|     """Tests of the Flags.""" | ||||
| 
 | ||||
|     class Perm(Flags): | ||||
|     class Perm(Flag): | ||||
|         R, W, X = 4, 2, 1 | ||||
| 
 | ||||
|     class Open(Flags): | ||||
|     class Open(Flag): | ||||
|         RO = 0 | ||||
|         WO = 1 | ||||
|         RW = 2 | ||||
|  | @ -1760,7 +1768,7 @@ def test_invert(self): | |||
|         self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) | ||||
| 
 | ||||
|     def test_programatic_function_string(self): | ||||
|         Perm = Flags('Perm', 'R W X') | ||||
|         Perm = Flag('Perm', 'R W X') | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -1775,7 +1783,7 @@ def test_programatic_function_string(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_string_with_start(self): | ||||
|         Perm = Flags('Perm', 'R W X', start=8) | ||||
|         Perm = Flag('Perm', 'R W X', start=8) | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -1790,7 +1798,7 @@ def test_programatic_function_string_with_start(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_string_list(self): | ||||
|         Perm = Flags('Perm', ['R', 'W', 'X']) | ||||
|         Perm = Flag('Perm', ['R', 'W', 'X']) | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -1805,7 +1813,7 @@ def test_programatic_function_string_list(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_iterable(self): | ||||
|         Perm = Flags('Perm', (('R', 2), ('W', 8), ('X', 32))) | ||||
|         Perm = Flag('Perm', (('R', 2), ('W', 8), ('X', 32))) | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -1820,7 +1828,7 @@ def test_programatic_function_iterable(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_from_dict(self): | ||||
|         Perm = Flags('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) | ||||
|         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) | ||||
|  | @ -1834,16 +1842,42 @@ def test_programatic_function_from_dict(self): | |||
|             self.assertIn(e, Perm) | ||||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     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) | ||||
| 
 | ||||
| class TestIntFlags(unittest.TestCase): | ||||
| 
 | ||||
|     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) | ||||
| 
 | ||||
| class TestIntFlag(unittest.TestCase): | ||||
|     """Tests of the IntFlags.""" | ||||
| 
 | ||||
|     class Perm(IntFlags): | ||||
|     class Perm(IntFlag): | ||||
|         X = 1 << 0 | ||||
|         W = 1 << 1 | ||||
|         R = 1 << 2 | ||||
| 
 | ||||
|     class Open(IntFlags): | ||||
|     class Open(IntFlag): | ||||
|         RO = 0 | ||||
|         WO = 1 | ||||
|         RW = 2 | ||||
|  | @ -2003,7 +2037,7 @@ def test_invert(self): | |||
|         self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) | ||||
| 
 | ||||
|     def test_programatic_function_string(self): | ||||
|         Perm = IntFlags('Perm', 'R W X') | ||||
|         Perm = IntFlag('Perm', 'R W X') | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -2019,7 +2053,7 @@ def test_programatic_function_string(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_string_with_start(self): | ||||
|         Perm = IntFlags('Perm', 'R W X', start=8) | ||||
|         Perm = IntFlag('Perm', 'R W X', start=8) | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -2035,7 +2069,7 @@ def test_programatic_function_string_with_start(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_string_list(self): | ||||
|         Perm = IntFlags('Perm', ['R', 'W', 'X']) | ||||
|         Perm = IntFlag('Perm', ['R', 'W', 'X']) | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -2051,7 +2085,7 @@ def test_programatic_function_string_list(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_iterable(self): | ||||
|         Perm = IntFlags('Perm', (('R', 2), ('W', 8), ('X', 32))) | ||||
|         Perm = IntFlag('Perm', (('R', 2), ('W', 8), ('X', 32))) | ||||
|         lst = list(Perm) | ||||
|         self.assertEqual(len(lst), len(Perm)) | ||||
|         self.assertEqual(len(Perm), 3, Perm) | ||||
|  | @ -2067,7 +2101,7 @@ def test_programatic_function_iterable(self): | |||
|             self.assertIs(type(e), Perm) | ||||
| 
 | ||||
|     def test_programatic_function_from_dict(self): | ||||
|         Perm = IntFlags('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) | ||||
|         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) | ||||
|  | @ -2083,6 +2117,26 @@ def test_programatic_function_from_dict(self): | |||
|             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) | ||||
| 
 | ||||
| class TestUnique(unittest.TestCase): | ||||
| 
 | ||||
|     def test_unique_clean(self): | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ethan Furman
						Ethan Furman