Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
update __doc__
  • Loading branch information
ethanfurman committed Jan 14, 2022
commit 13c4b9262b2fd2a47cc25e51f7bed66460585a93
172 changes: 164 additions & 8 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ def bin(num, max_bits=None):
digits = (sign[-1] * max_bits + digits)[-max_bits:]
return "%s %s" % (sign, digits)

def _dedent(text):
"""
Like textwrap.dedent. Rewritten because we cannot import textwrap.
"""
lines = text.split('\n')
blanks = 0
for i, ch in enumerate(lines[0]):
if ch != ' ':
break
for j, l in enumerate(lines):
lines[j] = l[i:]
return '\n'.join(lines)


_auto_null = object()
class auto:
Expand Down Expand Up @@ -459,10 +472,6 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
classdict['_flag_mask_'] = flag_mask
classdict['_all_bits_'] = 2 ** ((flag_mask).bit_length()) - 1
classdict['_inverted_'] = None
#
# create a default docstring if one has not been provided
if '__doc__' not in classdict:
classdict['__doc__'] = 'An enumeration.'
try:
exc = None
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)
Expand All @@ -476,6 +485,120 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
# update classdict with any changes made by __init_subclass__
classdict.update(enum_class.__dict__)
#
# create a default docstring if one has not been provided
if enum_class.__doc__ is None:
if not member_names:
enum_class.__doc__ = classdict['__doc__'] = _dedent("""\
Comment thread
ethanfurman marked this conversation as resolved.
Outdated
Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
... RED = 1
... BLUE = 2
... GREEN = 3

Access them by:

- attribute access::

>>> Color.RED
<Color.RED: 1>

- value lookup:

>>> Color(1)
<Color.RED: 1>

- name lookup:

>>> Color['RED']
<Color.RED: 1>

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3

>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own
attributes -- see the documentation for details.
""")
else:
member = list(enum_class)[0]
enum_length = len(enum_class)
cls_name = enum_class.__name__
if enum_length == 1:
list_line = 'list(%s)' % cls_name
list_repr = '[<%s.%s: %r>]' % (cls_name, member.name, member.value)
elif enum_length == 2:
member2 = list(enum_class)[1]
list_line = 'list(%s)' % cls_name
list_repr = '[<%s.%s: %r>, <%s.%s: %r>]' % (
cls_name, member.name, member.value,
cls_name, member2.name, member2.value,
)
elif enum_length == 3:
member2 = list(enum_class)[1]
member3 = list(enum_class)[2]
list_line = 'list(%s)' % cls_name
list_repr = '[<%s.%s: %r>, <%s.%s: %r>, <%s.%s: %r>]' % (
cls_name, member.name, member.value,
cls_name, member2.name, member2.value,
cls_name, member3.name, member3.value,
)
else:
member2 = list(enum_class)[1]
member3 = list(enum_class)[2]
list_line = 'list(%s)[:3]' % cls_name
list_repr = '[<%s.%s: %r>, <%s.%s: %r>, <%s.%s: %r>]' % (
Comment thread
ethanfurman marked this conversation as resolved.
Outdated
cls_name, member.name, member.value,
cls_name, member2.name, member2.value,
cls_name, member3.name, member3.value,
)
enum_class.__doc__ = classdict['__doc__'] = _dedent("""\
A collection of name/value pairs.

Access them by:

- attribute access::

>>> %s.%s
<%s.%s: %r>

- value lookup:

>>> %s(%r)
<%s.%s: %r>

- name lookup:

>>> %s[%r]
<%s.%s: %r>

Enumerations can be iterated over, and know how many members they have:

>>> len(%s)
%r

>>> list(%s)[:1]
[<%s.%s: %r>]

Methods can be added to enumerations, and members can have their own
attributes -- see the documentation for details.
"""
% (cls_name, member.name,
cls_name, member.name, member.value,
cls_name, member.value,
cls_name, member.name, member.value,
cls_name, member.name,
cls_name, member.name, member.value,
cls_name, enum_length,
cls_name, cls_name, member.name, member.value,
))
#
# double check that repr and friends are not the mixin's or various
# things break (such as pickle)
# however, if the method is defined in the Enum itself, don't replace
Expand Down Expand Up @@ -954,9 +1077,42 @@ def _find_new_(mcls, classdict, member_type, first_enum):

class Enum(metaclass=EnumType):
"""
Generic enumeration.
Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
... RED = 1
... BLUE = 2
... GREEN = 3

Access them by:

- attribute access::

>>> Color.RED
<Color.RED: 1>

- value lookup:

>>> Color(1)
<Color.RED: 1>

- name lookup:

>>> Color['RED']
<Color.RED: 1>

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3

>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Derive from this class to define new enumerations.
Methods can be added to enumerations, and members can have their own
attributes -- see the documentation for details.
"""

def __new__(cls, value):
Expand Down Expand Up @@ -1761,8 +1917,8 @@ def _test_simple_enum(checked_enum, simple_enum):
+ list(simple_enum._member_map_.keys())
)
for key in set(checked_keys + simple_keys):
if key in ('__module__', '_member_map_', '_value2member_map_'):
# keys known to be different
if key in ('__module__', '_member_map_', '_value2member_map_', '__doc__'):
# keys known to be different, or very long
continue
elif key in member_names:
# members are checked below
Expand Down
92 changes: 62 additions & 30 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -3813,7 +3813,35 @@ class TestEnumTypeSubclassing(unittest.TestCase):
class Color(enum.Enum)
| Color(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
|\x20\x20
| An enumeration.
| A collection of name/value pairs.
|\x20\x20
| Access them by:
|\x20\x20
| - attribute access::
|\x20\x20
| >>> Color.CYAN
| <Color.CYAN: 1>
|\x20\x20
| - value lookup:
|\x20\x20
| >>> Color(1)
| <Color.CYAN: 1>
|\x20\x20
| - name lookup:
|\x20\x20
| >>> Color['CYAN']
| <Color.CYAN: 1>
|\x20\x20
| Enumerations can be iterated over, and know how many members they have:
|\x20\x20
| >>> len(Color)
| 3
|\x20\x20
| >>> list(Color)[:1]
| [<Color.CYAN: 1>]
|\x20\x20
| Methods can be added to enumerations, and members can have their own
| attributes -- see the documentation for details.
|\x20\x20
| Method resolution order:
| Color
Expand All @@ -3822,11 +3850,11 @@ class Color(enum.Enum)
|\x20\x20
| Data and other attributes defined here:
|\x20\x20
| blue = <Color.blue: 3>
| CYAN = <Color.CYAN: 1>
|\x20\x20
| green = <Color.green: 2>
| MAGENTA = <Color.MAGENTA: 2>
|\x20\x20
| red = <Color.red: 1>
| YELLOW = <Color.YELLOW: 3>
|\x20\x20
| ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum:
Expand Down Expand Up @@ -3878,11 +3906,11 @@ class Color(enum.Enum)
|\x20\x20
| Data and other attributes defined here:
|\x20\x20
| blue = <Color.blue: 3>
| YELLOW = <Color.YELLOW: 3>
|\x20\x20
| green = <Color.green: 2>
| MAGENTA = <Color.MAGENTA: 2>
|\x20\x20
| red = <Color.red: 1>
| CYAN = <Color.CYAN: 1>
|\x20\x20
| ----------------------------------------------------------------------
| Data descriptors inherited from enum.Enum:
Expand All @@ -3901,9 +3929,9 @@ class TestStdLib(unittest.TestCase):
maxDiff = None

class Color(Enum):
red = 1
green = 2
blue = 3
CYAN = 1
MAGENTA = 2
YELLOW = 3

def test_pydoc(self):
# indirectly test __objclass__
Expand All @@ -3915,18 +3943,18 @@ def test_pydoc(self):
helper = pydoc.Helper(output=output)
helper(self.Color)
result = output.getvalue().strip()
self.assertEqual(result, expected_text)
self.assertEqual(result, expected_text, result)

def test_inspect_getmembers(self):
values = dict((
('__class__', EnumType),
('__doc__', 'An enumeration.'),
('__doc__', '...'),
('__members__', self.Color.__members__),
('__module__', __name__),
('blue', self.Color.blue),
('green', self.Color.green),
('YELLOW', self.Color.YELLOW),
('MAGENTA', self.Color.MAGENTA),
('CYAN', self.Color.CYAN),
('name', Enum.__dict__['name']),
('red', self.Color.red),
('value', Enum.__dict__['value']),
('__len__', self.Color.__len__),
('__contains__', self.Color.__contains__),
Expand All @@ -3940,6 +3968,9 @@ def test_inspect_getmembers(self):
self.assertEqual(set(values.keys()), set(result.keys()))
failed = False
for k in values.keys():
if k == '__doc__':
# __doc__ is huge, not comparing
continue
if result[k] != values[k]:
print()
print('\n%s\n key: %s\n result: %s\nexpected: %s\n%s\n' %
Expand All @@ -3957,7 +3988,7 @@ def test_inspect_classify_class_attrs(self):
Attribute(name='__contains__', kind='method',
defining_class=EnumType, object=self.Color.__contains__),
Attribute(name='__doc__', kind='data',
defining_class=self.Color, object='An enumeration.'),
defining_class=self.Color, object='...'),
Attribute(name='__getitem__', kind='method',
defining_class=EnumType, object=self.Color.__getitem__),
Attribute(name='__iter__', kind='method',
Expand All @@ -3974,12 +4005,12 @@ def test_inspect_classify_class_attrs(self):
defining_class=self.Color, object='Color'),
Attribute(name='__qualname__', kind='data',
defining_class=self.Color, object='TestStdLib.Color'),
Attribute(name='blue', kind='data',
defining_class=self.Color, object=self.Color.blue),
Attribute(name='green', kind='data',
defining_class=self.Color, object=self.Color.green),
Attribute(name='red', kind='data',
defining_class=self.Color, object=self.Color.red),
Attribute(name='YELLOW', kind='data',
defining_class=self.Color, object=self.Color.YELLOW),
Attribute(name='MAGENTA', kind='data',
defining_class=self.Color, object=self.Color.MAGENTA),
Attribute(name='CYAN', kind='data',
defining_class=self.Color, object=self.Color.CYAN),
Attribute(name='name', kind='data',
defining_class=Enum, object=Enum.__dict__['name']),
Attribute(name='value', kind='data',
Expand All @@ -3999,9 +4030,10 @@ def test_inspect_classify_class_attrs(self):
)
failed = False
for v, r in zip(values, result):
if r.name == '__init_subclass__':
if r.name in ('__init_subclass__', '__doc__'):
# not sure how to make the __init_subclass_ Attributes match
# so as long as there is one, call it good
# __doc__ is too big to check exactly, so treat the same as __init_subclass__
for name in ('name','kind','defining_class'):
if getattr(v, name) != getattr(r, name):
print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='')
Expand All @@ -4015,15 +4047,15 @@ def test_inspect_classify_class_attrs(self):
def test_test_simple_enum(self):
@_simple_enum(Enum)
class SimpleColor:
RED = 1
GREEN = 2
BLUE = 3
CYAN = 1
MAGENTA = 2
YELLOW = 3
class CheckedColor(Enum):
RED = 1
GREEN = 2
BLUE = 3
CYAN = 1
MAGENTA = 2
YELLOW = 3
self.assertTrue(_test_simple_enum(CheckedColor, SimpleColor) is None)
SimpleColor.GREEN._value_ = 9
SimpleColor.MAGENTA._value_ = 9
self.assertRaisesRegex(
TypeError, "enum mismatch",
_test_simple_enum, CheckedColor, SimpleColor,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
``IntEnum``, ``IntFlag``, and ``StrEnum`` use the mixed-in type for their
``str()`` and ``format()`` output.