Skip to content

Commit 255b603

Browse files
committed
try to associate all dunder methods with relevant pages from datamodel.rst
1 parent 0c0f04c commit 255b603

File tree

4 files changed

+145
-6
lines changed

4 files changed

+145
-6
lines changed

Doc/reference/datamodel.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2911,6 +2911,8 @@ automatic property creation, proxies, frameworks, and automatic resource
29112911
locking/synchronization.
29122912

29132913

2914+
.. _custom-instance-subclass:
2915+
29142916
Customizing instance and subclass checks
29152917
----------------------------------------
29162918

Doc/tools/extensions/pydoc_topics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"continue",
4848
"conversions",
4949
"customization",
50+
"custom-instance-subclass",
5051
"debugger",
5152
"del",
5253
"dict",

Lib/pydoc.py

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,10 +1739,14 @@ def resolve(thing, forceload=0):
17391739
if isinstance(thing, str):
17401740
object = locate(thing, forceload)
17411741
if object is None:
1742+
if re.match(r'^__\w+__$', thing):
1743+
special = "Use help('specialnames') for a list of special names for which help is available.\n"
1744+
else:
1745+
special = ""
17421746
raise ImportError('''\
17431747
No Python documentation found for %r.
1744-
Use help() to get the interactive help utility.
1745-
Use help(str) for help on the str class.''' % thing)
1748+
%sUse help() to get the interactive help utility.
1749+
Use help(str) for help on the str class.''' % (thing, special))
17461750
return object, thing
17471751
else:
17481752
name = getattr(thing, '__name__', None)
@@ -1845,6 +1849,87 @@ def _introdoc():
18451849
enter "q", "quit" or "exit".
18461850
''')
18471851

1852+
def collect_dunders(symbols):
1853+
dunders = {
1854+
'__main__': ('__main__', ''),
1855+
'__call__': ('callable-types', 'SPECIALMETHODS'),
1856+
}
1857+
1858+
basic_dunders = [
1859+
'__new__', '__init__', '__del__', '__repr__', '__str__', '__bytes__',
1860+
'__format__', '__hash__', '__bool__',
1861+
]
1862+
for bd in basic_dunders:
1863+
dunders[bd] = ('customization', 'SPECIALNAMES')
1864+
1865+
attribute_dunders = [
1866+
'__getattr__', '__getattribute__', '__setattr__', '__delattr__',
1867+
'__dir__', '__get__', '__set__', '__delete__', '__objclass__',
1868+
]
1869+
for ad in attribute_dunders:
1870+
dunders[ad] = ('attribute-access', 'SPECIALNAMES')
1871+
1872+
class_dunders = [
1873+
'__init_subclass__', '__set_names__', '__mro_entries__',
1874+
]
1875+
for cd in class_dunders:
1876+
dunders[cd] = ('class-customization', 'SPECIALNAMES')
1877+
1878+
instance_dunders = [
1879+
'__instancecheck__', '__subclasscheck__'
1880+
]
1881+
for d in instance_dunders:
1882+
dunders[d] = ('custom-instance-subclass', 'SPECIALNAMES')
1883+
1884+
sequence_dunders = [
1885+
'__len__', '__length_hint__', '__getitem__', '__setitem__',
1886+
'__delitem__', '__missing__', '__iter__', '__reversed__',
1887+
'__contains__',
1888+
]
1889+
for sd in sequence_dunders:
1890+
dunders[sd] = ('SEQUENCEMETHODS', 'SPECIALMETHODS')
1891+
1892+
comparison_dunders = {
1893+
'__lt__': '<',
1894+
'__le__': '<=',
1895+
'__eq__': '==',
1896+
'__ne__': '!=',
1897+
'__gt__': '>',
1898+
'__ge__': '>=',
1899+
}
1900+
for dunder, symbol in comparison_dunders.items():
1901+
dunders[dunder] = ('customization', f'{symbol} SPECIALMETHODS')
1902+
if symbol in symbols:
1903+
symbols[symbol] += f' {dunder}'
1904+
1905+
arithmetic_dunders = {
1906+
'__add__': '+',
1907+
'__sub__': '-',
1908+
'__mul__': '*',
1909+
'__matmul__': '@',
1910+
'__truediv__': '/',
1911+
'__floordiv__': '//',
1912+
'__mod__': '%',
1913+
'__pow__': '**',
1914+
'__lshift__': '<<',
1915+
'__rshift__': '>>',
1916+
'__and__': '&',
1917+
'__or__': '|',
1918+
'__xor__': '^',
1919+
}
1920+
for dunder, symbol in arithmetic_dunders.items():
1921+
rname = "__r" + dunder[2:]
1922+
iname = "__i" + dunder[2:]
1923+
dunders[dunder] = ('numeric-types', f'{symbol} {rname} {iname} SPECIALMETHODS')
1924+
dunders[rname] = ('numeric-types', f'{symbol} {dunder} SPECIALMETHODS')
1925+
dunders[iname] = ('numeric-types', f'{symbol} {dunder} SPECIALMETHODS')
1926+
if symbol in symbols:
1927+
symbols[symbol] += f' {dunder}'
1928+
1929+
dunders['__divmod__'] = ('numeric-types', 'divmod')
1930+
1931+
return dunders
1932+
18481933
class Helper:
18491934

18501935
# These dictionaries map a topic name to either an alias, or a tuple
@@ -1925,7 +2010,8 @@ class Helper:
19252010
'(': 'TUPLES FUNCTIONS CALLS',
19262011
')': 'TUPLES FUNCTIONS CALLS',
19272012
'[': 'LISTS SUBSCRIPTS SLICINGS',
1928-
']': 'LISTS SUBSCRIPTS SLICINGS'
2013+
']': 'LISTS SUBSCRIPTS SLICINGS',
2014+
19292015
}
19302016
for topic, symbols_ in _symbols_inverse.items():
19312017
for symbol in symbols_:
@@ -2023,9 +2109,13 @@ class Helper:
20232109
'CONTEXTMANAGERS': ('context-managers', 'with'),
20242110
'DUNDERMETHODS': 'SPECIALMETHODS',
20252111
'MAINMODULE': '__main__',
2026-
'__main__': ('__main__', ''),
20272112
}
20282113

2114+
# add dunder methods
2115+
dunders = collect_dunders(symbols)
2116+
topics |= dunders
2117+
2118+
20292119
def __init__(self, input=None, output=None):
20302120
self._input = input
20312121
self._output = output
@@ -2100,6 +2190,8 @@ def help(self, request, is_cli=False):
21002190
elif request == 'keywords': self.listkeywords()
21012191
elif request == 'symbols': self.listsymbols()
21022192
elif request == 'topics': self.listtopics()
2193+
elif request in {'specialnames', 'dunders'}:
2194+
self.listdunders()
21032195
elif request == 'modules': self.listmodules()
21042196
elif request[:8] == 'modules ':
21052197
self.listmodules(request.split()[1])
@@ -2174,7 +2266,14 @@ def listtopics(self):
21742266
Here is a list of available topics. Enter any topic name to get more help.
21752267
21762268
''')
2177-
self.list([k for k in self.topics.keys() if k.isupper()], columns=3)
2269+
self.list([k for k in self.topics if k not in self.dunders], columns=3)
2270+
2271+
def listdunders(self):
2272+
self.output.write('''
2273+
Here is a list of special names for which help is available. Enter any one to get more help.
2274+
2275+
''')
2276+
self.list(self.dunders.keys(), columns=3)
21782277

21792278
def showtopic(self, topic, more_xrefs=''):
21802279
try:

Lib/pydoc_data/topics.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Autogenerated by Sphinx on Sat Aug 16 17:38:04 2025
1+
# Autogenerated by Sphinx on Sat Aug 16 20:05:29 2025
22
# as part of the release process.
33

44
topics = {
@@ -4083,6 +4083,43 @@ def f() -> annotation: ...
40834083
Some additional rules apply for certain operators (e.g., a string as a
40844084
left argument to the ‘%’ operator). Extensions must define their own
40854085
conversion behavior.
4086+
''',
4087+
'custom-instance-subclass': r'''Customizing instance and subclass checks
4088+
****************************************
4089+
4090+
The following methods are used to override the default behavior of the
4091+
"isinstance()" and "issubclass()" built-in functions.
4092+
4093+
In particular, the metaclass "abc.ABCMeta" implements these methods in
4094+
order to allow the addition of Abstract Base Classes (ABCs) as
4095+
“virtual base classes” to any class or type (including built-in
4096+
types), including other ABCs.
4097+
4098+
type.__instancecheck__(self, instance)
4099+
4100+
Return true if *instance* should be considered a (direct or
4101+
indirect) instance of *class*. If defined, called to implement
4102+
"isinstance(instance, class)".
4103+
4104+
type.__subclasscheck__(self, subclass)
4105+
4106+
Return true if *subclass* should be considered a (direct or
4107+
indirect) subclass of *class*. If defined, called to implement
4108+
"issubclass(subclass, class)".
4109+
4110+
Note that these methods are looked up on the type (metaclass) of a
4111+
class. They cannot be defined as class methods in the actual class.
4112+
This is consistent with the lookup of special methods that are called
4113+
on instances, only in this case the instance is itself a class.
4114+
4115+
See also:
4116+
4117+
**PEP 3119** - Introducing Abstract Base Classes
4118+
Includes the specification for customizing "isinstance()" and
4119+
"issubclass()" behavior through "__instancecheck__()" and
4120+
"__subclasscheck__()", with motivation for this functionality in
4121+
the context of adding Abstract Base Classes (see the "abc"
4122+
module) to the language.
40864123
''',
40874124
'customization': r'''Basic customization
40884125
*******************

0 commit comments

Comments
 (0)