8000 gh-107782: Pydoc: fall back to __text_signature__ if inspect.signatur… · serhiy-storchaka/cpython@d92e675 · GitHub
[go: up one dir, main page]

Skip to content

Commit d92e675

Browse files
pythongh-107782: Pydoc: fall back to __text_signature__ if inspect.signature() fails
It allows to show signatures which are not representable in Python, e.g. for getattr and dict.pop.
1 parent 906b73b commit d92e675

File tree

3 files changed

+62
-40
lines changed

3 files changed

+62
-40
lines changed

Lib/pydoc.py

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,29 @@ def splitdoc(doc):
197197
return lines[0], '\n'.join(lines[2:])
198198
return '', '\n'.join(lines)
199199

200+
def _getargspec(object):
201+
try:
202+
signature = inspect.signature(object)
203+
if signature:
204+
return str(signature)
205+
except (ValueError, TypeError):
206+
argspec = getattr(object, '__text_signature__', None)
207+
if argspec:
208+
if argspec == '($module)':
209+
argspec = '()'
210+
elif argspec[:10] == '($module, ':
211+
argspec = '(' + argspec[10:]
212+
elif argspec == '($self)' and hasattr(object, '__self__'):
213+
argspec = '()'
214+
elif argspec[:8] == '($self, ' and hasattr(object, '__self__'):
215+
argspec = '(' + argspec[8:]
216+
elif argspec[:2] == '($':
217+
argspec = '(' + argspec[2:]
218+
if argspec[:4] == '(/, ':
219+
argspec = '(' + argspec[4:]
220+
return argspec
221+
return None
222+
200223
def classname(object, modname):
201224
"""Get a class name and qualify it with a module name if necessary."""
202225
name = object.__name__
@@ -1003,14 +1026,9 @@ def spilldata(msg, attrs, predicate):
10031026
title = title + '(%s)' % ', '.join(parents)
10041027

10051028
decl = ''
1006-
try:
1007-
signature = inspect.signature(object)
1008-
except (ValueError, TypeError):
1009-
signature = None
1010-
if signature:
1011-
argspec = str(signature)
1012-
if argspec and argspec != '()':
1013-
decl = name + self.escape(argspec) + '\n\n'
1029+
argspec = _getargspec(object)
1030+
if argspec and argspec != '()':
1031+
decl = name + self.escape(argspec) + '\n\n'
10141032

10151033
doc = getdoc(object)
10161034
if decl:
@@ -1063,18 +1081,13 @@ def docroutine(self, object, name=None, mod=None,
10631081
anchor, name, reallink)
10641082
argspec = None
10651083
if inspect.isroutine(object):
1066-
try:
1067-
signature = inspect.signature(object)
1068-
except (ValueError, TypeError):
1069-
signature = None
1070-
if signature:
1071-
argspec = str(signature)
1072-
if realname == '<lambda>':
1073-
title = '<strong>%s</strong> <em>lambda</em> ' % name
1074-
# XXX lambda's won't usually have func_annotations['return']
1075-
# since the syntax doesn't support but it is possible.
1076-
# So removing parentheses isn't truly safe.
1077-
argspec = argspec[1:-1] # remove parentheses
1084+
argspec = _getargspec(object)
1085+
if argspec and realname == '<lambda>':
1086+
title = '<strong>%s</strong> <em>lambda</em> ' % name
1087+
# XXX lambda's won't usually have func_annotations['return']
1088+
# since the syntax doesn't support but it is possible.
1089+
# So removing parentheses isn't truly safe.
1090+
argspec = argspec[1:-1] # remove parentheses
10781091
if not argspec:
10791092
argspec = '(...)'
10801093

@@ -1321,14 +1334,9 @@ def makename(c, m=object.__module__):
13211334
contents = []
13221335
push = contents.append
13231336

1324-
try:
1325-
signature = inspect.signature(object)
1326-
except (ValueError, TypeError):
1327-
signature = None
1328-
if signature:
1329-
argspec = str(signature)
1330-
if argspec and argspec != '()':
1331-
push(name + argspec + '\n')
1337+
argspec = _getargspec(object)
1338+
if argspec and argspec != '()':
1339+
push(name + argspec + '\n')
13321340

13331341
doc = getdoc(object)
13341342
if doc:
@@ -1492,18 +1500,13 @@ def docroutine(self, object, name=None, mod=None, cl=None):
14921500
argspec = None
14931501

14941502
if inspect.isroutine(object):
1495-
try:
1496-
signature = inspect.signature(object)
1497-
except (ValueError, TypeError):
1498-
signature = None
1499-
if signature:
1500-
argspec = str(signature)
1501-
if realname == '<lambda>':
1502-
title = self.bold(name) + ' lambda '
1503-
# XXX lambda's won't usually have func_annotations['return']
1504-
# since the syntax doesn't support but it is possible.
1505-
# So removing parentheses isn't truly safe.
1506-
argspec = argspec[1:-1] # remove parentheses
1503+
argspec = _getargspec(object)
1504+
if argspec and realname == '<lambda>':
1505+
title = self.bold(name) + ' lambda '
1506+
# XXX lambda's won't usually have func_annotations['return']
1507+
# since the syntax doesn't support but it is possible.
1508+
# So removing parentheses isn't truly safe.
1509+
argspec = argspec[1:-1] # remove parentheses
15071510
if not argspec:
15081511
argspec = '(...)'
15091512
decl = asyncqualifier + title + argspec + note

Lib/test/test_pydoc.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,23 @@ def test_module_level_callable(self):
11801180
self.assertEqual(self._get_summary_line(os.stat),
11811181
"stat(path, *, dir_fd=None, follow_symlinks=True)")
11821182

1183+
def test_module_level_callable_unrepresentable_default(self):
1184+
self.assertEqual(self._get_summary_line(getattr),
1185+
"getattr(object, name, default=<unrepresentable>, /)")
1186+
1187+
def test_builtin_staticmethod_unrepresentable_default(self):
1188+
self.assertEqual(self._get_summary_line(str.maketrans),
1189+
"maketrans(x, y=<unrepresentable>, z=<unrepresentable>, /)")
1190+
1191+
def test_unbound_builtin_method_unrepresentable_default(self):
1192+
self.assertEqual(self._get_summary_line(dict.pop),
1193+
"pop(self, key, default=<unrepresentable>, /)")
1194+
1195+
def test_bound_builtin_method_unrepresentable_default(self):
1196+
self.assertEqual(self._get_summary_line({}.pop),
1197+
"pop(key, default=<unrepresentable>, /) "
1198+
"method of builtins.dict instance")
1199+
11831200
@requires_docstrings
11841201
def test_staticmethod(self):
11851202
class X:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`pydoc` is now able to show signatures which are not representable in
2+
Python, e.g. for ``getattr`` and ``dict.pop``.

0 commit comments

Comments
 (0)
0