Commits

Andreas Stührk committed ddd5e40

Move argspec inspection code to a new module.

Comments (0)

Files changed (2)

 import string
 import socket
 import pydoc
-import types
 import unicodedata
 import textwrap
 from cStringIO import StringIO
 from bpython.formatter import BPythonFormatter, Parenthesis
 
 # This for completion
+from bpython import inspection
 from bpython import importcompletion
 from glob import glob
 
     return lines
 
 
-def parsekeywordpairs(signature):
-    tokens = PythonLexer().get_tokens(signature)
-    stack = []
-    substack = []
-    parendepth = 0
-    begin = False
-    for token, value in tokens:
-        if not begin:
-            if token is Token.Punctuation and value == u'(':
-                begin = True
-            continue
-
-        if token is Token.Punctuation:
-            if value == u'(':
-                parendepth += 1
-            elif value == u')':
-                parendepth -= 1
-            elif value == ':' and parendepth == -1:
-                # End of signature reached
-                break
-
-        if parendepth > 0:
-            substack.append(value)
-            continue
-
-        if (token is Token.Punctuation and
-            (value == ',' or (value == ')' and parendepth == -1))):
-            stack.append(substack[:])
-            del substack[:]
-            continue
-
-        if value and value.strip():
-            substack.append(value)
-
-    d = {}
-    for item in stack:
-        if len(item) >= 3:
-            d[item[0]] = ''.join(item[2:])
-    return d
-
-def fixlongargs(f, argspec):
-    """Functions taking default arguments that are references to other objects
-    whose str() is too big will cause breakage, so we swap out the object
-    itself with the name it was referenced with in the source by parsing the
-    source itself !"""
-    if argspec[3] is None:
-        # No keyword args, no need to do anything
-        return
-    values = list(argspec[3])
-    if not values:
-        return
-    keys = argspec[0][-len(values):]
-    try:
-        src = inspect.getsourcelines(f)
-    except IOError:
-        return
-    signature = ''.join(src[0])
-    kwparsed = parsekeywordpairs(signature)
-
-    for i, (key, value) in enumerate(zip(keys, values)):
-        if len(str(value)) != len(kwparsed[key]):
-            values[i] = kwparsed[key]
-
-    argspec[3] = values
-
 class FakeStdin(object):
     """Provide a fake stdin type for things like raw_input() etc."""
 
         fancy."""
         map(self.write, ["\x01%s\x03%s" % (OPTS.color_scheme['error'], i) for i in l])
 
-class AttrCleaner(object):
-    """A context manager that tries to make an object not exhibit side-effects
-       on attribute lookup."""
-    def __init__(self, obj):
-        self.obj = obj
-
-    def __enter__(self):
-        """Try to make an object not exhibit side-effects on attribute
-        lookup.""" 
-        type_ = type(self.obj)
-        __getattribute__ = None
-        __getattr__ = None
-        # Dark magic:
-        # If __getattribute__ doesn't exist on the class and __getattr__ does
-        # then __getattr__ will be called when doing
-        #   getattr(type_, '__getattribute__', None)
-        # so we need to first remove the __getattr__, then the
-        # __getattribute__, then look up the attributes and then restore the
-        # original methods. :-(
-        # The upshot being that introspecting on an object to display its
-        # attributes will avoid unwanted side-effects.
-        if py3 or type_ != types.InstanceType:
-            __getattr__ = getattr(type_, '__getattr__', None)
-            if __getattr__ is not None:
-                try:
-                    setattr(type_, '__getattr__', (lambda _: None))
-                except TypeError:
-                    __getattr__ = None
-            __getattribute__ = getattr(type_, '__getattribute__', None)
-            if __getattribute__ is not None:
-                try:
-                    setattr(type_, '__getattribute__', object.__getattribute__)
-                except TypeError:
-                    # XXX: This happens for e.g. built-in types
-                    __getattribute__ = None
-        self.attribs = (__getattribute__, __getattr__)
-        # /Dark magic
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        """Restore an object's magic methods."""
-        type_ = type(self.obj)
-        __getattribute__, __getattr__ = self.attribs
-        # Dark magic:
-        if __getattribute__ is not None:
-            setattr(type_, '__getattribute__', __getattribute__)
-        if __getattr__ is not None:
-            setattr(type_, '__getattr__', __getattr__)
-        # /Dark magic
 
 class Repl(object):
     """Implements the necessary guff for a Python-repl-alike interface
             # a SyntaxError
             return []
         obj = eval(expr, self.interp.locals)
-        with AttrCleaner(obj):
+        with inspection.AttrCleaner(obj):
             matches = self.attr_lookup(obj, expr, attr)
         return matches
 
 
     def _callable_postfix(self, value, word):
         """rlcompleter's _callable_postfix done right."""
-        with AttrCleaner(value):
+        with inspection.AttrCleaner(value):
             if hasattr(value, '__call__'):
                 word += '('
         return word
 
         self.current_func = None
 
-        def getpydocspec(f, func):
-            try:
-                argspec = pydoc.getdoc(f)
-            except NameError:
-                return None
-
-            rx = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*?)\((.*?)\)')
-            s = rx.search(argspec)
-            if s is None:
-                return None
-
-            if not hasattr(f, '__name__') or s.groups()[0] != f.__name__:
-                return None
-
-            args = list()
-            defaults = list()
-            varargs = varkwargs = None
-            kwonly_args = list()
-            kwonly_defaults = dict()
-            for arg in s.group(2).split(','):
-                arg = arg.strip()
-                if arg.startswith('**'):
-                    varkwargs = arg[2:]
-                elif arg.startswith('*'):
-                    varargs = arg[1:]
-                else:
-                    arg, _, default = arg.partition('=')
-                    if varargs is not None:
-                        kwonly_args.append(arg)
-                        if default:
-                            kwonly_defaults[arg] = default
-                    else:
-                        args.append(arg)
-                        if default:
-                            defaults.append(default)
-
-            return [func, (args, varargs, varkwargs, defaults,
-                           kwonly_args, kwonly_defaults)]
-
-        def getargspec(func):
-            try:
-                if func in self.interp.locals:
-                    f = self.interp.locals[func]
-            except TypeError:
-                return None
-            else:
-                try:
-                    f = eval(func, self.interp.locals)
-                except Exception:
-# Same deal with the exceptions :(
-                    return None
-                else:
-                    self.current_func = f
-
-            is_bound_method = inspect.ismethod(f) and f.im_self is not None
-            try:
-                if inspect.isclass(f):
-                    self.current_func = f
-                    if py3:
-                        argspec = inspect.getfullargspec(f.__init__)
-                    else:
-                        argspec = inspect.getargspec(f.__init__)
-                    self.current_func = f.__init__
-                    is_bound_method = True
-                else:
-                    if py3:
-                        argspec = inspect.getfullargspec(f)
-                    else:
-                        argspec = inspect.getargspec(f)
-                    self.current_func = f
-                argspec = list(argspec)
-                fixlongargs(f, argspec)
-                self.argspec = [func, argspec, is_bound_method]
-                return True
-
-            except (NameError, TypeError, KeyError):
-                with AttrCleaner(f):
-                    t = getpydocspec(f, func)
-                if t is None:
-                    return None
-                self.argspec = t
-                if inspect.ismethoddescriptor(f):
-                    self.argspec[1][0].insert(0, 'obj')
-                self.argspec.append(is_bound_method)
-                return True
-            except AttributeError:
-# This happens if no __init__ is found
-                return None
-
         if not OPTS.arg_spec:
             return False
 
+        # Find the name of the current function
         stack = [['', 0, '']]
         try:
             for (token, value) in PythonLexer().get_tokens(self.s):
         except IndexError:
             return False
 
-        if getargspec(func):
+        # We found a name, now get a function object
+        try:
+            if func in self.interp.locals:
+                f = self.interp.locals[func]
+        except TypeError:
+            return None
+        else:
+            try:
+                f = eval(func, self.interp.locals)
+            except Exception:
+                # Same deal with the exceptions :(
+                return None
+        if inspect.isclass(f):
+            try:
+                f = f.__init__
+            except AttributeError:
+                return None
+        self.current_func = f
+
+        self.argspec = inspection.getargspec(func, f)
+        if self.argspec:
             self.argspec.append(arg_number)
             return True
         return False

bpython/inspection.py

+# The MIT License
+#
+# Copyright (c) 2009 the bpython authors.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+
+import inspect
+import pydoc
+import re
+import sys
+import types
+
+from pygments.lexers import PythonLexer
+from pygments.token import Token
+
+
+py3 = sys.version_info[0] == 3
+
+
+class AttrCleaner(object):
+    """A context manager that tries to make an object not exhibit side-effects
+       on attribute lookup."""
+    def __init__(self, obj):
+        self.obj = obj
+
+    def __enter__(self):
+        """Try to make an object not exhibit side-effects on attribute
+        lookup.""" 
+        type_ = type(self.obj)
+        __getattribute__ = None
+        __getattr__ = None
+        # Dark magic:
+        # If __getattribute__ doesn't exist on the class and __getattr__ does
+        # then __getattr__ will be called when doing
+        #   getattr(type_, '__getattribute__', None)
+        # so we need to first remove the __getattr__, then the
+        # __getattribute__, then look up the attributes and then restore the
+        # original methods. :-(
+        # The upshot being that introspecting on an object to display its
+        # attributes will avoid unwanted side-effects.
+        if py3 or type_ != types.InstanceType:
+            __getattr__ = getattr(type_, '__getattr__', None)
+            if __getattr__ is not None:
+                try:
+                    setattr(type_, '__getattr__', (lambda _: None))
+                except TypeError:
+                    __getattr__ = None
+            __getattribute__ = getattr(type_, '__getattribute__', None)
+            if __getattribute__ is not None:
+                try:
+                    setattr(type_, '__getattribute__', object.__getattribute__)
+                except TypeError:
+                    # XXX: This happens for e.g. built-in types
+                    __getattribute__ = None
+        self.attribs = (__getattribute__, __getattr__)
+        # /Dark magic
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        """Restore an object's magic methods."""
+        type_ = type(self.obj)
+        __getattribute__, __getattr__ = self.attribs
+        # Dark magic:
+        if __getattribute__ is not None:
+            setattr(type_, '__getattribute__', __getattribute__)
+        if __getattr__ is not None:
+            setattr(type_, '__getattr__', __getattr__)
+        # /Dark magic
+
+
+def parsekeywordpairs(signature):
+    tokens = PythonLexer().get_tokens(signature)
+    stack = []
+    substack = []
+    parendepth = 0
+    begin = False
+    for token, value in tokens:
+        if not begin:
+            if token is Token.Punctuation and value == u'(':
+                begin = True
+            continue
+
+        if token is Token.Punctuation:
+            if value == u'(':
+                parendepth += 1
+            elif value == u')':
+                parendepth -= 1
+            elif value == ':' and parendepth == -1:
+                # End of signature reached
+                break
+
+        if parendepth > 0:
+            substack.append(value)
+            continue
+
+        if (token is Token.Punctuation and
+            (value == ',' or (value == ')' and parendepth == -1))):
+            stack.append(substack[:])
+            del substack[:]
+            continue
+
+        if value and value.strip():
+            substack.append(value)
+
+    d = {}
+    for item in stack:
+        if len(item) >= 3:
+            d[item[0]] = ''.join(item[2:])
+    return d
+
+def fixlongargs(f, argspec):
+    """Functions taking default arguments that are references to other objects
+    whose str() is too big will cause breakage, so we swap out the object
+    itself with the name it was referenced with in the source by parsing the
+    source itself !"""
+    if argspec[3] is None:
+        # No keyword args, no need to do anything
+        return
+    values = list(argspec[3])
+    if not values:
+        return
+    keys = argspec[0][-len(values):]
+    try:
+        src = inspect.getsourcelines(f)
+    except IOError:
+        return
+    signature = ''.join(src[0])
+    kwparsed = parsekeywordpairs(signature)
+
+    for i, (key, value) in enumerate(zip(keys, values)):
+        if len(str(value)) != len(kwparsed[key]):
+            values[i] = kwparsed[key]
+
+    argspec[3] = values
+
+
+def getpydocspec(f, func):
+    try:
+        argspec = pydoc.getdoc(f)
+    except NameError:
+        return None
+
+    rx = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*?)\((.*?)\)')
+    s = rx.search(argspec)
+    if s is None:
+        return None
+
+    if not hasattr(f, '__name__') or s.groups()[0] != f.__name__:
+        return None
+
+    args = list()
+    defaults = list()
+    varargs = varkwargs = None
+    kwonly_args = list()
+    kwonly_defaults = dict()
+    for arg in s.group(2).split(','):
+        arg = arg.strip()
+        if arg.startswith('**'):
+            varkwargs = arg[2:]
+        elif arg.startswith('*'):
+            varargs = arg[1:]
+        else:
+            arg, _, default = arg.partition('=')
+            if varargs is not None:
+                kwonly_args.append(arg)
+                if default:
+                    kwonly_defaults[arg] = default
+            else:
+                args.append(arg)
+                if default:
+                    defaults.append(default)
+
+    return [func, (args, varargs, varkwargs, defaults,
+                   kwonly_args, kwonly_defaults)]
+
+
+def getargspec(func, f):
+    is_bound_method = inspect.ismethod(f) and f.im_self is not None
+    try:
+        if py3:
+            argspec = inspect.getfullargspec(f)
+        else:
+            argspec = inspect.getargspec(f)
+                 
+        argspec = list(argspec)
+        fixlongargs(f, argspec)
+        argspec = [func, argspec, is_bound_method]
+    except (TypeError, KeyError):
+        with AttrCleaner(f):
+            argspec = getpydocspec(f, func)
+        if argspec is None:
+            return None
+        if inspect.ismethoddescriptor(f):
+            argspec[1][0].insert(0, 'obj')
+        argspec.append(is_bound_method)
+    return argspec