diff options
-rw-r--r-- | ranger/core/main.py | 61 | ||||
-rw-r--r-- | ranger/ext/vcs/vcs.py | 76 | ||||
-rw-r--r-- | ranger/gui/ui.py | 2 |
3 files changed, 81 insertions, 58 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..64dec6a1 100644 --- a/ranger/ext/vcs/vcs.py +++ b/ranger/ext/vcs/vcs.py @@ -374,19 +374,20 @@ 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.queue = queue.Queue() - self.advance = threading.Event() - self.advance.set() + 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() + self._awoken = threading.Event() + self._redraw = False + self._roots = set() def _is_targeted(self, dirobj): """Check if dirobj is targeted""" - if self.ui.browser.main_column and self.ui.browser.main_column.target == dirobj: + if self._ui.browser.main_column and self._ui.browser.main_column.target == dirobj: return True return False @@ -404,14 +405,14 @@ class VcsThread(threading.Thread): # pylint: disable=too-many-instance-attribut if fsobj.vcs.is_root_pointer: has_vcschild = True if not rootvcs.rootinit and not self._is_targeted(rootvcs.obj): - self.roots.add(rootvcs.path) + self._roots.add(rootvcs.path) if not rootvcs.init_root(): rootvcs.update_tree(purge=True) - self.redraw = True + self._redraw = True if fsobj.is_link: fsobj.vcsstatus = rootvcs.obj.vcsstatus fsobj.vcsremotestatus = rootvcs.obj.vcsremotestatus - self.redraw = True + self._redraw = True return has_vcschild @@ -419,11 +420,11 @@ class VcsThread(threading.Thread): # pylint: disable=too-many-instance-attribut """Process queue""" dirobjs = [] paths = set() - self.roots.clear() + self._roots.clear() while True: try: - dirobjs.append(self.queue.get(block=False)) + dirobjs.append(self._queue.get(block=False)) except queue.Empty: break @@ -435,55 +436,66 @@ class VcsThread(threading.Thread): # pylint: disable=too-many-instance-attribut dirobj.vcs.reinit() if dirobj.vcs.track: rootvcs = dirobj.vcs.rootvcs - if rootvcs.path not in self.roots and rootvcs.check_outdated(): - self.roots.add(rootvcs.path) + if rootvcs.path not in self._roots and rootvcs.check_outdated(): + self._roots.add(rootvcs.path) if rootvcs.update_root(): rootvcs.update_tree() else: rootvcs.update_tree(purge=True) - self.redraw = True + self._redraw = True has_vcschild = self._update_subroots(dirobj.files_all) if dirobj.has_vcschild != has_vcschild: dirobj.has_vcschild = has_vcschild - self.redraw = True + self._redraw = True def run(self): while True: self.paused.set() - self.advance.wait() - self.awoken.wait() - if not self.advance.isSet(): + self._advance.wait() + self._awoken.wait() + if self._stop.isSet(): + self.stopped.set() + return + if not self._advance.isSet(): continue + self._awoken.clear() self.paused.clear() - self.awoken.clear() try: self._queue_process() - if self.redraw: - self.redraw = False - for column in self.ui.browser.columns: + 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() + self._ui.status.need_redraw = True + self._ui.redraw() except Exception as ex: # pylint: disable=broad-except - self.ui.fm.notify('VCS Exception: View log for more info', bad=True, exception=ex) + 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() + self._advance.clear() def unpause(self): """Unpause thread""" - self.advance.set() + self._advance.set() def process(self, dirobj): """Process dirobj""" - self.queue.put(dirobj) - self.awoken.set() + self._queue.put(dirobj) + self._awoken.set() # Backend imports 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` |