summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorael-code <tommy.ael@gmail.com>2016-11-11 13:12:20 +0100
committernfnty <git@nfnty.se>2017-01-29 23:16:30 +0100
commit62a77e71426ce4e08e410e065ef8789e8dfa6ee8 (patch)
tree8eed4b2bdd04fc2561d6b8d671c3734fd66aa849
parentad51cca4893b214d9428248572f7d0b518c9a285 (diff)
downloadranger-62a77e71426ce4e08e410e065ef8789e8dfa6ee8.tar.gz
core.main: Refactor exit handling
Decrease complexity
Do not ignore system exit exceptions
Properly destroy UI
-rw-r--r--ranger/core/main.py61
-rw-r--r--ranger/ext/vcs/vcs.py18
-rw-r--r--ranger/gui/ui.py2
3 files changed, 52 insertions, 29 deletions
diff --git a/ranger/core/main.py b/ranger/core/main.py
index cee698c7..281f89f5 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -97,7 +97,9 @@ def main(
         print("Inaccessible paths: %s" % paths)
         return 1
 
-    crash_traceback = None
+    profile = None
+    exit_msg = ''
+    exit_code = 0
     try:
         # Initialize objects
         fm = FM(paths=paths)
@@ -148,43 +150,50 @@ def main(
         if ranger.args.profile:
             import cProfile
             import pstats
-            profile = None
             ranger.__fm = fm  # pylint: disable=protected-access
-            cProfile.run('ranger.__fm.loop()', tempfile.gettempdir() + '/ranger_profile')
-            profile = pstats.Stats(tempfile.gettempdir() + '/ranger_profile', stream=sys.stderr)
+            profile_file = tempfile.gettempdir() + '/ranger_profile'
+            cProfile.run('ranger.__fm.loop()', profile_file)
+            profile = pstats.Stats(profile_file, stream=sys.stderr)
         else:
             fm.loop()
     except Exception:  # pylint: disable=broad-except
         import traceback
-        crash_traceback = traceback.format_exc()
-    except SystemExit as error:
-        return error.args[0]
+        exit_msg += '''\
+ranger version: {0}
+Python version: {1}
+Locale: {2}
+'''.format(ranger.__version__, sys.version.split()[0],
+           '.'.join(str(s) for s in locale.getlocale()))
+        try:
+            exit_msg += "Current file: '{0}'\n".format(fm.thisfile.path)
+        except AttributeError:
+            pass
+        exit_msg += '''
+{0}
+ranger crashed. Please report this traceback at:
+https://github.com/hut/ranger/issues
+'''.format(traceback.format_exc())
+        exit_code = 1
+
+    except SystemExit as ex:
+        if ex.code is not None:
+            if not isinstance(ex.code, int):
+                exit_msg = ex.code
+                exit_code = 1
+            else:
+                exit_code = ex.code
     finally:
-        if crash_traceback:
-            try:
-                filepath = fm.thisfile.path if fm.thisfile else "None"
-            except AttributeError:
-                filepath = "None"
         try:
             fm.ui.destroy()
         except (AttributeError, NameError):
             pass
+        # If profiler is enabled print the stats
         if ranger.args.profile and profile:
             profile.strip_dirs().sort_stats('cumulative').print_callees()
-        if crash_traceback:
-            print("ranger version: %s, executed with python %s" %
-                  (ranger.__version__, sys.version.split()[0]))
-            print("Locale: %s" % '.'.join(str(s) for s in locale.getlocale()))
-            try:
-                print("Current file: %s" % filepath)
-            except NameError:
-                pass
-            print(crash_traceback)
-            print("ranger crashed.  "
-                  "Please report this traceback at:")
-            print("https://github.com/hut/ranger/issues")
-            return 1  # pylint: disable=lost-exception
-        return 0  # pylint: disable=lost-exception
+        # print the exit message if any
+        if exit_msg:
+            sys.stderr.write(exit_msg)
+        return exit_code  # pylint: disable=lost-exception
 
 
 def parse_arguments():
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index 00f51152..fa5c04c0 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -374,13 +374,14 @@ class VcsThread(threading.Thread):  # pylint: disable=too-many-instance-attribut
     def __init__(self, ui):
         super(VcsThread, self).__init__()
         self.daemon = True
-        self.ui = ui  # pylint: disable=invalid-name
+        self.ui = ui
         self.queue = queue.Queue()
+        self._stop = threading.Event()
+        self.stopped = threading.Event()
         self.advance = threading.Event()
         self.advance.set()
         self.paused = threading.Event()
         self.awoken = threading.Event()
-        self.timestamp = time.time()
         self.redraw = False
         self.roots = set()
 
@@ -454,10 +455,13 @@ class VcsThread(threading.Thread):  # pylint: disable=too-many-instance-attribut
             self.paused.set()
             self.advance.wait()
             self.awoken.wait()
+            if self._stop.isSet():
+                self.stopped.set()
+                return
             if not self.advance.isSet():
                 continue
-            self.paused.clear()
             self.awoken.clear()
+            self.paused.clear()
 
             try:
                 self._queue_process()
@@ -472,6 +476,14 @@ class VcsThread(threading.Thread):  # pylint: disable=too-many-instance-attribut
             except Exception as ex:  # pylint: disable=broad-except
                 self.ui.fm.notify('VCS Exception: View log for more info', bad=True, exception=ex)
 
+    def stop(self):
+        """Stop thread synchronously"""
+        self._stop.set()
+        self.paused.wait()
+        self.advance.set()
+        self.awoken.set()
+        self.stopped.wait()
+
     def pause(self):
         """Pause thread"""
         self.advance.clear()
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 6a2f3c6c..c3bc8539 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -164,6 +164,8 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
 
     def destroy(self):
         """Destroy all widgets and turn off curses"""
+        if 'vcsthread' in self.__dict__:
+            self.vcsthread.stop()
         DisplayableContainer.destroy(self)
 
         # Restore tmux setting `automatic-rename`