bpo-40066: Enum: modify repr() and str() (GH-22392)

* Enum: streamline repr() and str(); improve docs

- repr() is now ``enum_class.member_name``
- stdlib global enums are ``module_name.member_name``
- str() is now ``member_name``
- add HOW-TO section for ``Enum``
- change main documentation to be an API reference
This commit is contained in:
Ethan Furman 2021-03-30 21:17:26 -07:00 committed by GitHub
parent 51a85ddce8
commit b775106d94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 2225 additions and 1491 deletions

1416
Doc/howto/enum.rst Normal file

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,7 @@ Currently, the HOWTOs are:
cporting.rst cporting.rst
curses.rst curses.rst
descriptor.rst descriptor.rst
enum.rst
functional.rst functional.rst
logging.rst logging.rst
logging-cookbook.rst logging-cookbook.rst

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,7 @@ associated messages through the :class:`http.HTTPStatus` enum:
>>> from http import HTTPStatus >>> from http import HTTPStatus
>>> HTTPStatus.OK >>> HTTPStatus.OK
<HTTPStatus.OK: 200> HTTPStatus.OK
>>> HTTPStatus.OK == 200 >>> HTTPStatus.OK == 200
True True
>>> HTTPStatus.OK.value >>> HTTPStatus.OK.value
@ -45,7 +45,7 @@ associated messages through the :class:`http.HTTPStatus` enum:
>>> HTTPStatus.OK.description >>> HTTPStatus.OK.description
'Request fulfilled, document follows' 'Request fulfilled, document follows'
>>> list(HTTPStatus) >>> list(HTTPStatus)
[<HTTPStatus.CONTINUE: 100>, <HTTPStatus.SWITCHING_PROTOCOLS: 101>, ...] [HTTPStatus.CONTINUE, HTTPStatus.SWITCHING_PROTOCOLS, ...]
.. _http-status-codes: .. _http-status-codes:

View file

@ -785,9 +785,9 @@ The :mod:`socket` module also offers various network-related services:
system if IPv6 isn't enabled):: system if IPv6 isn't enabled)::
>>> socket.getaddrinfo("example.org", 80, proto=socket.IPPROTO_TCP) >>> socket.getaddrinfo("example.org", 80, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET6: 10>, <SocketType.SOCK_STREAM: 1>, [(socket.AF_INET6, socket.SOCK_STREAM,
6, '', ('2606:2800:220:1:248:1893:25c8:1946', 80, 0, 0)), 6, '', ('2606:2800:220:1:248:1893:25c8:1946', 80, 0, 0)),
(<AddressFamily.AF_INET: 2>, <SocketType.SOCK_STREAM: 1>, (socket.AF_INET, socket.SOCK_STREAM,
6, '', ('93.184.216.34', 80))] 6, '', ('93.184.216.34', 80))]
.. versionchanged:: 3.2 .. versionchanged:: 3.2

View file

@ -2062,7 +2062,7 @@ to speed up repeated connections from the same clients.
:attr:`SSLContext.verify_flags` returns :class:`VerifyFlags` flags: :attr:`SSLContext.verify_flags` returns :class:`VerifyFlags` flags:
>>> ssl.create_default_context().verify_flags # doctest: +SKIP >>> ssl.create_default_context().verify_flags # doctest: +SKIP
<VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768> ssl.VERIFY_X509_TRUSTED_FIRST
.. attribute:: SSLContext.verify_mode .. attribute:: SSLContext.verify_mode
@ -2074,7 +2074,7 @@ to speed up repeated connections from the same clients.
:attr:`SSLContext.verify_mode` returns :class:`VerifyMode` enum: :attr:`SSLContext.verify_mode` returns :class:`VerifyMode` enum:
>>> ssl.create_default_context().verify_mode >>> ssl.create_default_context().verify_mode
<VerifyMode.CERT_REQUIRED: 2> ssl.CERT_REQUIRED
.. index:: single: certificates .. index:: single: certificates

View file

@ -716,6 +716,14 @@ encodings
:func:`encodings.normalize_encoding` now ignores non-ASCII characters. :func:`encodings.normalize_encoding` now ignores non-ASCII characters.
(Contributed by Hai Shi in :issue:`39337`.) (Contributed by Hai Shi in :issue:`39337`.)
enum
----
:class:`Enum` :func:`__repr__` now returns ``enum_name.member_name`` and
:func:`__str__` now returns ``member_name``. Stdlib enums available as
module constants have a :func:`repr` of ``module_name.member_name``.
(Contributed by Ethan Furman in :issue:`40066`.)
gc gc
-- --

View file

@ -4,17 +4,18 @@ from builtins import property as _bltin_property, bin as _bltin_bin
__all__ = [ __all__ = [
'EnumMeta', 'EnumType', 'EnumMeta',
'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag',
'auto', 'unique', 'auto', 'unique',
'property', 'property',
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP', 'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
'global_flag_repr', 'global_enum_repr', 'global_enum',
] ]
# Dummy value for Enum and Flag as there are explicit checks for them # Dummy value for Enum and Flag as there are explicit checks for them
# before they have been created. # before they have been created.
# This is also why there are checks in EnumMeta like `if Enum is not None` # This is also why there are checks in EnumType like `if Enum is not None`
Enum = Flag = EJECT = None Enum = Flag = EJECT = None
def _is_descriptor(obj): def _is_descriptor(obj):
@ -285,7 +286,7 @@ class _EnumDict(dict):
""" """
Track enum member order and ensure member names are not reused. Track enum member order and ensure member names are not reused.
EnumMeta will use the names found in self._member_names as the EnumType will use the names found in self._member_names as the
enumeration member names. enumeration member names.
""" """
def __init__(self): def __init__(self):
@ -321,7 +322,8 @@ class _EnumDict(dict):
# check if members already defined as auto() # check if members already defined as auto()
if self._auto_called: if self._auto_called:
raise TypeError("_generate_next_value_ must be defined before members") raise TypeError("_generate_next_value_ must be defined before members")
setattr(self, '_generate_next_value', value) _gnv = value.__func__ if isinstance(value, staticmethod) else value
setattr(self, '_generate_next_value', _gnv)
elif key == '_ignore_': elif key == '_ignore_':
if isinstance(value, str): if isinstance(value, str):
value = value.replace(',',' ').split() value = value.replace(',',' ').split()
@ -368,7 +370,7 @@ class _EnumDict(dict):
self[name] = value self[name] = value
class EnumMeta(type): class EnumType(type):
""" """
Metaclass for Enum Metaclass for Enum
""" """
@ -756,9 +758,9 @@ class EnumMeta(type):
# module; # module;
# also, replace the __reduce_ex__ method so unpickling works in # also, replace the __reduce_ex__ method so unpickling works in
# previous Python versions # previous Python versions
module_globals = vars(sys.modules[module]) module_globals = sys.modules[module].__dict__
if source: if source:
source = vars(source) source = source.__dict__
else: else:
source = module_globals source = module_globals
# _value2member_map_ is populated in the same order every time # _value2member_map_ is populated in the same order every time
@ -776,7 +778,7 @@ class EnumMeta(type):
members.sort(key=lambda t: t[0]) members.sort(key=lambda t: t[0])
cls = cls(name, members, module=module, boundary=boundary or KEEP) cls = cls(name, members, module=module, boundary=boundary or KEEP)
cls.__reduce_ex__ = _reduce_ex_by_name cls.__reduce_ex__ = _reduce_ex_by_name
module_globals.update(cls.__members__) global_enum(cls)
module_globals[name] = cls module_globals[name] = cls
return cls return cls
@ -881,9 +883,10 @@ class EnumMeta(type):
else: else:
use_args = True use_args = True
return __new__, save_new, use_args return __new__, save_new, use_args
EnumMeta = EnumType
class Enum(metaclass=EnumMeta): class Enum(metaclass=EnumType):
""" """
Generic enumeration. Generic enumeration.
@ -958,11 +961,10 @@ class Enum(metaclass=EnumMeta):
return None return None
def __repr__(self): def __repr__(self):
return "<%s.%s: %r>" % ( return "%s.%s" % ( self.__class__.__name__, self._name_)
self.__class__.__name__, self._name_, self._value_)
def __str__(self): def __str__(self):
return "%s.%s" % (self.__class__.__name__, self._name_) return "%s" % (self._name_, )
def __dir__(self): def __dir__(self):
""" """
@ -1220,19 +1222,28 @@ class Flag(Enum, boundary=STRICT):
return self._value_.bit_count() return self._value_.bit_count()
def __repr__(self): def __repr__(self):
cls = self.__class__ cls_name = self.__class__.__name__
if self._name_ is not None: if self._name_ is None:
return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) return "0x%x" % (self._value_, )
if _is_single_bit(self._value_):
return '%s.%s' % (cls_name, self._name_)
if self._boundary_ is not FlagBoundary.KEEP:
return '%s.' % cls_name + ('|%s.' % cls_name).join(self.name.split('|'))
else: else:
# only zero is unnamed by default name = []
return '<%s: %r>' % (cls.__name__, self._value_) for n in self._name_.split('|'):
if n.startswith('0'):
name.append(n)
else:
name.append('%s.%s' % (cls_name, n))
return '|'.join(name)
def __str__(self): def __str__(self):
cls = self.__class__ cls = self.__class__
if self._name_ is not None: if self._name_ is None:
return '%s.%s' % (cls.__name__, self._name_) return '%s(%x)' % (cls.__name__, self._value_)
else: else:
return '%s(%s)' % (cls.__name__, self._value_) return self._name_
def __bool__(self): def __bool__(self):
return bool(self._value_) return bool(self._value_)
@ -1329,3 +1340,38 @@ def _power_of_two(value):
if value < 1: if value < 1:
return False return False
return value == 2 ** _high_bit(value) return value == 2 ** _high_bit(value)
def global_enum_repr(self):
return '%s.%s' % (self.__class__.__module__, self._name_)
def global_flag_repr(self):
module = self.__class__.__module__
cls_name = self.__class__.__name__
if self._name_ is None:
return "%x" % (module, cls_name, self._value_)
if _is_single_bit(self):
return '%s.%s' % (module, self._name_)
if self._boundary_ is not FlagBoundary.KEEP:
return module + module.join(self.name.split('|'))
else:
name = []
for n in self._name_.split('|'):
if n.startswith('0'):
name.append(n)
else:
name.append('%s.%s' % (module, n))
return '|'.join(name)
def global_enum(cls):
"""
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
sys.modules[cls.__module__].__dict__.update(cls.__members__)
return cls

View file

@ -2455,9 +2455,6 @@ class _ParameterKind(enum.IntEnum):
KEYWORD_ONLY = 3 KEYWORD_ONLY = 3
VAR_KEYWORD = 4 VAR_KEYWORD = 4
def __str__(self):
return self._name_
@property @property
def description(self): def description(self):
return _PARAM_NAME_MAPPING[self] return _PARAM_NAME_MAPPING[self]

View file

@ -61,8 +61,7 @@ import struct
from xml.parsers.expat import ParserCreate from xml.parsers.expat import ParserCreate
PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__) PlistFormat = enum.global_enum(enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__))
globals().update(PlistFormat.__members__)
class UID: class UID:

View file

@ -142,6 +142,7 @@ __all__ = [
__version__ = "2.2.1" __version__ = "2.2.1"
@enum.global_enum
class RegexFlag(enum.IntFlag, boundary=enum.KEEP): class RegexFlag(enum.IntFlag, boundary=enum.KEEP):
ASCII = A = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" ASCII = A = sre_compile.SRE_FLAG_ASCII # assume ascii "locale"
IGNORECASE = I = sre_compile.SRE_FLAG_IGNORECASE # ignore case IGNORECASE = I = sre_compile.SRE_FLAG_IGNORECASE # ignore case
@ -154,22 +155,6 @@ class RegexFlag(enum.IntFlag, boundary=enum.KEEP):
TEMPLATE = T = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking TEMPLATE = T = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking
DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation
def __repr__(self):
res = ''
if self._name_:
member_names = self._name_.split('|')
constant = None
if member_names[-1].startswith('0x'):
constant = member_names.pop()
res = 're.' + '|re.'.join(member_names)
if constant:
res += '|%s' % constant
return res
__str__ = object.__str__
globals().update(RegexFlag.__members__)
# sre exception # sre exception
error = sre_compile.error error = sre_compile.error

View file

@ -7,7 +7,7 @@ import sys
import unittest import unittest
import threading import threading
from collections import OrderedDict from collections import OrderedDict
from enum import Enum, IntEnum, StrEnum, EnumMeta, Flag, IntFlag, unique, auto from enum import Enum, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto
from enum import STRICT, CONFORM, EJECT, KEEP from enum import STRICT, CONFORM, EJECT, KEEP
from io import StringIO from io import StringIO
from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL
@ -262,11 +262,8 @@ class TestEnum(unittest.TestCase):
self.assertIn(e, Season) self.assertIn(e, Season)
self.assertIs(type(e), Season) self.assertIs(type(e), Season)
self.assertIsInstance(e, Season) self.assertIsInstance(e, Season)
self.assertEqual(str(e), 'Season.' + season) self.assertEqual(str(e), season)
self.assertEqual( self.assertEqual(repr(e), 'Season.{0}'.format(season))
repr(e),
'<Season.{0}: {1}>'.format(season, i),
)
def test_value_name(self): def test_value_name(self):
Season = self.Season Season = self.Season
@ -440,7 +437,7 @@ class TestEnum(unittest.TestCase):
def test_reserved__sunder_(self): def test_reserved__sunder_(self):
with self.assertRaisesRegex( with self.assertRaisesRegex(
ValueError, ValueError,
"_sunder_ names, such as '_bad_', are reserved", '_sunder_ names, such as ._bad_., are reserved',
): ):
class Bad(Enum): class Bad(Enum):
_bad_ = 1 _bad_ = 1
@ -488,7 +485,7 @@ class TestEnum(unittest.TestCase):
two = 2.0 two = 2.0
def __format__(self, spec): def __format__(self, spec):
return 'Format!!' return 'Format!!'
self.assertEqual(str(EnumWithFormatOverride.one), 'EnumWithFormatOverride.one') self.assertEqual(str(EnumWithFormatOverride.one), 'one')
self.assertEqual('{}'.format(EnumWithFormatOverride.one), 'Format!!') self.assertEqual('{}'.format(EnumWithFormatOverride.one), 'Format!!')
def test_str_and_format_override_enum(self): def test_str_and_format_override_enum(self):
@ -528,7 +525,7 @@ class TestEnum(unittest.TestCase):
two = 2.0 two = 2.0
def __format__(self, spec): def __format__(self, spec):
return 'TestFloat success!' return 'TestFloat success!'
self.assertEqual(str(TestFloat.one), 'TestFloat.one') self.assertEqual(str(TestFloat.one), 'one')
self.assertEqual('{}'.format(TestFloat.one), 'TestFloat success!') self.assertEqual('{}'.format(TestFloat.one), 'TestFloat success!')
def assertFormatIsValue(self, spec, member): def assertFormatIsValue(self, spec, member):
@ -614,6 +611,8 @@ class TestEnum(unittest.TestCase):
A = 1 A = 1
B = 2 B = 2
C = 3 C = 3
def __repr__(self):
return '<%s.%s: %r>' % (self.__class__.__name__, self._name_, self._value_)
self.assertEqual(repr(MyEnum.A), '<MyEnum.A: 0x1>') self.assertEqual(repr(MyEnum.A), '<MyEnum.A: 0x1>')
def test_too_many_data_types(self): def test_too_many_data_types(self):
@ -1959,7 +1958,7 @@ class TestEnum(unittest.TestCase):
self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 3) self.assertEqual(Color.BLUE.value, 3)
self.assertEqual(Color.MAX, 3) self.assertEqual(Color.MAX, 3)
self.assertEqual(str(Color.BLUE), 'Color.BLUE') self.assertEqual(str(Color.BLUE), 'BLUE')
class Color(MaxMixin, StrMixin, Enum): class Color(MaxMixin, StrMixin, Enum):
RED = auto() RED = auto()
GREEN = auto() GREEN = auto()
@ -2330,64 +2329,62 @@ class TestFlag(unittest.TestCase):
def test_str(self): def test_str(self):
Perm = self.Perm Perm = self.Perm
self.assertEqual(str(Perm.R), 'Perm.R') self.assertEqual(str(Perm.R), 'R')
self.assertEqual(str(Perm.W), 'Perm.W') self.assertEqual(str(Perm.W), 'W')
self.assertEqual(str(Perm.X), 'Perm.X') self.assertEqual(str(Perm.X), 'X')
self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W') self.assertEqual(str(Perm.R | Perm.W), 'R|W')
self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X') self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'R|W|X')
self.assertEqual(str(Perm(0)), 'Perm(0)') self.assertEqual(str(Perm(0)), 'Perm(0)')
self.assertEqual(str(~Perm.R), 'Perm.W|X') self.assertEqual(str(~Perm.R), 'W|X')
self.assertEqual(str(~Perm.W), 'Perm.R|X') self.assertEqual(str(~Perm.W), 'R|X')
self.assertEqual(str(~Perm.X), 'Perm.R|W') self.assertEqual(str(~Perm.X), 'R|W')
self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X') self.assertEqual(str(~(Perm.R | Perm.W)), 'X')
self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)') self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)')
self.assertEqual(str(Perm(~0)), 'Perm.R|W|X') self.assertEqual(str(Perm(~0)), 'R|W|X')
Open = self.Open Open = self.Open
self.assertEqual(str(Open.RO), 'Open.RO') self.assertEqual(str(Open.RO), 'RO')
self.assertEqual(str(Open.WO), 'Open.WO') self.assertEqual(str(Open.WO), 'WO')
self.assertEqual(str(Open.AC), 'Open.AC') self.assertEqual(str(Open.AC), 'AC')
self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') self.assertEqual(str(Open.RO | Open.CE), 'CE')
self.assertEqual(str(Open.WO | Open.CE), 'Open.WO|CE') self.assertEqual(str(Open.WO | Open.CE), 'WO|CE')
self.assertEqual(str(~Open.RO), 'Open.WO|RW|CE') self.assertEqual(str(~Open.RO), 'WO|RW|CE')
self.assertEqual(str(~Open.WO), 'Open.RW|CE') self.assertEqual(str(~Open.WO), 'RW|CE')
self.assertEqual(str(~Open.AC), 'Open.CE') self.assertEqual(str(~Open.AC), 'CE')
self.assertEqual(str(~Open.CE), 'Open.AC') self.assertEqual(str(~(Open.RO | Open.CE)), 'AC')
self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC') self.assertEqual(str(~(Open.WO | Open.CE)), 'RW')
self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW')
def test_repr(self): def test_repr(self):
Perm = self.Perm Perm = self.Perm
self.assertEqual(repr(Perm.R), '<Perm.R: 4>') self.assertEqual(repr(Perm.R), 'Perm.R')
self.assertEqual(repr(Perm.W), '<Perm.W: 2>') self.assertEqual(repr(Perm.W), 'Perm.W')
self.assertEqual(repr(Perm.X), '<Perm.X: 1>') self.assertEqual(repr(Perm.X), 'Perm.X')
self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>') self.assertEqual(repr(Perm.R | Perm.W), 'Perm.R|Perm.W')
self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>') self.assertEqual(repr(Perm.R | Perm.W | Perm.X), 'Perm.R|Perm.W|Perm.X')
self.assertEqual(repr(Perm(0)), '<Perm: 0>') self.assertEqual(repr(Perm(0)), '0x0')
self.assertEqual(repr(~Perm.R), '<Perm.W|X: 3>') self.assertEqual(repr(~Perm.R), 'Perm.W|Perm.X')
self.assertEqual(repr(~Perm.W), '<Perm.R|X: 5>') self.assertEqual(repr(~Perm.W), 'Perm.R|Perm.X')
self.assertEqual(repr(~Perm.X), '<Perm.R|W: 6>') self.assertEqual(repr(~Perm.X), 'Perm.R|Perm.W')
self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X: 1>') self.assertEqual(repr(~(Perm.R | Perm.W)), 'Perm.X')
self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm: 0>') self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '0x0')
self.assertEqual(repr(Perm(~0)), '<Perm.R|W|X: 7>') self.assertEqual(repr(Perm(~0)), 'Perm.R|Perm.W|Perm.X')
Open = self.Open Open = self.Open
self.assertEqual(repr(Open.RO), '<Open.RO: 0>') self.assertEqual(repr(Open.RO), 'Open.RO')
self.assertEqual(repr(Open.WO), '<Open.WO: 1>') self.assertEqual(repr(Open.WO), 'Open.WO')
self.assertEqual(repr(Open.AC), '<Open.AC: 3>') self.assertEqual(repr(Open.AC), 'Open.AC')
self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>') self.assertEqual(repr(Open.RO | Open.CE), 'Open.CE')
self.assertEqual(repr(Open.WO | Open.CE), '<Open.WO|CE: 524289>') self.assertEqual(repr(Open.WO | Open.CE), 'Open.WO|Open.CE')
self.assertEqual(repr(~Open.RO), '<Open.WO|RW|CE: 524291>') self.assertEqual(repr(~Open.RO), 'Open.WO|Open.RW|Open.CE')
self.assertEqual(repr(~Open.WO), '<Open.RW|CE: 524290>') self.assertEqual(repr(~Open.WO), 'Open.RW|Open.CE')
self.assertEqual(repr(~Open.AC), '<Open.CE: 524288>') self.assertEqual(repr(~Open.AC), 'Open.CE')
self.assertEqual(repr(~Open.CE), '<Open.AC: 3>') self.assertEqual(repr(~(Open.RO | Open.CE)), 'Open.AC')
self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>') self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>')
def test_format(self): def test_format(self):
Perm = self.Perm Perm = self.Perm
self.assertEqual(format(Perm.R, ''), 'Perm.R') self.assertEqual(format(Perm.R, ''), 'R')
self.assertEqual(format(Perm.R | Perm.X, ''), 'Perm.R|X') self.assertEqual(format(Perm.R | Perm.X, ''), 'R|X')
def test_or(self): def test_or(self):
Perm = self.Perm Perm = self.Perm
@ -2707,7 +2704,7 @@ class TestFlag(unittest.TestCase):
self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4) self.assertEqual(Color.BLUE.value, 4)
self.assertEqual(Color.ALL.value, 7) self.assertEqual(Color.ALL.value, 7)
self.assertEqual(str(Color.BLUE), 'Color.BLUE') self.assertEqual(str(Color.BLUE), 'BLUE')
class Color(AllMixin, StrMixin, Flag): class Color(AllMixin, StrMixin, Flag):
RED = auto() RED = auto()
GREEN = auto() GREEN = auto()
@ -2850,77 +2847,70 @@ class TestIntFlag(unittest.TestCase):
def test_str(self): def test_str(self):
Perm = self.Perm Perm = self.Perm
self.assertEqual(str(Perm.R), 'Perm.R') self.assertEqual(str(Perm.R), 'R')
self.assertEqual(str(Perm.W), 'Perm.W') self.assertEqual(str(Perm.W), 'W')
self.assertEqual(str(Perm.X), 'Perm.X') self.assertEqual(str(Perm.X), 'X')
self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W') self.assertEqual(str(Perm.R | Perm.W), 'R|W')
self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X') self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'R|W|X')
self.assertEqual(str(Perm.R | 8), '12') self.assertEqual(str(Perm.R | 8), '12')
self.assertEqual(str(Perm(0)), 'Perm(0)') self.assertEqual(str(Perm(0)), 'Perm(0)')
self.assertEqual(str(Perm(8)), '8') self.assertEqual(str(Perm(8)), '8')
self.assertEqual(str(~Perm.R), 'Perm.W|X') self.assertEqual(str(~Perm.R), 'W|X')
self.assertEqual(str(~Perm.W), 'Perm.R|X') self.assertEqual(str(~Perm.W), 'R|X')
self.assertEqual(str(~Perm.X), 'Perm.R|W') self.assertEqual(str(~Perm.X), 'R|W')
self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X') self.assertEqual(str(~(Perm.R | Perm.W)), 'X')
self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)') self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)')
self.assertEqual(str(~(Perm.R | 8)), '-13') self.assertEqual(str(~(Perm.R | 8)), '-13')
self.assertEqual(str(Perm(~0)), 'Perm.R|W|X') self.assertEqual(str(Perm(~0)), 'R|W|X')
self.assertEqual(str(Perm(~8)), '-9') self.assertEqual(str(Perm(~8)), '-9')
Open = self.Open Open = self.Open
self.assertEqual(str(Open.RO), 'Open.RO') self.assertEqual(str(Open.RO), 'RO')
self.assertEqual(str(Open.WO), 'Open.WO') self.assertEqual(str(Open.WO), 'WO')
self.assertEqual(str(Open.AC), 'Open.AC') self.assertEqual(str(Open.AC), 'AC')
self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') self.assertEqual(str(Open.RO | Open.CE), 'CE')
self.assertEqual(str(Open.WO | Open.CE), 'Open.WO|CE') self.assertEqual(str(Open.WO | Open.CE), 'WO|CE')
self.assertEqual(str(Open(4)), '4') self.assertEqual(str(Open(4)), '4')
self.assertEqual(str(~Open.RO), 'Open.WO|RW|CE') self.assertEqual(str(~Open.RO), 'WO|RW|CE')
self.assertEqual(str(~Open.WO), 'Open.RW|CE') self.assertEqual(str(~Open.WO), 'RW|CE')
self.assertEqual(str(~Open.AC), 'Open.CE') self.assertEqual(str(~Open.AC), 'CE')
self.assertEqual(str(~Open.CE), 'Open.AC') self.assertEqual(str(~(Open.RO | Open.CE)), 'AC')
self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC') self.assertEqual(str(~(Open.WO | Open.CE)), 'RW')
self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW')
self.assertEqual(str(Open(~4)), '-5') self.assertEqual(str(Open(~4)), '-5')
Skip = self.Skip
self.assertEqual(str(Skip(~4)), 'Skip.FIRST|SECOND|EIGHTH')
def test_repr(self): def test_repr(self):
Perm = self.Perm Perm = self.Perm
self.assertEqual(repr(Perm.R), '<Perm.R: 4>') self.assertEqual(repr(Perm.R), 'Perm.R')
self.assertEqual(repr(Perm.W), '<Perm.W: 2>') self.assertEqual(repr(Perm.W), 'Perm.W')
self.assertEqual(repr(Perm.X), '<Perm.X: 1>') self.assertEqual(repr(Perm.X), 'Perm.X')
self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>') self.assertEqual(repr(Perm.R | Perm.W), 'Perm.R|Perm.W')
self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>') self.assertEqual(repr(Perm.R | Perm.W | Perm.X), 'Perm.R|Perm.W|Perm.X')
self.assertEqual(repr(Perm.R | 8), '12') self.assertEqual(repr(Perm.R | 8), '12')
self.assertEqual(repr(Perm(0)), '<Perm: 0>') self.assertEqual(repr(Perm(0)), '0x0')
self.assertEqual(repr(Perm(8)), '8') self.assertEqual(repr(Perm(8)), '8')
self.assertEqual(repr(~Perm.R), '<Perm.W|X: 3>') self.assertEqual(repr(~Perm.R), 'Perm.W|Perm.X')
self.assertEqual(repr(~Perm.W), '<Perm.R|X: 5>') self.assertEqual(repr(~Perm.W), 'Perm.R|Perm.X')
self.assertEqual(repr(~Perm.X), '<Perm.R|W: 6>') self.assertEqual(repr(~Perm.X), 'Perm.R|Perm.W')
self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X: 1>') self.assertEqual(repr(~(Perm.R | Perm.W)), 'Perm.X')
self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm: 0>') self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '0x0')
self.assertEqual(repr(~(Perm.R | 8)), '-13') self.assertEqual(repr(~(Perm.R | 8)), '-13')
self.assertEqual(repr(Perm(~0)), '<Perm.R|W|X: 7>') self.assertEqual(repr(Perm(~0)), 'Perm.R|Perm.W|Perm.X')
self.assertEqual(repr(Perm(~8)), '-9') self.assertEqual(repr(Perm(~8)), '-9')
Open = self.Open Open = self.Open
self.assertEqual(repr(Open.RO), '<Open.RO: 0>') self.assertEqual(repr(Open.RO), 'Open.RO')
self.assertEqual(repr(Open.WO), '<Open.WO: 1>') self.assertEqual(repr(Open.WO), 'Open.WO')
self.assertEqual(repr(Open.AC), '<Open.AC: 3>') self.assertEqual(repr(Open.AC), 'Open.AC')
self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>') self.assertEqual(repr(Open.RO | Open.CE), 'Open.CE')
self.assertEqual(repr(Open.WO | Open.CE), '<Open.WO|CE: 524289>') self.assertEqual(repr(Open.WO | Open.CE), 'Open.WO|Open.CE')
self.assertEqual(repr(Open(4)), '4') self.assertEqual(repr(Open(4)), '4')
self.assertEqual(repr(~Open.RO), '<Open.WO|RW|CE: 524291>') self.assertEqual(repr(~Open.RO), 'Open.WO|Open.RW|Open.CE')
self.assertEqual(repr(~Open.WO), '<Open.RW|CE: 524290>') self.assertEqual(repr(~Open.WO), 'Open.RW|Open.CE')
self.assertEqual(repr(~Open.AC), '<Open.CE: 524288>') self.assertEqual(repr(~Open.AC), 'Open.CE')
self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>') self.assertEqual(repr(~(Open.RO | Open.CE)), 'Open.AC')
self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>') self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
self.assertEqual(repr(Open(~4)), '-5') self.assertEqual(repr(Open(~4)), '-5')
Skip = self.Skip
self.assertEqual(repr(Skip(~4)), '<Skip.FIRST|SECOND|EIGHTH: 11>')
def test_format(self): def test_format(self):
Perm = self.Perm Perm = self.Perm
self.assertEqual(format(Perm.R, ''), '4') self.assertEqual(format(Perm.R, ''), '4')
@ -3252,7 +3242,7 @@ class TestIntFlag(unittest.TestCase):
self.assertEqual(Color.GREEN.value, 2) self.assertEqual(Color.GREEN.value, 2)
self.assertEqual(Color.BLUE.value, 4) self.assertEqual(Color.BLUE.value, 4)
self.assertEqual(Color.ALL.value, 7) self.assertEqual(Color.ALL.value, 7)
self.assertEqual(str(Color.BLUE), 'Color.BLUE') self.assertEqual(str(Color.BLUE), 'BLUE')
class Color(AllMixin, StrMixin, IntFlag): class Color(AllMixin, StrMixin, IntFlag):
RED = auto() RED = auto()
GREEN = auto() GREEN = auto()
@ -3374,6 +3364,8 @@ class TestUnique(unittest.TestCase):
value = 4 value = 4
class TestEnumTypeSubclassing(unittest.TestCase):
pass
expected_help_output_with_docs = """\ expected_help_output_with_docs = """\
Help on class Color in module %s: Help on class Color in module %s:
@ -3390,11 +3382,11 @@ class Color(enum.Enum)
|\x20\x20 |\x20\x20
| Data and other attributes defined here: | Data and other attributes defined here:
|\x20\x20 |\x20\x20
| blue = <Color.blue: 3> | blue = Color.blue
|\x20\x20 |\x20\x20
| green = <Color.green: 2> | green = Color.green
|\x20\x20 |\x20\x20
| red = <Color.red: 1> | red = Color.red
|\x20\x20 |\x20\x20
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum: | Data descriptors inherited from enum.Enum:
@ -3406,7 +3398,7 @@ class Color(enum.Enum)
| The value of the Enum member. | The value of the Enum member.
|\x20\x20 |\x20\x20
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------
| Readonly properties inherited from enum.EnumMeta: | Readonly properties inherited from enum.EnumType:
|\x20\x20 |\x20\x20
| __members__ | __members__
| Returns a mapping of member name->value. | Returns a mapping of member name->value.
@ -3427,11 +3419,11 @@ class Color(enum.Enum)
|\x20\x20 |\x20\x20
| Data and other attributes defined here: | Data and other attributes defined here:
|\x20\x20 |\x20\x20
| blue = <Color.blue: 3> | blue = Color.blue
|\x20\x20 |\x20\x20
| green = <Color.green: 2> | green = Color.green
|\x20\x20 |\x20\x20
| red = <Color.red: 1> | red = Color.red
|\x20\x20 |\x20\x20
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum: | Data descriptors inherited from enum.Enum:
@ -3441,7 +3433,7 @@ class Color(enum.Enum)
| value | value
|\x20\x20 |\x20\x20
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------
| Data descriptors inherited from enum.EnumMeta: | Data descriptors inherited from enum.EnumType:
|\x20\x20 |\x20\x20
| __members__""" | __members__"""
@ -3468,7 +3460,7 @@ class TestStdLib(unittest.TestCase):
def test_inspect_getmembers(self): def test_inspect_getmembers(self):
values = dict(( values = dict((
('__class__', EnumMeta), ('__class__', EnumType),
('__doc__', 'An enumeration.'), ('__doc__', 'An enumeration.'),
('__members__', self.Color.__members__), ('__members__', self.Color.__members__),
('__module__', __name__), ('__module__', __name__),
@ -3495,11 +3487,11 @@ class TestStdLib(unittest.TestCase):
from inspect import Attribute from inspect import Attribute
values = [ values = [
Attribute(name='__class__', kind='data', Attribute(name='__class__', kind='data',
defining_class=object, object=EnumMeta), defining_class=object, object=EnumType),
Attribute(name='__doc__', kind='data', Attribute(name='__doc__', kind='data',
defining_class=self.Color, object='An enumeration.'), defining_class=self.Color, object='An enumeration.'),
Attribute(name='__members__', kind='property', Attribute(name='__members__', kind='property',
defining_class=EnumMeta, object=EnumMeta.__members__), defining_class=EnumType, object=EnumType.__members__),
Attribute(name='__module__', kind='data', Attribute(name='__module__', kind='data',
defining_class=self.Color, object=__name__), defining_class=self.Color, object=__name__),
Attribute(name='blue', kind='data', Attribute(name='blue', kind='data',
@ -3589,6 +3581,45 @@ class TestIntEnumConvert(unittest.TestCase):
('test.test_enum', '__main__')[__name__=='__main__'], ('test.test_enum', '__main__')[__name__=='__main__'],
filter=lambda x: x.startswith('CONVERT_TEST_')) filter=lambda x: x.startswith('CONVERT_TEST_'))
def test_convert_repr_and_str(self):
module = ('test.test_enum', '__main__')[__name__=='__main__']
test_type = enum.IntEnum._convert_(
'UnittestConvert',
module,
filter=lambda x: x.startswith('CONVERT_TEST_'))
self.assertEqual(repr(test_type.CONVERT_TEST_NAME_A), '%s.CONVERT_TEST_NAME_A' % module)
self.assertEqual(str(test_type.CONVERT_TEST_NAME_A), 'CONVERT_TEST_NAME_A')
self.assertEqual(format(test_type.CONVERT_TEST_NAME_A), '5')
# global names for StrEnum._convert_ test
CONVERT_STR_TEST_2 = 'goodbye'
CONVERT_STR_TEST_1 = 'hello'
class TestStrEnumConvert(unittest.TestCase):
def test_convert(self):
test_type = enum.StrEnum._convert_(
'UnittestConvert',
('test.test_enum', '__main__')[__name__=='__main__'],
filter=lambda x: x.startswith('CONVERT_STR_'))
# 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.
self.assertEqual([name for name in dir(test_type)
if name[0:2] not in ('CO', '__')],
[], msg='Names other than CONVERT_STR_* found.')
def test_convert_repr_and_str(self):
module = ('test.test_enum', '__main__')[__name__=='__main__']
test_type = enum.StrEnum._convert_(
'UnittestConvert',
module,
filter=lambda x: x.startswith('CONVERT_STR_'))
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % module)
self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -453,7 +453,7 @@ class PydocDocTest(unittest.TestCase):
zero = 0 zero = 0
one = 1 one = 1
doc = pydoc.render_doc(BinaryInteger) doc = pydoc.render_doc(BinaryInteger)
self.assertIn('<BinaryInteger.zero: 0>', doc) self.assertIn('BinaryInteger.zero', doc)
def test_mixed_case_module_names_are_lower_cased(self): def test_mixed_case_module_names_are_lower_cased(self):
# issue16484 # issue16484

View file

@ -872,7 +872,7 @@ class PendingSignalsTests(unittest.TestCase):
%s %s
blocked = %s blocked = %r
signum = signal.SIGALRM signum = signal.SIGALRM
# child: block and wait the signal # child: block and wait the signal

View file

@ -1518,9 +1518,9 @@ class GeneralModuleTests(unittest.TestCase):
infos = socket.getaddrinfo(HOST, 80, socket.AF_INET, socket.SOCK_STREAM) infos = socket.getaddrinfo(HOST, 80, socket.AF_INET, socket.SOCK_STREAM)
for family, type, _, _, _ in infos: for family, type, _, _, _ in infos:
self.assertEqual(family, socket.AF_INET) self.assertEqual(family, socket.AF_INET)
self.assertEqual(str(family), 'AddressFamily.AF_INET') self.assertEqual(str(family), 'AF_INET')
self.assertEqual(type, socket.SOCK_STREAM) self.assertEqual(type, socket.SOCK_STREAM)
self.assertEqual(str(type), 'SocketKind.SOCK_STREAM') self.assertEqual(str(type), 'SOCK_STREAM')
infos = socket.getaddrinfo(HOST, None, 0, socket.SOCK_STREAM) infos = socket.getaddrinfo(HOST, None, 0, socket.SOCK_STREAM)
for _, socktype, _, _, _ in infos: for _, socktype, _, _, _ in infos:
self.assertEqual(socktype, socket.SOCK_STREAM) self.assertEqual(socktype, socket.SOCK_STREAM)
@ -1793,8 +1793,8 @@ class GeneralModuleTests(unittest.TestCase):
# Make sure that the AF_* and SOCK_* constants have enum-like string # Make sure that the AF_* and SOCK_* constants have enum-like string
# reprs. # reprs.
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
self.assertEqual(str(s.family), 'AddressFamily.AF_INET') self.assertEqual(str(s.family), 'AF_INET')
self.assertEqual(str(s.type), 'SocketKind.SOCK_STREAM') self.assertEqual(str(s.type), 'SOCK_STREAM')
def test_socket_consistent_sock_type(self): def test_socket_consistent_sock_type(self):
SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0) SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0)

View file

@ -381,7 +381,7 @@ class BasicSocketTests(unittest.TestCase):
# Make sure that the PROTOCOL_* constants have enum-like string # Make sure that the PROTOCOL_* constants have enum-like string
# reprs. # reprs.
proto = ssl.PROTOCOL_TLS proto = ssl.PROTOCOL_TLS
self.assertEqual(str(proto), '_SSLMethod.PROTOCOL_TLS') self.assertEqual(str(proto), 'PROTOCOL_TLS')
ctx = ssl.SSLContext(proto) ctx = ssl.SSLContext(proto)
self.assertIs(ctx.protocol, proto) self.assertIs(ctx.protocol, proto)

View file

@ -1467,18 +1467,18 @@ class UnicodeTest(string_tests.CommonTest,
ABC = 'abc' ABC = 'abc'
# Testing Unicode formatting strings... # Testing Unicode formatting strings...
self.assertEqual("%s, %s" % (Str.ABC, Str.ABC), self.assertEqual("%s, %s" % (Str.ABC, Str.ABC),
'Str.ABC, Str.ABC') 'ABC, ABC')
self.assertEqual("%s, %s, %d, %i, %u, %f, %5.2f" % self.assertEqual("%s, %s, %d, %i, %u, %f, %5.2f" %
(Str.ABC, Str.ABC, (Str.ABC, Str.ABC,
Int.IDES, Int.IDES, Int.IDES, Int.IDES, Int.IDES, Int.IDES,
Float.PI, Float.PI), Float.PI, Float.PI),
'Str.ABC, Str.ABC, 15, 15, 15, 3.141593, 3.14') 'ABC, ABC, 15, 15, 15, 3.141593, 3.14')
# formatting jobs delegated from the string implementation: # formatting jobs delegated from the string implementation:
self.assertEqual('...%(foo)s...' % {'foo':Str.ABC}, self.assertEqual('...%(foo)s...' % {'foo':Str.ABC},
'...Str.ABC...') '...ABC...')
self.assertEqual('...%(foo)s...' % {'foo':Int.IDES}, self.assertEqual('...%(foo)s...' % {'foo':Int.IDES},
'...Int.IDES...') '...IDES...')
self.assertEqual('...%(foo)i...' % {'foo':Int.IDES}, self.assertEqual('...%(foo)i...' % {'foo':Int.IDES},
'...15...') '...15...')
self.assertEqual('...%(foo)d...' % {'foo':Int.IDES}, self.assertEqual('...%(foo)d...' % {'foo':Int.IDES},

View file

@ -0,0 +1,4 @@
Enum's `repr()` and `str()` have changed: `repr()` is now *EnumClass.MemberName*
and `str()` is *MemberName*. Additionally, stdlib Enum's whose contents are
available as module attributes, such as `RegexFlag.IGNORECASE`, have their
`repr()` as *module.name*, e.g. `re.IGNORECASE`.

View file

@ -0,0 +1,3 @@
Enum: adjust ``repr()`` to show only enum and member name (not value, nor
angle brackets) and ``str()`` to show only member name. Update and improve
documentation to match.