[Python-3000-checkins] r54043 - python/branches/p3yk/Lib/inspect.py python/branches/p3yk/Lib/pydoc.py

guido.van.rossum python-3000-checkins at python.org
Wed Feb 28 23:03:55 CET 2007


Author: guido.van.rossum
Date: Wed Feb 28 23:03:49 2007
New Revision: 54043

Modified:
   python/branches/p3yk/Lib/inspect.py
   python/branches/p3yk/Lib/pydoc.py
Log:
Final part of SF# 1607548 by Tony Lownds: fix pydoc and inspect.


Modified: python/branches/p3yk/Lib/inspect.py
==============================================================================
--- python/branches/p3yk/Lib/inspect.py	(original)
+++ python/branches/p3yk/Lib/inspect.py	Wed Feb 28 23:03:49 2007
@@ -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,9 +673,20 @@
 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')
@@ -682,7 +694,9 @@
     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.__code__)
-    return args, varargs, varkw, func.__defaults__
+    args, varargs, kwonlyargs, varkw = _getfullargs(func.__code__)
+    return (args, varargs, varkw, func.__defaults__, 
+            kwonlyargs, func.__kwdefaults__, 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,

Modified: python/branches/p3yk/Lib/pydoc.py
==============================================================================
--- python/branches/p3yk/Lib/pydoc.py	(original)
+++ python/branches/p3yk/Lib/pydoc.py	Wed Feb 28 23:03:49 2007
@@ -875,11 +875,17 @@
             title = '<a name="%s"><strong>%s</strong></a> = %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 == '<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
         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 == '<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
         else:
             argspec = '(...)'


More information about the Python-3000-checkins mailing list