diff -r a4101218364e Doc/library/enum.rst --- a/Doc/library/enum.rst Mon Aug 15 10:06:16 2016 +0300 +++ b/Doc/library/enum.rst Mon Aug 15 11:50:24 2016 +0300 @@ -23,9 +23,9 @@ by identity, and the enumeration itself 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 three 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 @@ -44,6 +44,13 @@ one decorator, :func:`unique`. .. versionadded:: 3.6 +.. class:: IntFlags + + Base class for creating enumerated constants that are also + subclasses of :class:`int` and are purposed to be used as bit flags. + + .. versionadded:: 3.6 + .. function:: unique Enum class decorator that ensures only one name is bound to any one value. @@ -290,7 +297,7 @@ Enumeration members are compared by iden True Ordered comparisons between enumeration values are *not* supported. Enum -members are not integers (but see `IntEnum`_ below):: +members are not integers (but see `IntEnum`_ and `IntFlags`_ below):: >>> Color.red < Color.blue Traceback (most recent call last): @@ -307,8 +314,8 @@ Equality comparisons are defined though: True Comparisons against non-enumeration values will always compare not equal -(again, :class:`IntEnum` was explicitly designed to behave differently, see -below):: +(again, :class:`IntEnum` and :class:`IntFlags` were explicitly designed to +behave differently, see below):: >>> Color.blue == 2 False @@ -610,6 +617,33 @@ where there's no other choice; for examp with enumerations and backwards compatibility is required with code that still expects integers. +IntFlags +^^^^^^^^ + +A variation of :class:`IntEnum` is provided which is purposed to represent bit +flags. The result of bitwise operations (``|``, ``&``, ``^``, and ``~``) +preserves the type of :class:`IntFlags`. + + >>> from enum import IntFlags + >>> class Perm(IntFlags): + ... read = 1<<0 + ... write = 1<<1 + ... execute = 1<<2 + ... + >>> Perm.read | Perm.write + + >>> (Perm.read | Perm.write) & ~Perm.write + + +Functional interface of :class:`IntFlags` creates flags with values that are +powers of two:: + + >>> from enum import IntFlags + >>> Perm = IntFlags('Perm', 'read write execute') + >>> list(Perm) + [, , ] + + Others ^^^^^^ @@ -649,7 +683,8 @@ 5. :ref:`Formatted string literals 1 or value: + s = '~(%s)' % s + else: + s = '~%s' % s + return s + + def __repr__(self): + if self._name_ is not None: + return '<%s.%s: %r>' % ( + self.__class__.__name__, self._name_, self._value_) + invert, names, value = self._decompose() + if value or not names: + names.append(str(value)) + s = '|'.join('%s' % n for n in names) + if invert: + if len(names) > 1: + s = '~(%s)' % s + else: + s = '~%s' % s + return '<%s: %s = %r>' % (self.__class__.__name__, s, self._value_) + + def __or__(self, other): + if isinstance(other, self.__class__): + other = other._value_ + elif isinstance(other, IntFlags) or not isinstance(other, int): + return NotImplemented + return self.__class__(self._value_ | other) + + def __ror__(self, other): + if isinstance(other, self.__class__): + other = other._value_ + elif isinstance(other, IntFlags) or not isinstance(other, int): + return NotImplemented + return self.__class__(other | self._value_) + + def __and__(self, other): + if isinstance(other, self.__class__): + other = other._value_ + elif isinstance(other, IntFlags) or not isinstance(other, int): + return NotImplemented + return self.__class__(self._value_ & other) + + def __rand__(self, other): + if isinstance(other, self.__class__): + other = other._value_ + elif isinstance(other, IntFlags) or not isinstance(other, int): + return NotImplemented + return self.__class__(other & self._value_) + + def __xor__(self, other): + if isinstance(other, self.__class__): + other = other._value_ + elif isinstance(other, IntFlags) or not isinstance(other, int): + return NotImplemented + return self.__class__(self._value_ ^ other) + + def __rxor__(self, other): + if isinstance(other, self.__class__): + other = other._value_ + elif isinstance(other, IntFlags) or not isinstance(other, int): + return NotImplemented + return self.__class__(other ^ self._value_) + + def __invert__(self): + return self.__class__(~self._value_) diff -r a4101218364e Lib/os.py --- a/Lib/os.py Mon Aug 15 10:06:16 2016 +0300 +++ b/Lib/os.py Mon Aug 15 11:50:24 2016 +0300 @@ -23,6 +23,7 @@ and opendir), and leave all pathname man #' import abc +import enum import sys, errno import stat as st @@ -115,6 +116,23 @@ from os.path import (curdir, pardir, sep del _names +OpenMode = enum.IntFlags('OpenMode', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('O_')}) +globals().update(OpenMode.__members__) +StatFlags = enum.IntFlags('StatFlags', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('ST_')}) +globals().update(StatFlags.__members__) +DLOpenFlags = enum.IntFlags('DLOpenFlags', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('RTLD_')}) +globals().update(DLOpenFlags.__members__) +AccessMode = enum.IntFlags('AccessMode', + [(name, globals()[name]) for name in ('R_OK', 'W_OK', 'X_OK', 'F_OK') + if name in globals()]) +globals().update(AccessMode.__members__) + if _exists("_have_functions"): _globals = globals() diff -r a4101218364e Lib/re.py --- a/Lib/re.py Mon Aug 15 10:06:16 2016 +0300 +++ b/Lib/re.py Mon Aug 15 11:50:24 2016 +0300 @@ -119,6 +119,7 @@ This module also defines an exception 'e """ +import enum import sre_compile import sre_parse try: @@ -138,17 +139,20 @@ except ImportError: __version__ = "2.2.1" # flags -A = ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" -I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case -L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale -U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" -M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline -S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline -X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments +class Flags(enum.IntFlags): + A = ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" + I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case + L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale + U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" + M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline + S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline + X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments -# sre extensions (experimental, don't rely on these) -T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking -DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation + # sre extensions (experimental, don't rely on these) + T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking + DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation + +globals().update(Flags.__members__) # sre exception error = sre_compile.error diff -r a4101218364e Lib/socket.py --- a/Lib/socket.py Mon Aug 15 10:06:16 2016 +0300 +++ b/Lib/socket.py Mon Aug 15 11:50:24 2016 +0300 @@ -50,7 +50,7 @@ import _socket from _socket import * import os, sys, io, selectors -from enum import IntEnum +from enum import IntEnum, IntFlags try: import errno @@ -80,6 +80,17 @@ IntEnum._convert( __name__, lambda C: C.isupper() and C.startswith('SOCK_')) +MsgFlags = IntFlags('MsgFlags', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('MSG_')}) +globals().update(MsgFlags.__members__) + +AddressInfo = IntFlags('AddressInfo', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('AI_')}) +globals().update(AddressInfo.__members__) + + _LOCALHOST = '127.0.0.1' _LOCALHOST_V6 = '::1' diff -r a4101218364e Lib/stat.py --- a/Lib/stat.py Mon Aug 15 10:06:16 2016 +0300 +++ b/Lib/stat.py Mon Aug 15 11:50:24 2016 +0300 @@ -2,6 +2,7 @@ Suggested usage: from stat import * """ +import enum # Indices for stat struct members in the tuple returned by os.stat() @@ -176,3 +177,18 @@ try: from _stat import * except ImportError: pass + +Permissions = enum.IntFlags('Permissions', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('S_') and isinstance(value, int)}) +globals().update(Permissions.__members__) + +FileFlags = enum.IntFlags('FileFlags', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith(('UF_', 'SF_'))}) +globals().update(FileFlags.__members__) + +FileAttributes = enum.IntFlags('FileAttributes', + {name: value for name, value in globals().items() + if name.isupper() and name.startswith('FILE_ATTRIBUTE_')}) +globals().update(FileAttributes.__members__) diff -r a4101218364e Lib/test/test_enum.py --- a/Lib/test/test_enum.py Mon Aug 15 10:06:16 2016 +0300 +++ b/Lib/test/test_enum.py Mon Aug 15 11:50:24 2016 +0300 @@ -3,7 +3,7 @@ import inspect import pydoc import unittest from collections import OrderedDict -from enum import EnumMeta, Enum, IntEnum, AutoEnum, unique +from enum import EnumMeta, Enum, IntEnum, AutoEnum, unique, IntFlags from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -2126,5 +2126,254 @@ class TestIntEnumConvert(unittest.TestCa [], msg='Names other than CONVERT_TEST_* found.') + +class TestIntFlags(unittest.TestCase): + """Tests of the IntFlags.""" + + class Perm(IntFlags): + R = 1 << 0 + W = 1 << 1 + X = 1 << 2 + + class Open(IntFlags): + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + + 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|Perm.W') + self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|Perm.W|Perm.X') + self.assertEqual(str(Perm.R | 8), 'Perm.R|8') + self.assertEqual(str(Perm(0)), '0') + self.assertEqual(str(Perm(8)), '8') + 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|Perm.W)') + self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), '~(Perm.R|Perm.W|Perm.X)') + self.assertEqual(str(~(Perm.R | 8)), '~(Perm.R|8)') + self.assertEqual(str(Perm(~0)), '~0') + self.assertEqual(str(Perm(~8)), '~8') + + 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|Open.CE') + self.assertEqual(str(Open(4)), '4') + 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|Open.CE)') + self.assertEqual(str(Open(~4)), '~4') + + def test_repr(self): + Perm = self.Perm + self.assertEqual(repr(Perm.R), '') + self.assertEqual(repr(Perm.W), '') + self.assertEqual(repr(Perm.X), '') + self.assertEqual(repr(Perm.R | Perm.W), '') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') + self.assertEqual(repr(Perm.R | 8), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(Perm(8)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(~(Perm.R | 8)), '') + self.assertEqual(repr(Perm(~0)), '') + self.assertEqual(repr(Perm(~8)), '') + + Open = self.Open + self.assertEqual(repr(Open.RO), '') + self.assertEqual(repr(Open.WO), '') + self.assertEqual(repr(Open.AC), '') + self.assertEqual(repr(Open.RO | Open.CE), '') + self.assertEqual(repr(Open.WO | Open.CE), '') + self.assertEqual(repr(Open(4)), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + self.assertEqual(repr(Open(~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) + 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 & 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.assertEqual((~i).value, ~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_programatic_function_string(self): + Perm = IntFlags('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<