issue23673
add private method to enum to support replacing global constants with Enum members: - search for candidate constants via supplied filter - create new enum class and members - insert enum class and replace constants with members via supplied module name - replace __reduce_ex__ with function that returns member name, so previous Python versions can unpickle modify IntEnum classes to use new method
This commit is contained in:
parent
8eef6a9ad0
commit
24e837f231
26
Lib/enum.py
26
Lib/enum.py
|
@ -519,11 +519,37 @@ class Enum(metaclass=EnumMeta):
|
||||||
"""The value of the Enum member."""
|
"""The value of the Enum member."""
|
||||||
return self._value_
|
return self._value_
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _convert(cls, name, module, filter, source=None):
|
||||||
|
"""
|
||||||
|
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 = {name: value for name, value in source.items()
|
||||||
|
if filter(name)}
|
||||||
|
cls = cls(name, members, module=module)
|
||||||
|
cls.__reduce_ex__ = _reduce_ex_by_name
|
||||||
|
module_globals.update(cls.__members__)
|
||||||
|
module_globals[name] = cls
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
class IntEnum(int, Enum):
|
class IntEnum(int, Enum):
|
||||||
"""Enum where members are also (and must be) ints"""
|
"""Enum where members are also (and must be) ints"""
|
||||||
|
|
||||||
|
|
||||||
|
def _reduce_ex_by_name(self, proto):
|
||||||
|
return self.name
|
||||||
|
|
||||||
def unique(enumeration):
|
def unique(enumeration):
|
||||||
"""Class decorator for enumerations ensuring unique member values."""
|
"""Class decorator for enumerations ensuring unique member values."""
|
||||||
duplicates = []
|
duplicates = []
|
||||||
|
|
|
@ -5,27 +5,21 @@ from enum import IntEnum as _IntEnum
|
||||||
|
|
||||||
_globals = globals()
|
_globals = globals()
|
||||||
|
|
||||||
Signals = _IntEnum(
|
_IntEnum._convert(
|
||||||
'Signals',
|
'Signals', __name__,
|
||||||
{name: value for name, value in _globals.items()
|
lambda name:
|
||||||
if name.isupper()
|
name.isupper()
|
||||||
and (name.startswith('SIG') and not name.startswith('SIG_'))
|
and (name.startswith('SIG') and not name.startswith('SIG_'))
|
||||||
or name.startswith('CTRL_')})
|
or name.startswith('CTRL_'))
|
||||||
|
|
||||||
class Handlers(_IntEnum):
|
_IntEnum._convert(
|
||||||
SIG_DFL = _signal.SIG_DFL
|
'Handlers', __name__,
|
||||||
SIG_IGN = _signal.SIG_IGN
|
lambda name: name in ('SIG_DFL', 'SIG_IGN'))
|
||||||
|
|
||||||
_globals.update(Signals.__members__)
|
|
||||||
_globals.update(Handlers.__members__)
|
|
||||||
|
|
||||||
if 'pthread_sigmask' in _globals:
|
if 'pthread_sigmask' in _globals:
|
||||||
class Sigmasks(_IntEnum):
|
_IntEnum._convert(
|
||||||
SIG_BLOCK = _signal.SIG_BLOCK
|
'Sigmasks', __name__,
|
||||||
SIG_UNBLOCK = _signal.SIG_UNBLOCK
|
lambda name: name in ('SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK'))
|
||||||
SIG_SETMASK = _signal.SIG_SETMASK
|
|
||||||
|
|
||||||
_globals.update(Sigmasks.__members__)
|
|
||||||
|
|
||||||
|
|
||||||
def _int_to_enum(value, enum_klass):
|
def _int_to_enum(value, enum_klass):
|
||||||
|
|
|
@ -69,16 +69,16 @@ __all__.extend(os._get_exports_list(_socket))
|
||||||
# Note that _socket only knows about the integer values. The public interface
|
# Note that _socket only knows about the integer values. The public interface
|
||||||
# in this module understands the enums and translates them back from integers
|
# in this module understands the enums and translates them back from integers
|
||||||
# where needed (e.g. .family property of a socket object).
|
# where needed (e.g. .family property of a socket object).
|
||||||
AddressFamily = IntEnum('AddressFamily',
|
|
||||||
{name: value for name, value in globals().items()
|
|
||||||
if name.isupper() and name.startswith('AF_')})
|
|
||||||
globals().update(AddressFamily.__members__)
|
|
||||||
|
|
||||||
SocketKind = IntEnum('SocketKind',
|
IntEnum._convert(
|
||||||
{name: value for name, value in globals().items()
|
'AddressFamily',
|
||||||
if name.isupper() and name.startswith('SOCK_')})
|
__name__,
|
||||||
globals().update(SocketKind.__members__)
|
lambda C: C.isupper() and C.startswith('AF_'))
|
||||||
|
|
||||||
|
IntEnum._convert(
|
||||||
|
'SocketKind',
|
||||||
|
__name__,
|
||||||
|
lambda C: C.isupper() and C.startswith('SOCK_'))
|
||||||
|
|
||||||
_LOCALHOST = '127.0.0.1'
|
_LOCALHOST = '127.0.0.1'
|
||||||
_LOCALHOST_V6 = '::1'
|
_LOCALHOST_V6 = '::1'
|
||||||
|
|
|
@ -126,10 +126,10 @@ from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN
|
||||||
|
|
||||||
from _ssl import _OPENSSL_API_VERSION
|
from _ssl import _OPENSSL_API_VERSION
|
||||||
|
|
||||||
_SSLMethod = _IntEnum('_SSLMethod',
|
_IntEnum._convert(
|
||||||
{name: value for name, value in vars(_ssl).items()
|
'_SSLMethod', __name__,
|
||||||
if name.startswith('PROTOCOL_')})
|
lambda name: name.startswith('PROTOCOL_'),
|
||||||
globals().update(_SSLMethod.__members__)
|
source=_ssl)
|
||||||
|
|
||||||
_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()}
|
_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()}
|
||||||
|
|
||||||
|
|
|
@ -581,6 +581,14 @@ class TestEnum(unittest.TestCase):
|
||||||
test_pickle_dump_load(self.assertIs, self.NestedEnum.twigs,
|
test_pickle_dump_load(self.assertIs, self.NestedEnum.twigs,
|
||||||
protocol=(4, HIGHEST_PROTOCOL))
|
protocol=(4, HIGHEST_PROTOCOL))
|
||||||
|
|
||||||
|
def test_pickle_by_name(self):
|
||||||
|
class ReplaceGlobalInt(IntEnum):
|
||||||
|
ONE = 1
|
||||||
|
TWO = 2
|
||||||
|
ReplaceGlobalInt.__reduce_ex__ = enum._reduce_ex_by_name
|
||||||
|
for proto in range(HIGHEST_PROTOCOL):
|
||||||
|
self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO')
|
||||||
|
|
||||||
def test_exploding_pickle(self):
|
def test_exploding_pickle(self):
|
||||||
BadPickle = Enum(
|
BadPickle = Enum(
|
||||||
'BadPickle', 'dill sweet bread-n-butter', module=__name__)
|
'BadPickle', 'dill sweet bread-n-butter', module=__name__)
|
||||||
|
|
|
@ -1377,6 +1377,11 @@ class GeneralModuleTests(unittest.TestCase):
|
||||||
with sock:
|
with sock:
|
||||||
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
|
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||||
self.assertRaises(TypeError, pickle.dumps, sock, protocol)
|
self.assertRaises(TypeError, pickle.dumps, sock, protocol)
|
||||||
|
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||||
|
family = pickle.loads(pickle.dumps(socket.AF_INET, protocol))
|
||||||
|
self.assertEqual(family, socket.AF_INET)
|
||||||
|
type = pickle.loads(pickle.dumps(socket.SOCK_STREAM, protocol))
|
||||||
|
self.assertEqual(type, socket.SOCK_STREAM)
|
||||||
|
|
||||||
def test_listen_backlog(self):
|
def test_listen_backlog(self):
|
||||||
for backlog in 0, -1:
|
for backlog in 0, -1:
|
||||||
|
|
Loading…
Reference in New Issue