Skip to content

Commit a323cdc

Browse files
CuriousLearnerncoghlan
authored andcommitted
bpo-8525: help() on a type now shows builtin subclasses (GH-5066)
For builtin types with builtin subclasses, help() on the type now shows up to 4 of the subclasses. This partially replaces the exception hierarchy information previously displayed in Python 2.7.
1 parent d31e773 commit a323cdc

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

Lib/pydoc.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,24 @@ def makename(c, m=object.__module__):
12541254
push(' ' + makename(base))
12551255
push('')
12561256

1257+
# List the built-in subclasses, if any:
1258+
subclasses = sorted(
1259+
(str(cls.__name__) for cls in object.__subclasses__()
1260+
if not cls.__name__.startswith("_") and cls.__module__ == "builtins"),
1261+
key=str.lower
1262+
)
1263+
no_of_subclasses = len(subclasses)
1264+
MAX_SUBCLASSES_TO_DISPLAY = 4
1265+
if subclasses:
1266+
push("Built-in subclasses:")
1267+
for subclassname in subclasses[:MAX_SUBCLASSES_TO_DISPLAY]:
1268+
push(' ' + subclassname)
1269+
if no_of_subclasses > MAX_SUBCLASSES_TO_DISPLAY:
1270+
push(' ... and ' +
1271+
str(no_of_subclasses - MAX_SUBCLASSES_TO_DISPLAY) +
1272+
' other subclasses')
1273+
push('')
1274+
12571275
# Cute little class to pump out a horizontal rule between sections.
12581276
class HorizontalRule:
12591277
def __init__(self):

Lib/test/test_pydoc.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,124 @@ def test_stripid(self):
517517
self.assertEqual(stripid("<type 'exceptions.Exception'>"),
518518
"<type 'exceptions.Exception'>")
519519

520+
def test_builtin_with_more_than_four_children(self):
521+
"""Tests help on builtin object which have more than four child classes.
522+
523+
When running help() on a builtin class which has child classes, it
524+
should contain a "Built-in subclasses" section and only 4 classes
525+
should be displayed with a hint on how many more subclasses are present.
526+
For example:
527+
528+
>>> help(object)
529+
Help on class object in module builtins:
530+
531+
class object
532+
| The most base type
533+
|
534+
| Built-in subclasses:
535+
| async_generator
536+
| BaseException
537+
| builtin_function_or_method
538+
| bytearray
539+
| ... and 82 other subclasses
540+
"""
541+
doc = pydoc.TextDoc()
542+
text = doc.docclass(object)
543+
snip = (" | Built-in subclasses:\n"
544+
" | async_generator\n"
545+
" | BaseException\n"
546+
" | builtin_function_or_method\n"
547+
" | bytearray\n"
548+
" | ... and \\d+ other subclasses")
549+
self.assertRegex(text, snip)
550+
551+
def test_builtin_with_child(self):
552+
"""Tests help on builtin object which have only child classes.
553+
554+
When running help() on a builtin class which has child classes, it
555+
should contain a "Built-in subclasses" section. For example:
556+
557+
>>> help(ArithmeticError)
558+
Help on class ArithmeticError in module builtins:
559+
560+
class ArithmeticError(Exception)
561+
| Base class for arithmetic errors.
562+
|
563+
...
564+
|
565+
| Built-in subclasses:
566+
| FloatingPointError
567+
| OverflowError
568+
| ZeroDivisionError
569+
"""
570+
doc = pydoc.TextDoc()
571+
text = doc.docclass(ArithmeticError)
572+
snip = (" | Built-in subclasses:\n"
573+
" | FloatingPointError\n"
574+
" | OverflowError\n"
575+
" | ZeroDivisionError")
576+
self.assertIn(snip, text)
577+
578+
def test_builtin_with_grandchild(self):
579+
"""Tests help on builtin classes which have grandchild classes.
580+
581+
When running help() on a builtin class which has child classes, it
582+
should contain a "Built-in subclasses" section. However, if it also has
583+
grandchildren, these should not show up on the subclasses section.
584+
For example:
585+
586+
>>> help(Exception)
587+
Help on class Exception in module builtins:
588+
589+
class Exception(BaseException)
590+
| Common base class for all non-exit exceptions.
591+
|
592+
...
593+
|
594+
| Built-in subclasses:
595+
| ArithmeticError
596+
| AssertionError
597+
| AttributeError
598+
...
599+
"""
600+
doc = pydoc.TextDoc()
601+
text = doc.docclass(Exception)
602+
snip = (" | Built-in subclasses:\n"
603+
" | ArithmeticError\n"
604+
" | AssertionError\n"
605+
" | AttributeError")
606+
self.assertIn(snip, text)
607+
# Testing that the grandchild ZeroDivisionError does not show up
608+
self.assertNotIn('ZeroDivisionError', text)
609+
610+
def test_builtin_no_child(self):
611+
"""Tests help on builtin object which have no child classes.
612+
613+
When running help() on a builtin class which has no child classes, it
614+
should not contain any "Built-in subclasses" section. For example:
615+
616+
>>> help(ZeroDivisionError)
617+
618+
Help on class ZeroDivisionError in module builtins:
619+
620+
class ZeroDivisionError(ArithmeticError)
621+
| Second argument to a division or modulo operation was zero.
622+
|
623+
| Method resolution order:
624+
| ZeroDivisionError
625+
| ArithmeticError
626+
| Exception
627+
| BaseException
628+
| object
629+
|
630+
| Methods defined here:
631+
...
632+
"""
633+
doc = pydoc.TextDoc()
634+
text = doc.docclass(ZeroDivisionError)
635+
# Testing that the subclasses section does not appear
636+
self.assertNotIn('Built-in subclasses', text)
637+
520638
@unittest.skipIf(sys.flags.optimize >= 2,
521639
'Docstrings are omitted with -O2 and above')
522640
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
help() on a type now displays builtin subclasses. This is intended primarily
2+
to help with notification of more specific exception subclasses.
3+
4+
Patch by Sanyam Khurana.

0 commit comments

Comments
 (0)