Commit 2e65f892 authored by Guido van Rossum's avatar Guido van Rossum

Final part of SF# 1607548 by Tony Lownds: fix pydoc and inspect.

parent 4b7f3179
...@@ -17,6 +17,7 @@ Here are some of the useful functions provided by this module: ...@@ -17,6 +17,7 @@ Here are some of the useful functions provided by this module:
getclasstree() - arrange classes so as to represent their hierarchy getclasstree() - arrange classes so as to represent their hierarchy
getargspec(), getargvalues() - get info about function arguments getargspec(), getargvalues() - get info about function arguments
getfullargspec() - same, with support for Python-3000 features
formatargspec(), formatargvalues() - format an argument spec formatargspec(), formatargvalues() - format an argument spec
getouterframes(), getinnerframes() - get info about frames getouterframes(), getinnerframes() - get info about frames
currentframe() - get the current stack frame currentframe() - get the current stack frame
...@@ -672,9 +673,20 @@ CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8 ...@@ -672,9 +673,20 @@ CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8
def getargs(co): def getargs(co):
"""Get information about the arguments accepted by a code object. """Get information about the arguments accepted by a code object.
Three things are returned: (args, varargs, varkw), where 'args' is Three things are returned: (args, varargs, varkw), where
a list of argument names (possibly containing nested lists), and 'args' is the list of argument names, possibly containing nested
'varargs' and 'varkw' are the names of the * and ** arguments or None.""" lists. Keyword-only arguments are appended. 'varargs' and 'varkw'
are the names of the * and ** arguments or None."""
args, varargs, kwonlyargs, varkw = _getfullargs(co)
return args + kwonlyargs, varargs, varkw
def _getfullargs(co):
"""Get information about the arguments accepted by a code object.
Four things are returned: (args, varargs, kwonlyargs, varkw), where
'args' and 'kwonlyargs' are lists of argument names (with 'args'
possibly containing nested lists), and 'varargs' and 'varkw' are the
names of the * and ** arguments or None."""
if not iscode(co): if not iscode(co):
raise TypeError('arg is not a code object') raise TypeError('arg is not a code object')
...@@ -682,7 +694,9 @@ def getargs(co): ...@@ -682,7 +694,9 @@ def getargs(co):
code = co.co_code code = co.co_code
nargs = co.co_argcount nargs = co.co_argcount
names = co.co_varnames names = co.co_varnames
nkwargs = co.co_kwonlyargcount
args = list(names[:nargs]) args = list(names[:nargs])
kwonlyargs = list(names[nargs:nargs+nkwargs])
step = 0 step = 0
# The following acrobatics are for anonymous (tuple) arguments. # The following acrobatics are for anonymous (tuple) arguments.
...@@ -719,6 +733,7 @@ def getargs(co): ...@@ -719,6 +733,7 @@ def getargs(co):
if not remain: break if not remain: break
args[i] = stack[0] args[i] = stack[0]
nargs += nkwargs
varargs = None varargs = None
if co.co_flags & CO_VARARGS: if co.co_flags & CO_VARARGS:
varargs = co.co_varnames[nargs] varargs = co.co_varnames[nargs]
...@@ -726,23 +741,51 @@ def getargs(co): ...@@ -726,23 +741,51 @@ def getargs(co):
varkw = None varkw = None
if co.co_flags & CO_VARKEYWORDS: if co.co_flags & CO_VARKEYWORDS:
varkw = co.co_varnames[nargs] varkw = co.co_varnames[nargs]
return args, varargs, varkw return args, varargs, kwonlyargs, varkw
def getargspec(func): def getargspec(func):
"""Get the names and default values of a function's arguments. """Get the names and default values of a function's arguments.
A tuple of four things is returned: (args, varargs, varkw, defaults). A tuple of four things is returned: (args, varargs, varkw, defaults).
'args' is a list of the argument names (it may contain nested lists). 'args' is a list of the argument names (it may contain nested lists).
'args' will include keyword-only argument names.
'varargs' and 'varkw' are the names of the * and ** arguments or None.
'defaults' is an n-tuple of the default values of the last n arguments.
Use the getfullargspec() API for Python-3000 code, as annotations
and keyword arguments are supported. getargspec() will raise ValueError
if the func has either annotations or keyword arguments.
"""
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
getfullargspec(func)
if kwonlyargs or ann:
raise ValueError, ("Function has keyword-only arguments or annotations"
", use getfullargspec() API which can support them")
return (args, varargs, varkw, defaults)
def getfullargspec(func):
"""Get the names and default values of a function's arguments.
A tuple of seven things is returned: (args, varargs, kwonlyargs,
kwonlydefaults, varkw, defaults, annotations).
'args' is a list of the argument names (it may contain nested lists).
'varargs' and 'varkw' are the names of the * and ** arguments or None. 'varargs' and 'varkw' are the names of the * and ** arguments or None.
'defaults' is an n-tuple of the default values of the last n arguments. 'defaults' is an n-tuple of the default values of the last n arguments.
'kwonlyargs' is a list of keyword-only argument names.
'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults.
'annotations' is a dictionary mapping argument names to annotations.
The first four items in the tuple correspond to getargspec().
""" """
if ismethod(func): if ismethod(func):
func = func.im_func func = func.im_func
if not isfunction(func): if not isfunction(func):
raise TypeError('arg is not a Python function') raise TypeError('arg is not a Python function')
args, varargs, varkw = getargs(func.__code__) args, varargs, kwonlyargs, varkw = _getfullargs(func.__code__)
return args, varargs, varkw, func.__defaults__ return (args, varargs, varkw, func.__defaults__,
kwonlyargs, func.__kwdefaults__, func.__annotations__)
def getargvalues(frame): def getargvalues(frame):
"""Get information about arguments passed into a particular frame. """Get information about arguments passed into a particular frame.
...@@ -767,31 +810,66 @@ def strseq(object, convert, join=joinseq): ...@@ -767,31 +810,66 @@ def strseq(object, convert, join=joinseq):
else: else:
return convert(object) return convert(object)
def formatannotation(annotation, base_module=None):
if isinstance(annotation, type):
if annotation.__module__ in ('__builtin__', base_module):
return annotation.__name__
return annotation.__module__+'.'+annotation.__name__
return repr(annotation)
def formatannotationrelativeto(object):
module = getattr(object, '__module__', None)
def _formatannotation(annotation):
return formatannotation(annotation, module)
return _formatannotation
def formatargspec(args, varargs=None, varkw=None, defaults=None, def formatargspec(args, varargs=None, varkw=None, defaults=None,
kwonlyargs=(), kwonlydefaults={}, annotations={},
formatarg=str, formatarg=str,
formatvarargs=lambda name: '*' + name, formatvarargs=lambda name: '*' + name,
formatvarkw=lambda name: '**' + name, formatvarkw=lambda name: '**' + name,
formatvalue=lambda value: '=' + repr(value), formatvalue=lambda value: '=' + repr(value),
formatreturns=lambda text: ' -> ' + text,
formatannotation=formatannotation,
join=joinseq): join=joinseq):
"""Format an argument spec from the 4 values returned by getargspec. """Format an argument spec from the values returned by getargspec
or getfullargspec.
The first four arguments are (args, varargs, varkw, defaults). The
other four arguments are the corresponding optional formatting functions The first seven arguments are (args, varargs, varkw, defaults,
that are called to turn names and values into strings. The ninth kwonlyargs, kwonlydefaults, annotations). The other five arguments
argument is an optional function to format the sequence of arguments.""" are the corresponding optional formatting functions that are called to
turn names and values into strings. The last argument is an optional
function to format the sequence of arguments."""
def formatargandannotation(arg):
result = formatarg(arg)
if arg in annotations:
result += ': ' + formatannotation(annotations[arg])
return result
specs = [] specs = []
if defaults: if defaults:
firstdefault = len(args) - len(defaults) firstdefault = len(args) - len(defaults)
for i in range(len(args)): for i in range(len(args)):
spec = strseq(args[i], formatarg, join) spec = strseq(args[i], formatargandannotation, join)
if defaults and i >= firstdefault: if defaults and i >= firstdefault:
spec = spec + formatvalue(defaults[i - firstdefault]) spec = spec + formatvalue(defaults[i - firstdefault])
specs.append(spec) specs.append(spec)
if varargs is not None: if varargs is not None:
specs.append(formatvarargs(varargs)) specs.append(formatvarargs(formatargandannotation(varargs)))
else:
if kwonlyargs:
specs.append('*')
if kwonlyargs:
for kwonlyarg in kwonlyargs:
spec = formatargandannotation(kwonlyarg)
if kwonlyarg in kwonlydefaults:
spec += formatvalue(kwonlydefaults[kwonlyarg])
specs.append(spec)
if varkw is not None: if varkw is not None:
specs.append(formatvarkw(varkw)) specs.append(formatvarkw(formatargandannotation(varkw)))
return '(' + string.join(specs, ', ') + ')' result = '(' + string.join(specs, ', ') + ')'
if 'return' in annotations:
result += formatreturns(formatannotation(annotations['return']))
return result
def formatargvalues(args, varargs, varkw, locals, def formatargvalues(args, varargs, varkw, locals,
formatarg=str, formatarg=str,
......
...@@ -875,11 +875,17 @@ class HTMLDoc(Doc): ...@@ -875,11 +875,17 @@ class HTMLDoc(Doc):
title = '<a name="%s"><strong>%s</strong></a> = %s' % ( title = '<a name="%s"><strong>%s</strong></a> = %s' % (
anchor, name, reallink) anchor, name, reallink)
if inspect.isfunction(object): if inspect.isfunction(object):
args, varargs, varkw, defaults = inspect.getargspec(object) args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann = \
inspect.getfullargspec(object)
argspec = inspect.formatargspec( argspec = inspect.formatargspec(
args, varargs, varkw, defaults, formatvalue=self.formatvalue) args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann,
formatvalue=self.formatvalue,
formatannotation=inspect.formatannotationrelativeto(object))
if realname == '<lambda>': if realname == '<lambda>':
title = '<strong>%s</strong> <em>lambda</em> ' % name 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 = argspec[1:-1] # remove parentheses
else: else:
argspec = '(...)' argspec = '(...)'
...@@ -1241,11 +1247,17 @@ class TextDoc(Doc): ...@@ -1241,11 +1247,17 @@ class TextDoc(Doc):
skipdocs = 1 skipdocs = 1
title = self.bold(name) + ' = ' + realname title = self.bold(name) + ' = ' + realname
if inspect.isfunction(object): if inspect.isfunction(object):
args, varargs, varkw, defaults = inspect.getargspec(object) args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann = \
inspect.getfullargspec(object)
argspec = inspect.formatargspec( argspec = inspect.formatargspec(
args, varargs, varkw, defaults, formatvalue=self.formatvalue) args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann,
formatvalue=self.formatvalue,
formatannotation=inspect.formatannotationrelativeto(object))
if realname == '<lambda>': if realname == '<lambda>':
title = self.bold(name) + ' 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 = argspec[1:-1] # remove parentheses
else: else:
argspec = '(...)' argspec = '(...)'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment