Andreas Stührk avatar Andreas Stührk committed 9993c91

Add show source feature.

Comments (0)

Files changed (3)

 
 # These are used for syntax hilighting.
 from pygments import format
+from pygments.formatters import TerminalFormatter
 from pygments.lexers import PythonLexer
 from pygments.token import Token
 from bpython.formatter import BPythonFormatter, Parenthesis
         self.matches = []
         self.matches_iter = MatchesIterator()
         self.argspec = None
+        self.current_func = None
         self.s = ''
         self.inside_string = False
         self.highlighted_paren = None
             i += 1
         return self.s[-i +1:]
 
+    def get_object(self, name):
+        if name in self.interp.locals:
+            return self.interp.locals[name]
+        else:
+            return eval(name, self.interp.locals)
+
     def get_args(self):
         """Check if an unclosed parenthesis exists, then attempt to get the
         argspec() for it. On success, update self.argspec and return True,
         if not OPTS.arg_spec:
             return False
 
-        # Find the name of the current function
+        # Get the name of the current function and where we are in
+        # the arguments
         stack = [['', 0, '']]
         try:
             for (token, value) in PythonLexer().get_tokens(self.s):
             func, _, _ = stack.pop()
         except IndexError:
             return False
+        if not func:
+            return False
 
-        # 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
+            f = self.get_object(func)
+        except (AttributeError, NameError):
+            return False
+
         if inspect.isclass(f):
             try:
                 f = f.__init__
             page(self.stdout_hist[self.prev_block_finished:-4])
             return ''
 
+        elif key in key_dispatch[OPTS.show_source_key]:
+            try:
+                obj = self.current_func
+                if obj is None and inspection.is_eval_safe_name(self.s):
+                    obj = self.get_object(self.s)
+                source = inspect.getsource(obj)
+            except (AttributeError, NameError, TypeError):
+                self.statusbar.message("Cannot show source.")
+                return ''
+            else:
+                if OPTS.highlight_show_source:
+                    source = format(PythonLexer().get_tokens(source),
+                                    TerminalFormatter())
+                page(source)
+            return ''
+
         elif key == '\n':
             self.lf()
             return None

bpython/config.py

             'auto_display_list': True,
             'color_scheme': 'default',
             'flush_output': True,
+            'highlight_show_source': True,
             'hist_file': '~/.pythonhist',
             'hist_length': 100,
             'paste_time': 0.02,
             'last_output': 'F9',
             'pastebin': 'F8',
             'save': 'C-s',
+            'show_source': 'F2',
             'undo': 'C-r',
             'up_one_line': 'C-p',
             'yank_from_buffer': 'C-y'
     struct.syntax = config.getboolean('general', 'syntax')
     struct.arg_spec = config.getboolean('general', 'arg_spec')
     struct.paste_time = config.getfloat('general', 'paste_time')
+    struct.highlight_show_source = config.getboolean('general',
+                                                     'highlight_show_source')
     struct.hist_file = config.get('general', 'hist_file')
     struct.hist_length = config.getint('general', 'hist_length')
     struct.flush_output = config.getboolean('general', 'flush_output')
     struct.pastebin_key = config.get('keyboard', 'pastebin')
     struct.save_key = config.get('keyboard', 'save')
+    struct.show_source_key = config.get('keyboard', 'show_source')
     struct.undo_key = config.get('keyboard', 'undo')
     struct.up_one_line_key = config.get('keyboard', 'up_one_line')
     struct.down_one_line_key = config.get('keyboard', 'down_one_line')

bpython/inspection.py

 
 py3 = sys.version_info[0] == 3
 
+if not py3:
+    _name = re.compile(r'[a-zA-Z_]\w*')
+
 
 class AttrCleaner(object):
     """A context manager that tries to make an object not exhibit side-effects
 
     def __enter__(self):
         """Try to make an object not exhibit side-effects on attribute
-        lookup.""" 
+        lookup."""
         type_ = type(self.obj)
         __getattribute__ = None
         __getattr__ = None
             argspec = inspect.getfullargspec(f)
         else:
             argspec = inspect.getargspec(f)
-                 
+
         argspec = list(argspec)
         fixlongargs(f, argspec)
         argspec = [func, argspec, is_bound_method]
             argspec[1][0].insert(0, 'obj')
         argspec.append(is_bound_method)
     return argspec
+
+
+def is_eval_safe_name(string):
+    if py3:
+        return all(part.isidentifier() for part in string.split('.'))
+    else:
+        return all(_name.match(part) for part in string.split('.'))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.