Skip to content

Commit 95801bb

Browse files
committed
Issue #24879: Teach pydoc to display named tuple fields in the order they were defined.
1 parent 15b87bf commit 95801bb

3 files changed

Lines changed: 35 additions & 4 deletions

File tree

‎Lib/pydoc.py‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ def classify_class_attrs(object):
209209
results.append((name, kind, cls, value))
210210
return results
211211

212+
def sort_attributes(attrs, object):
213+
'Sort the attrs list in-place by _fields and then alphabetically by name'
214+
# This allows data descriptors to be ordered according
215+
# to a _fields attribute if present.
216+
fields = getattr(object, '_fields', [])
217+
try:
218+
field_order = {name : i-len(fields) for (i, name) in enumerate(fields)}
219+
except TypeError:
220+
field_order = {}
221+
keyfunc = lambda attr: (field_order.get(attr[0], 0), attr[0])
222+
attrs.sort(key=keyfunc)
223+
212224
# ----------------------------------------------------- module manipulation
213225

214226
def ispackage(path):
@@ -867,8 +879,7 @@ def spilldata(msg, attrs, predicate):
867879
object.__module__)
868880
tag += ':<br>\n'
869881

870-
# Sort attrs by name.
871-
attrs.sort(key=lambda t: t[0])
882+
sort_attributes(attrs, object)
872883

873884
# Pump out the attrs, segregated by kind.
874885
attrs = spill('Methods %s' % tag, attrs,
@@ -1286,8 +1297,8 @@ def spilldata(msg, attrs, predicate):
12861297
else:
12871298
tag = "inherited from %s" % classname(thisclass,
12881299
object.__module__)
1289-
# Sort attrs by name.
1290-
attrs.sort()
1300+
1301+
sort_attributes(attrs, object)
12911302

12921303
# Pump out the attrs, segregated by kind.
12931304
attrs = spill("Methods %s:\n" % tag, attrs,

‎Lib/test/test_pydoc.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,22 @@ def test_bound_python_method(self):
811811
self.assertEqual(self._get_summary_line(t.wrap),
812812
"wrap(text) method of textwrap.TextWrapper instance")
813813

814+
def test_field_order_for_named_tuples(self):
815+
Person = namedtuple('Person', ['nickname', 'firstname', 'agegroup'])
816+
s = pydoc.render_doc(Person)
817+
self.assertLess(s.index('nickname'), s.index('firstname'))
818+
self.assertLess(s.index('firstname'), s.index('agegroup'))
819+
820+
class NonIterableFields:
821+
_fields = None
822+
823+
class NonHashableFields:
824+
_fields = [[]]
825+
826+
# Make sure these doesn't fail
827+
pydoc.render_doc(NonIterableFields)
828+
pydoc.render_doc(NonHashableFields)
829+
814830
@requires_docstrings
815831
def test_bound_builtin_method(self):
816832
s = StringIO()

‎Misc/NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ Library
2626
header in part headers. Patch written by Peter Landry and reviewed by Pierre
2727
Quentel.
2828

29+
- Issue #24879: help() and pydoc can now list named tuple fields in the
30+
order they were defined rather than alphabetically. The ordering is
31+
determined by the _fields attribute if present.
32+
2933
- Issue #24774: Fix docstring in http.server.test. Patch from Chiu-Hsiang Hsu.
3034

3135
- Issue #21159: Improve message in configparser.InterpolationMissingOptionError.

0 commit comments

Comments
 (0)