return lines[0], '\n'.join(lines[2:])
return '', '\n'.join(lines)
+def _getargspec(object):
+ try:
+ signature = inspect.signature(object)
+ if signature:
+ return str(signature)
+ except (ValueError, TypeError):
+ argspec = getattr(object, '__text_signature__', None)
+ if argspec:
+ if argspec[:2] == '($':
+ argspec = '(' + argspec[2:]
+ if getattr(object, '__self__', None) is not None:
+ # Strip the bound argument.
+ m = re.match(r'\(\w+(?:(?=\))|,\s*(?:/(?:(?=\))|,\s*))?)', argspec)
+ if m:
+ argspec = '(' + argspec[m.end():]
+ return argspec
+ return None
+
def classname(object, modname):
"""Get a class name and qualify it with a module name if necessary."""
name = object.__name__
title = title + '(%s)' % ', '.join(parents)
decl = ''
- try:
- signature = inspect.signature(object)
- except (ValueError, TypeError):
- signature = None
- if signature:
- argspec = str(signature)
- if argspec and argspec != '()':
- decl = name + self.escape(argspec) + '\n\n'
+ argspec = _getargspec(object)
+ if argspec and argspec != '()':
+ decl = name + self.escape(argspec) + '\n\n'
doc = getdoc(object)
if decl:
anchor, name, reallink)
argspec = None
if inspect.isroutine(object):
- try:
- signature = inspect.signature(object)
- except (ValueError, TypeError):
- signature = None
- if signature:
- argspec = str(signature)
- if realname == '<lambda>':
- title = '<strong>%s</strong> <em>lambda</em> ' % name
- # XXX lambda's won't usually have func_annotations['return']
- # since the syntax doesn't support but it is possible.
- # So removing parentheses isn't truly safe.
- argspec = argspec[1:-1] # remove parentheses
+ argspec = _getargspec(object)
+ if argspec and realname == '<lambda>':
+ title = '<strong>%s</strong> <em>lambda</em> ' % name
+ # XXX lambda's won't usually have func_annotations['return']
+ # since the syntax doesn't support but it is possible.
+ # So removing parentheses isn't truly safe.
+ argspec = argspec[1:-1] # remove parentheses
if not argspec:
argspec = '(...)'
contents = []
push = contents.append
- try:
- signature = inspect.signature(object)
- except (ValueError, TypeError):
- signature = None
- if signature:
- argspec = str(signature)
- if argspec and argspec != '()':
- push(name + argspec + '\n')
+ argspec = _getargspec(object)
+ if argspec and argspec != '()':
+ push(name + argspec + '\n')
doc = getdoc(object)
if doc:
argspec = None
if inspect.isroutine(object):
- try:
- signature = inspect.signature(object)
- except (ValueError, TypeError):
- signature = None
- if signature:
- argspec = str(signature)
- if realname == '<lambda>':
- title = self.bold(name) + ' lambda '
- # XXX lambda's won't usually have func_annotations['return']
- # since the syntax doesn't support but it is possible.
- # So removing parentheses isn't truly safe.
- argspec = argspec[1:-1] # remove parentheses
+ argspec = _getargspec(object)
+ if argspec and realname == '<lambda>':
+ title = self.bold(name) + ' lambda '
+ # XXX lambda's won't usually have func_annotations['return']
+ # since the syntax doesn't support but it is possible.
+ # So removing parentheses isn't truly safe.
+ argspec = argspec[1:-1] # remove parentheses
if not argspec:
argspec = '(...)'
decl = asyncqualifier + title + argspec + note
self.assertEqual(self._get_summary_line(dict.__class_getitem__),
"__class_getitem__(object, /) method of builtins.type instance")
+ def test_module_level_callable_unrepresentable_default(self):
+ self.assertEqual(self._get_summary_line(getattr),
+ "getattr(object, name, default=<unrepresentable>, /)")
+
+ def test_builtin_staticmethod_unrepresentable_default(self):
+ self.assertEqual(self._get_summary_line(str.maketrans),
+ "maketrans(x, y=<unrepresentable>, z=<unrepresentable>, /)")
+
+ def test_unbound_builtin_method_unrepresentable_default(self):
+ self.assertEqual(self._get_summary_line(dict.pop),
+ "pop(self, key, default=<unrepresentable>, /)")
+
+ def test_bound_builtin_method_unrepresentable_default(self):
+ self.assertEqual(self._get_summary_line({}.pop),
+ "pop(key, default=<unrepresentable>, /) "
+ "method of builtins.dict instance")
+
+ def test_overridden_text_signature(self):
+ class C:
+ def meth(*args, **kwargs):
+ pass
+ @classmethod
+ def cmeth(*args, **kwargs):
+ pass
+ @staticmethod
+ def smeth(*args, **kwargs):
+ pass
+ for text_signature, unbound, bound in [
+ ("($slf)", "(slf, /)", "()"),
+ ("($slf, /)", "(slf, /)", "()"),
+ ("($slf, /, arg)", "(slf, /, arg)", "(arg)"),
+ ("($slf, /, arg=<x>)", "(slf, /, arg=<x>)", "(arg=<x>)"),
+ ("($slf, arg, /)", "(slf, arg, /)", "(arg, /)"),
+ ("($slf, arg=<x>, /)", "(slf, arg=<x>, /)", "(arg=<x>, /)"),
+ ("(/, slf, arg)", "(/, slf, arg)", "(/, slf, arg)"),
+ ("(/, slf, arg=<x>)", "(/, slf, arg=<x>)", "(/, slf, arg=<x>)"),
+ ("(slf, /, arg)", "(slf, /, arg)", "(arg)"),
+ ("(slf, /, arg=<x>)", "(slf, /, arg=<x>)", "(arg=<x>)"),
+ ("(slf, arg, /)", "(slf, arg, /)", "(arg, /)"),
+ ("(slf, arg=<x>, /)", "(slf, arg=<x>, /)", "(arg=<x>, /)"),
+ ]:
+ with self.subTest(text_signature):
+ C.meth.__text_signature__ = text_signature
+ self.assertEqual(self._get_summary_line(C.meth),
+ "meth" + unbound)
+ self.assertEqual(self._get_summary_line(C().meth),
+ "meth" + bound + " method of test.test_pydoc.C instance")
+ C.cmeth.__func__.__text_signature__ = text_signature
+ self.assertEqual(self._get_summary_line(C.cmeth),
+ "cmeth" + bound + " method of builtins.type instance")
+ C.smeth.__text_signature__ = text_signature
+ self.assertEqual(self._get_summary_line(C.smeth),
+ "smeth" + unbound)
+
@requires_docstrings
def test_staticmethod(self):
class X: