Index: Lib/inspect.py =================================================================== --- Lib/inspect.py (revision 53289) +++ Lib/inspect.py (working copy) @@ -17,6 +17,7 @@ getclasstree() - arrange classes so as to represent their hierarchy getargspec(), getargvalues() - get info about function arguments + getfullargspec() - same, with support for Python-3000 features formatargspec(), formatargvalues() - format an argument spec getouterframes(), getinnerframes() - get info about frames currentframe() - get the current stack frame @@ -672,17 +673,30 @@ def getargs(co): """Get information about the arguments accepted by a code object. - Three things are returned: (args, varargs, varkw), where 'args' is - a list of argument names (possibly containing nested lists), and - 'varargs' and 'varkw' are the names of the * and ** arguments or None.""" + Three things are returned: (args, varargs, varkw), where + 'args' is the list of argument names, possibly containing nested + 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): raise TypeError('arg is not a code object') code = co.co_code nargs = co.co_argcount names = co.co_varnames + nkwargs = co.co_kwonlyargcount args = list(names[:nargs]) + kwonlyargs = list(names[nargs:nargs+nkwargs]) step = 0 # The following acrobatics are for anonymous (tuple) arguments. @@ -719,6 +733,7 @@ if not remain: break args[i] = stack[0] + nargs += nkwargs varargs = None if co.co_flags & CO_VARARGS: varargs = co.co_varnames[nargs] @@ -726,23 +741,51 @@ varkw = None if co.co_flags & CO_VARKEYWORDS: varkw = co.co_varnames[nargs] - return args, varargs, varkw + return args, varargs, kwonlyargs, varkw def getargspec(func): """Get the names and default values of a function's arguments. 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' 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. + '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): func = func.im_func if not isfunction(func): raise TypeError('arg is not a Python function') - args, varargs, varkw = getargs(func.func_code) - return args, varargs, varkw, func.func_defaults + args, varargs, kwonlyargs, varkw = _getfullargs(func.func_code) + return (args, varargs, varkw, func.func_defaults, + kwonlyargs, func.func_kwdefaults, func.func_annotations) def getargvalues(frame): """Get information about arguments passed into a particular frame. @@ -767,31 +810,66 @@ else: 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, + kwonlyargs=(), kwonlydefaults={}, annotations={}, formatarg=str, formatvarargs=lambda name: '*' + name, formatvarkw=lambda name: '**' + name, formatvalue=lambda value: '=' + repr(value), + formatreturns=lambda text: ' -> ' + text, + formatannotation=formatannotation, 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 - that are called to turn names and values into strings. The ninth - argument is an optional function to format the sequence of arguments.""" + The first seven arguments are (args, varargs, varkw, defaults, + kwonlyargs, kwonlydefaults, annotations). The other five 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 = [] if defaults: firstdefault = len(args) - len(defaults) for i in range(len(args)): - spec = strseq(args[i], formatarg, join) + spec = strseq(args[i], formatargandannotation, join) if defaults and i >= firstdefault: spec = spec + formatvalue(defaults[i - firstdefault]) specs.append(spec) 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: - specs.append(formatvarkw(varkw)) - return '(' + string.join(specs, ', ') + ')' + specs.append(formatvarkw(formatargandannotation(varkw))) + result = '(' + string.join(specs, ', ') + ')' + if 'return' in annotations: + result += formatreturns(formatannotation(annotations['return'])) + return result def formatargvalues(args, varargs, varkw, locals, formatarg=str, Index: Lib/pydoc.py =================================================================== --- Lib/pydoc.py (revision 53289) +++ Lib/pydoc.py (working copy) @@ -875,11 +875,17 @@ title = '%s = %s' % ( anchor, name, reallink) if inspect.isfunction(object): - args, varargs, varkw, defaults = inspect.getargspec(object) + args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann = \ + inspect.getfullargspec(object) 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 == '': title = '%s lambda ' % 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 else: argspec = '(...)' @@ -1241,11 +1247,17 @@ skipdocs = 1 title = self.bold(name) + ' = ' + realname if inspect.isfunction(object): - args, varargs, varkw, defaults = inspect.getargspec(object) + args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann = \ + inspect.getfullargspec(object) 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 == '': 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 else: argspec = '(...)'