summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/core/fm.py6
-rw-r--r--ranger/core/linemode.py5
-rw-r--r--ranger/core/main.py11
-rw-r--r--ranger/ext/spawn.py49
-rw-r--r--ranger/ext/vcs/vcs.py35
5 files changed, 75 insertions, 31 deletions
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index 8168b7d6..fb6dd6b9 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -49,7 +49,7 @@ class FM(Actions, SignalDispatcher):
             self.ui = ui
         self.start_paths = paths
         self.directories = dict()
-        self.log = deque(maxlen=20)
+        self.log = deque(maxlen=1000)
         self.bookmarks = bookmarks
         self.current_tab = 1
         self.tabs = {}
@@ -70,9 +70,9 @@ class FM(Actions, SignalDispatcher):
         self.hostname = socket.gethostname()
         self.home_path = os.path.expanduser('~')
 
-        self.log.append('ranger {0} started! Process ID is {1}.'
+        self.log.appendleft('ranger {0} started! Process ID is {1}.'
                 .format(__version__, os.getpid()))
-        self.log.append('Running on Python ' + sys.version.replace('\n', ''))
+        self.log.appendleft('Running on Python ' + sys.version.replace('\n', ''))
 
         mimetypes.knownfiles.append(os.path.expanduser('~/.mime.types'))
         mimetypes.knownfiles.append(self.relpath('data/mime.types'))
diff --git a/ranger/core/linemode.py b/ranger/core/linemode.py
index 96557515..a8ce4e6d 100644
--- a/ranger/core/linemode.py
+++ b/ranger/core/linemode.py
@@ -7,6 +7,7 @@ import sys
 from abc import *
 from datetime import datetime
 from ranger.ext.human_readable import human_readable
+from ranger.ext.spawn import spawn
 
 DEFAULT_LINEMODE = "filename"
 
@@ -97,9 +98,9 @@ class FileInfoLinemode(LinemodeBase):
 
     def infostring(self, file, metadata):
         if not file.is_directory:
-            from subprocess import check_output, CalledProcessError
+            from subprocess import Popen, PIPE, CalledProcessError
             try:
-                fileinfo = check_output(["file", "-bL", file.path]).strip()
+                fileinfo = spawn(["file", "-bL", file.path]).strip()
             except CalledProcessError:
                 return "unknown"
             if sys.version_info[0] >= 3:
diff --git a/ranger/core/main.py b/ranger/core/main.py
index 118a7480..93c71e81 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -112,7 +112,7 @@ def main():
         if fm.username == 'root':
             fm.settings.preview_files = False
             fm.settings.use_preview_script = False
-            fm.log.append("Running as root, disabling the file previews.")
+            fm.log.appendleft("Running as root, disabling the file previews.")
         if not arg.debug:
             from ranger.ext import curses_interrupt_handler
             curses_interrupt_handler.install_interrupt_handler()
@@ -313,12 +313,11 @@ def load_settings(fm, clean):
                     else:
                         module = importlib.import_module('plugins.' + plugin)
                         fm.commands.load_commands_from_module(module)
-                    fm.log.append("Loaded plugin '%s'." % plugin)
-                except Exception as e:
-                    fm.log.append("Error in plugin '%s'" % plugin)
+                    fm.log.appendleft("Loaded plugin '%s'." % plugin)
+                except Exception:
                     import traceback
-                    for line in traceback.format_exception_only(type(e), e):
-                        fm.log.append(line)
+                    fm.log.extendleft(reversed(traceback.format_exc().splitlines()))
+                    fm.notify("Error in plugin '%s'" % plugin, bad=True)
             ranger.fm = None
 
         # COMPAT: Load the outdated options.py
diff --git a/ranger/ext/spawn.py b/ranger/ext/spawn.py
index 7c5c921c..393d48d9 100644
--- a/ranger/ext/spawn.py
+++ b/ranger/ext/spawn.py
@@ -1,18 +1,57 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
-from subprocess import Popen, PIPE
+from subprocess import Popen, PIPE, CalledProcessError
 ENCODING = 'utf-8'
 
 
-def spawn(*args):
-    """Runs a program, waits for its termination and returns its stdout"""
+def spawn(*args, **kwargs):
+    """Runs a program, waits for its termination and returns its stdout
+
+    This function is similar to python 2.7's subprocess.check_output,
+    but is favored due to python 2.6 compatibility.
+
+    The arguments may be:
+
+        spawn(string)
+        spawn(command, arg1, arg2...)
+        spawn([command, arg1, arg2])
+
+    The string will be run through a shell, otherwise the command is executed
+    directly.
+
+    The keyword argument "decode" determines if the output shall be decoded
+    with the encoding '%s'.
+
+    Further keyword arguments are passed to Popen.
+    """ % (ENCODING, )
+
     if len(args) == 1:
         popen_arguments = args[0]
         shell = isinstance(popen_arguments, str)
     else:
         popen_arguments = args
         shell = False
-    process = Popen(popen_arguments, stdout=PIPE, shell=shell)
+
+    if 'decode' in kwargs:
+        do_decode = kwargs['decode']
+        del kwargs['decode']
+    else:
+        do_decode = True
+    if 'stdout' not in kwargs:
+        kwargs['stdout'] = PIPE
+    if 'shell' not in kwargs:
+        kwargs['shell'] = shell
+
+    process = Popen(popen_arguments, **kwargs)
     stdout, stderr = process.communicate()
-    return stdout.decode(ENCODING)
+    return_value = process.poll()
+    if return_value:
+        error = CalledProcessError(return_value, popen_arguments[0])
+        error.output = stdout
+        raise error
+
+    if do_decode:
+        return stdout.decode(ENCODING)
+    else:
+        return stdout
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index 9c7be653..cfdc1e3b 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -7,6 +7,7 @@ import os
 import subprocess
 import threading
 import time
+from ranger.ext.spawn import spawn
 
 # Python2 compatibility
 try:
@@ -119,14 +120,13 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
         with open(os.devnull, 'w') as devnull:
             try:
                 if catchout:
-                    output = subprocess.check_output(cmd, cwd=path, stderr=devnull)
-                    if retbytes:
-                        return output
-                    else:
-                        output = output.decode('UTF-8')
+                    output = spawn(cmd, cwd=path, stderr=devnull,
+                            decode=not retbytes)
+                    if (not retbytes and rstrip_newline and
+                            output.endswith('\n')):
                         if rstrip_newline and output.endswith('\n'):
                             return output[:-1]
-                        return output
+                    return output
                 else:
                     subprocess.check_call(cmd, cwd=path, stdout=devnull, stderr=devnull)
             except (subprocess.CalledProcessError, FileNotFoundError):
@@ -460,15 +460,20 @@ class VcsThread(threading.Thread):  # pylint: disable=too-many-instance-attribut
             self.paused.clear()
             self.awoken.clear()
 
-            self._queue_process()
-
-            if self.redraw:
-                self.redraw = False
-                for column in self.ui.browser.columns:
-                    if column.target and column.target.is_directory:
-                        column.need_redraw = True
-                self.ui.status.need_redraw = True
-                self.ui.redraw()
+            try:
+                self._queue_process()
+
+                if self.redraw:
+                    self.redraw = False
+                    for column in self.ui.browser.columns:
+                        if column.target and column.target.is_directory:
+                            column.need_redraw = True
+                    self.ui.status.need_redraw = True
+                    self.ui.redraw()
+            except Exception:  # pylint: disable=broad-except
+                import traceback
+                self.ui.fm.log.extendleft(reversed(traceback.format_exc().splitlines()))
+                self.ui.fm.notify('VCS Exception', bad=True)
 
     def pause(self):
         """Pause thread"""