diff -r 2545bfe0d273 Lib/enum.py --- a/Lib/enum.py Wed Mar 11 08:43:12 2015 -0700 +++ b/Lib/enum.py Mon Mar 16 10:55:59 2015 -0700 @@ -512,25 +512,51 @@ class Enum(metaclass=EnumMeta): @DynamicClassAttribute def name(self): """The name of the Enum member.""" return self._name_ @DynamicClassAttribute def value(self): """The value of the Enum member.""" 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): """Enum where members are also (and must be) ints""" +def _reduce_ex_by_name(self, proto): + return self.name + def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" duplicates = [] for name, member in enumeration.__members__.items(): if name != member.name: duplicates.append((name, member.name)) if duplicates: alias_details = ', '.join( ["%s -> %s" % (alias, name) for (alias, name) in duplicates]) raise ValueError('duplicate values found in %r: %s' % diff -r 2545bfe0d273 Lib/http/__init__.py --- a/Lib/http/__init__.py Wed Mar 11 08:43:12 2015 -0700 +++ b/Lib/http/__init__.py Mon Mar 16 10:55:59 2015 -0700 @@ -1,15 +1,15 @@ -from enum import IntEnum +import enum __all__ = ['HTTPStatus'] -class HTTPStatus(IntEnum): +class HTTPStatus(enum.IntEnum): """HTTP status codes and reason phrases Status codes from the following RFCs are all observed: * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616 * RFC 6585: Additional HTTP Status Codes * RFC 3229: Delta encoding in HTTP * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518 * RFC 5842: Binding Extensions to WebDAV * RFC 7238: Permanent Redirect @@ -17,20 +17,23 @@ class HTTPStatus(IntEnum): * RFC 2774: An HTTP Extension Framework """ def __new__(cls, value, phrase, description=''): obj = int.__new__(cls, value) obj._value_ = value obj.phrase = phrase obj.description = description return obj + # make sure previous python versions can unpickle this class + __reduce_ex__ = enum._reduce_ex_by_name + # informational CONTINUE = 100, 'Continue', 'Request received, please continue' SWITCHING_PROTOCOLS = (101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header') PROCESSING = 102, 'Processing' # success OK = 200, 'OK', 'Request fulfilled, document follows' CREATED = 201, 'Created', 'Document created, URL follows' ACCEPTED = (202, 'Accepted', diff -r 2545bfe0d273 Lib/signal.py --- a/Lib/signal.py Wed Mar 11 08:43:12 2015 -0700 +++ b/Lib/signal.py Mon Mar 16 10:55:59 2015 -0700 @@ -1,38 +1,32 @@ import _signal from _signal import * from functools import wraps as _wraps from enum import IntEnum as _IntEnum _globals = globals() -Signals = _IntEnum( - 'Signals', - {name: value for name, value in _globals.items() - if name.isupper() - and (name.startswith('SIG') and not name.startswith('SIG_')) - or name.startswith('CTRL_')}) +_IntEnum._convert( + 'Signals', __name__, + lambda name: + name.isupper() + and (name.startswith('SIG') and not name.startswith('SIG_')) + or name.startswith('CTRL_')) -class Handlers(_IntEnum): - SIG_DFL = _signal.SIG_DFL - SIG_IGN = _signal.SIG_IGN - -_globals.update(Signals.__members__) -_globals.update(Handlers.__members__) +_IntEnum._convert( + 'Handlers', __name__, + lambda name: name in ('SIG_DFL', 'SIG_IGN')) if 'pthread_sigmask' in _globals: - class Sigmasks(_IntEnum): - SIG_BLOCK = _signal.SIG_BLOCK - SIG_UNBLOCK = _signal.SIG_UNBLOCK - SIG_SETMASK = _signal.SIG_SETMASK - - _globals.update(Sigmasks.__members__) + _IntEnum._convert( + 'Sigmasks', __name__, + lambda name: name in ('SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK')) def _int_to_enum(value, enum_klass): """Convert a numeric value to an IntEnum member. If it's not a known member, return the numeric value itself. """ try: return enum_klass(value) except ValueError: return value diff -r 2545bfe0d273 Lib/socket.py --- a/Lib/socket.py Wed Mar 11 08:43:12 2015 -0700 +++ b/Lib/socket.py Mon Mar 16 10:55:59 2015 -0700 @@ -62,30 +62,30 @@ EWOULDBLOCK = getattr(errno, 'EWOULDBLOC __all__ = ["fromfd", "getfqdn", "create_connection", "AddressFamily", "SocketKind"] __all__.extend(os._get_exports_list(_socket)) # Set up the socket.AF_* socket.SOCK_* constants as members of IntEnums for # nicer string representations. # Note that _socket only knows about the integer values. The public interface # in this module understands the enums and translates them back from integers # 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', - {name: value for name, value in globals().items() - if name.isupper() and name.startswith('SOCK_')}) -globals().update(SocketKind.__members__) +IntEnum._convert( + 'AddressFamily', + __name__, + 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_V6 = '::1' def _intenum_converter(value, enum_klass): """Convert a numeric family value to an IntEnum member. If it's not a known member, return the numeric value itself. """ diff -r 2545bfe0d273 Lib/ssl.py --- a/Lib/ssl.py Wed Mar 11 08:43:12 2015 -0700 +++ b/Lib/ssl.py Mon Mar 16 10:55:59 2015 -0700 @@ -119,24 +119,24 @@ def _import_symbols(prefix): _import_symbols('OP_') _import_symbols('ALERT_DESCRIPTION_') _import_symbols('SSL_ERROR_') _import_symbols('VERIFY_') from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN from _ssl import _OPENSSL_API_VERSION -_SSLMethod = _IntEnum('_SSLMethod', - {name: value for name, value in vars(_ssl).items() - if name.startswith('PROTOCOL_')}) -globals().update(_SSLMethod.__members__) +_IntEnum._convert( + '_SSLMethod', __name__, + lambda name: name.startswith('PROTOCOL_'), + source=_ssl) _PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()} try: _SSLv2_IF_EXISTS = PROTOCOL_SSLv2 except NameError: _SSLv2_IF_EXISTS = None if sys.platform == "win32": from _ssl import enum_certificates, enum_crls diff -r 2545bfe0d273 Lib/test/test_enum.py --- a/Lib/test/test_enum.py Wed Mar 11 08:43:12 2015 -0700 +++ b/Lib/test/test_enum.py Mon Mar 16 10:55:59 2015 -0700 @@ -574,20 +574,28 @@ class TestEnum(unittest.TestCase): 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.assertIs, self.NestedEnum.twigs, 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): BadPickle = Enum( 'BadPickle', 'dill sweet bread-n-butter', module=__name__) globals()['BadPickle'] = BadPickle # now break BadPickle to test exception raising enum._make_class_unpicklable(BadPickle) test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill) test_pickle_exception(self.assertRaises, PicklingError, BadPickle) def test_string_enum(self): diff -r 2545bfe0d273 Lib/test/test_socket.py --- a/Lib/test/test_socket.py Wed Mar 11 08:43:12 2015 -0700 +++ b/Lib/test/test_socket.py Mon Mar 16 10:55:59 2015 -0700 @@ -1370,20 +1370,25 @@ class GeneralModuleTests(unittest.TestCa fp.close() self.assertRaises(ValueError, fp.readable) self.assertRaises(ValueError, fp.writable) self.assertRaises(ValueError, fp.seekable) def test_pickle(self): sock = socket.socket() with sock: for protocol in range(pickle.HIGHEST_PROTOCOL + 1): 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): for backlog in 0, -1: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv: srv.bind((HOST, 0)) srv.listen(backlog) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv: srv.bind((HOST, 0)) srv.listen()