summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xdoc/tools/print_colors.py4
-rw-r--r--examples/plugin_pmount.py29
-rw-r--r--pylintrc2
-rw-r--r--ranger/api/commands.py24
-rwxr-xr-xranger/config/commands.py28
-rw-r--r--ranger/config/pylintrc2
-rw-r--r--ranger/container/bookmarks.py12
-rw-r--r--ranger/container/directory.py44
-rw-r--r--ranger/container/file.py13
-rw-r--r--ranger/container/fsobject.py13
-rw-r--r--ranger/container/history.py11
-rw-r--r--ranger/container/settings.py5
-rw-r--r--ranger/core/actions.py86
-rw-r--r--ranger/core/fm.py24
-rw-r--r--ranger/core/loader.py13
-rw-r--r--ranger/core/main.py25
-rw-r--r--ranger/core/runner.py14
-rw-r--r--ranger/core/tab.py2
-rw-r--r--ranger/ext/cached_function.py2
-rw-r--r--ranger/ext/direction.py10
-rw-r--r--ranger/ext/get_executables.py4
-rw-r--r--ranger/ext/img_display.py29
-rw-r--r--ranger/ext/keybinding_parser.py11
-rw-r--r--ranger/ext/logutils.py23
-rwxr-xr-xranger/ext/rifle.py20
-rw-r--r--ranger/ext/shutil_generatorized.py16
-rw-r--r--ranger/ext/signals.py8
-rw-r--r--ranger/ext/vcs/vcs.py27
-rw-r--r--ranger/ext/widestring.py5
-rw-r--r--ranger/gui/ansi.py2
-rw-r--r--ranger/gui/color.py4
-rw-r--r--ranger/gui/colorscheme.py10
-rw-r--r--ranger/gui/curses_shortcuts.py10
-rw-r--r--ranger/gui/displayable.py8
-rw-r--r--ranger/gui/mouse_event.py2
-rw-r--r--ranger/gui/ui.py16
-rw-r--r--ranger/gui/widgets/browsercolumn.py13
-rw-r--r--ranger/gui/widgets/console.py23
-rw-r--r--ranger/gui/widgets/pager.py6
-rw-r--r--ranger/gui/widgets/statusbar.py9
-rw-r--r--ranger/gui/widgets/titlebar.py5
-rw-r--r--ranger/gui/widgets/view_base.py12
-rw-r--r--ranger/gui/widgets/view_miller.py4
43 files changed, 306 insertions, 324 deletions
diff --git a/doc/tools/print_colors.py b/doc/tools/print_colors.py
index 65625cb5..762ac3e3 100755
--- a/doc/tools/print_colors.py
+++ b/doc/tools/print_colors.py
@@ -15,14 +15,14 @@ def main(win):
         for color in range(-1, curses.COLORS):
             try:
                 curses.init_pair(color, color, 0)
-            except Exception:
+            except curses.error:
                 pass
             else:
                 win.addstr(str(color) + ' ', curses.color_pair(color) | attr)
     curses.start_color()
     try:
         curses.use_default_colors()
-    except Exception:
+    except curses.error:
         pass
     win.addstr("available colors: %d\n\n" % curses.COLORS)
     print_all_colors(0)
diff --git a/examples/plugin_pmount.py b/examples/plugin_pmount.py
index d7c88349..38c4a17c 100644
--- a/examples/plugin_pmount.py
+++ b/examples/plugin_pmount.py
@@ -22,22 +22,19 @@ HOOK_INIT_OLD = ranger.api.hook_init
 
 
 def hook_init(fm):
-    try:
-        fm.execute_console("map {key} shell -p lsblk".format(key=LIST_MOUNTS_KEY))
-        for disk in "abcdefgh":
-            fm.execute_console("map {key}{0} chain shell pmount sd{1}; cd /media/sd{1}".format(
-                disk.upper(), disk, key=MOUNT_KEY))
-            fm.execute_console("map {key}{0} chain cd; chain shell pumount sd{1}".format(
-                disk.upper(), disk, key=UMOUNT_KEY))
-            for part in "123456789":
-                fm.execute_console(
-                    "map {key}{0}{1} chain shell pmount sd{0}{1}; cd /media/sd{0}{1}".format(
-                        disk, part, key=MOUNT_KEY)
-                )
-                fm.execute_console("map {key}{0}{1} chain cd; shell pumount sd{0}{1}".format(
-                    disk, part, key=UMOUNT_KEY))
-    except Exception:
-        pass
+    fm.execute_console("map {key} shell -p lsblk".format(key=LIST_MOUNTS_KEY))
+    for disk in "abcdefgh":
+        fm.execute_console("map {key}{0} chain shell pmount sd{1}; cd /media/sd{1}".format(
+            disk.upper(), disk, key=MOUNT_KEY))
+        fm.execute_console("map {key}{0} chain cd; chain shell pumount sd{1}".format(
+            disk.upper(), disk, key=UMOUNT_KEY))
+        for part in "123456789":
+            fm.execute_console(
+                "map {key}{0}{1} chain shell pmount sd{0}{1}; cd /media/sd{0}{1}".format(
+                    disk, part, key=MOUNT_KEY)
+            )
+            fm.execute_console("map {key}{0}{1} chain cd; shell pumount sd{0}{1}".format(
+                disk, part, key=UMOUNT_KEY))
 
     return HOOK_INIT_OLD(fm)
 
diff --git a/pylintrc b/pylintrc
index d2729ce4..d108c93f 100644
--- a/pylintrc
+++ b/pylintrc
@@ -8,7 +8,7 @@ max-branches=16
 
 [FORMAT]
 max-line-length = 99
-disable=locally-disabled,locally-enabled,missing-docstring,duplicate-code,fixme,broad-except,cyclic-import,redefined-variable-type
+disable=locally-disabled,locally-enabled,missing-docstring,duplicate-code,fixme,cyclic-import,redefined-variable-type
 
 [TYPECHECK]
 ignored-classes=ranger.core.actions.Actions
diff --git a/ranger/api/commands.py b/ranger/api/commands.py
index 25353b5b..6b761de8 100644
--- a/ranger/api/commands.py
+++ b/ranger/api/commands.py
@@ -29,18 +29,14 @@ class CommandContainer(object):
         return self.commands[key]
 
     def alias(self, name, full_command):
-        try:
-            cmd = type(name, (AliasCommand, ), dict())
-            # pylint: disable=protected-access
-            cmd._based_function = name
-            cmd._function_name = name
-            cmd._object_name = name
-            cmd._line = full_command
-            # pylint: enable=protected-access
-            self.commands[name] = cmd
-
-        except Exception:
-            pass
+        cmd = type(name, (AliasCommand, ), dict())
+        # pylint: disable=protected-access
+        cmd._based_function = name
+        cmd._function_name = name
+        cmd._object_name = name
+        cmd._line = full_command
+        # pylint: enable=protected-access
+        self.commands[name] = cmd
 
     def load_commands_from_module(self, module):
         for var in vars(module).values():
@@ -392,13 +388,13 @@ class FunctionCommand(Command):
             value = arg if (equal_sign is -1) else arg[equal_sign + 1:]
             try:
                 value = int(value)
-            except Exception:
+            except ValueError:
                 if value in ('True', 'False'):
                     value = (value == 'True')
                 else:
                     try:
                         value = float(value)
-                    except Exception:
+                    except ValueError:
                         pass
 
             if equal_sign == -1:
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index bb20ddcb..eba94e19 100755
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -598,9 +598,10 @@ class console(Command):
         if self.arg(1)[0:2] == '-p':
             try:
                 position = int(self.arg(1)[2:])
-                self.shift()
-            except Exception:
+            except ValueError:
                 pass
+            else:
+                self.shift()
         self.fm.open_console(self.rest(1), position=position)
 
 
@@ -614,10 +615,10 @@ class load_copy_buffer(Command):
     def execute(self):
         from ranger.container.file import File
         from os.path import exists
+        fname = self.fm.confpath(self.copy_buffer_filename)
         try:
-            fname = self.fm.confpath(self.copy_buffer_filename)
             fobj = open(fname, 'r')
-        except Exception:
+        except OSError:
             return self.fm.notify(
                 "Cannot open %s" % (fname or self.copy_buffer_filename), bad=True)
         self.fm.copy_buffer = set(File(g)
@@ -635,10 +636,10 @@ class save_copy_buffer(Command):
 
     def execute(self):
         fname = None
+        fname = self.fm.confpath(self.copy_buffer_filename)
         try:
-            fname = self.fm.confpath(self.copy_buffer_filename)
             fobj = open(fname, 'w')
-        except Exception:
+        except OSError:
             return self.fm.notify("Cannot open %s" %
                                   (fname or self.copy_buffer_filename), bad=True)
         fobj.write("\n".join(fobj.path for fobj in self.fm.copy_buffer))
@@ -744,7 +745,7 @@ class eval_(Command):
             else:
                 if result and not quiet:
                     p(result)
-        except Exception as err:
+        except Exception as err:  # pylint: disable=broad-except
             p(err)
 
 
@@ -841,15 +842,12 @@ class chmod(Command):
         for fobj in self.fm.thistab.get_selection():
             try:
                 os.chmod(fobj.path, mode)
-            except Exception as ex:
+            except OSError as ex:
                 self.fm.notify(ex)
 
-        try:
-            # reloading directory.  maybe its better to reload the selected
-            # files only.
-            self.fm.thisdir.load_content()
-        except Exception:
-            pass
+        # reloading directory.  maybe its better to reload the selected
+        # files only.
+        self.fm.thisdir.content_outdated = True
 
 
 class bulkrename(Command):
@@ -1270,7 +1268,7 @@ class scout(Command):
         # pylint: enable=no-member
         try:
             self._regex = re.compile(regex, options)
-        except Exception:
+        except re.error:
             self._regex = re.compile("")
         return self._regex
 
diff --git a/ranger/config/pylintrc b/ranger/config/pylintrc
index adec41f1..7cac80d1 100644
--- a/ranger/config/pylintrc
+++ b/ranger/config/pylintrc
@@ -5,4 +5,4 @@ class-rgx=[a-z][a-z0-9_]{1,30}$
 [FORMAT]
 max-line-length = 99
 max-module-lines=3000
-disable=locally-disabled,locally-enabled,missing-docstring,duplicate-code,fixme,broad-except
+disable=locally-disabled,locally-enabled,missing-docstring,duplicate-code,fixme
diff --git a/ranger/container/bookmarks.py b/ranger/container/bookmarks.py
index edffa5ae..305ac4c2 100644
--- a/ranger/container/bookmarks.py
+++ b/ranger/container/bookmarks.py
@@ -155,12 +155,8 @@ class Bookmarks(object):
         if os.access(self.path, os.W_OK):
             fobj = open(self.path + ".new", 'w')
             for key, value in self.dct.items():
-                if isinstance(key, str)\
-                        and key in ALLOWED_KEYS:
-                    try:
-                        fobj.write("{0}:{1}\n".format(str(key), str(value)))
-                    except Exception:
-                        pass
+                if isinstance(key, str) and key in ALLOWED_KEYS:
+                    fobj.write("{0}:{1}\n".format(str(key), str(value)))
 
             fobj.close()
             old_perms = os.stat(self.path)
@@ -181,8 +177,8 @@ class Bookmarks(object):
         if not os.path.exists(self.path):
             try:
                 fobj = open(self.path, 'w')
-            except Exception:
-                raise OSError('Cannot read the given path')
+            except OSError:
+                raise
             fobj.close()
 
         if os.access(self.path, os.R_OK):
diff --git a/ranger/container/directory.py b/ranger/container/directory.py
index 4fbc2e29..22901006 100644
--- a/ranger/container/directory.py
+++ b/ranger/container/directory.py
@@ -331,25 +331,25 @@ class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public
                             file_stat = os_stat(name)
                         else:
                             file_stat = file_lstat
+                    except OSError:
+                        file_lstat = None
+                        file_stat = None
+                    if file_lstat and file_stat:
                         stats = (file_stat, file_lstat)
                         is_a_dir = file_stat.st_mode & 0o170000 == 0o040000
-                    except Exception:
+                    else:
                         stats = None
                         is_a_dir = False
+
                     if is_a_dir:
-                        try:
-                            item = self.fm.get_directory(name)
-                            item.load_if_outdated()
-                        except Exception:
-                            item = Directory(name, preload=stats, path_is_abs=True,
-                                             basename_is_rel_to=basename_is_rel_to)
-                            item.load()
+                        item = self.fm.get_directory(name, preload=stats, path_is_abs=True,
+                                                     basename_is_rel_to=basename_is_rel_to)
+                        item.load_if_outdated()
+                        if self.flat:
+                            item.relative_path = os.path.relpath(item.path, self.path)
                         else:
-                            if self.flat:
-                                item.relative_path = os.path.relpath(item.path, self.path)
-                            else:
-                                item.relative_path = item.basename
-                            item.relative_path_lower = item.relative_path.lower()
+                            item.relative_path = item.basename
+                        item.relative_path_lower = item.relative_path.lower()
                         if item.vcs and item.vcs.track:
                             if item.vcs.is_root_pointer:
                                 has_vcschild = True
@@ -454,7 +454,7 @@ class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public
 
         try:
             sort_func = self.sort_dict[self.settings.sort]
-        except Exception:
+        except KeyError:
             sort_func = sort_by_basename
 
         if self.settings.sort_case_insensitive and \
@@ -494,9 +494,9 @@ class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public
                         stat = os_stat(realpath(dirpath + "/" + fname))
                     else:
                         stat = os_stat(dirpath + "/" + fname)
-                    cum += stat.st_size
-                except Exception:
-                    pass
+                except OSError:
+                    continue
+                cum += stat.st_size
         return cum
 
     def look_up_cumulative_size(self):
@@ -548,7 +548,7 @@ class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public
     def move_to_obj(self, arg, attr=None):
         try:
             arg = arg.path
-        except Exception:
+        except AttributeError:
             pass
         self.load_content_once(schedule=False)
         if self.empty():
@@ -594,11 +594,11 @@ class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public
         """Make sure the pointer is in the valid range"""
         Accumulator.correct_pointer(self)
 
-        try:
-            if self == self.fm.thisdir:
+        if self == self.fm.thisdir:
+            try:
                 self.fm.thisfile = self.pointed_obj
-        except Exception:
-            pass
+            except AttributeError:
+                pass
 
     def load_content_once(self, *a, **k):
         """Load the contents of the directory if not done yet"""
diff --git a/ranger/container/file.py b/ranger/container/file.py
index e1284c1f..e0b9922c 100644
--- a/ranger/container/file.py
+++ b/ranger/container/file.py
@@ -54,14 +54,11 @@ class File(FileSystemObject):
     def firstbytes(self):
         if self._firstbytes is None:
             try:
-                fobj = open(self.path, 'r')
-                self._firstbytes = fobj.read(N_FIRST_BYTES)
-                fobj.close()
-                return self._firstbytes
-            except Exception:
-                pass
-        else:
-            return self._firstbytes
+                with open(self.path, 'r') as fobj:
+                    self._firstbytes = fobj.read(N_FIRST_BYTES)
+            except OSError:
+                return None
+        return self._firstbytes
 
     def is_binary(self):
         if self.firstbytes and control_characters & set(self.firstbytes):
diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py
index fbb75e6c..0447d5a6 100644
--- a/ranger/container/fsobject.py
+++ b/ranger/container/fsobject.py
@@ -18,6 +18,7 @@ from ranger.ext import spawn
 from ranger.ext.lazy_property import lazy_property
 from ranger.ext.human_readable import human_readable
 
+# Python 2 compatibility
 if hasattr(str, 'maketrans'):
     maketrans = str.maketrans  # pylint: disable=invalid-name,no-member
 else:
@@ -164,14 +165,14 @@ class FileSystemObject(  # pylint: disable=too-many-instance-attributes
     def user(self):
         try:
             return getpwuid(self.stat.st_uid)[0]
-        except Exception:
+        except KeyError:
             return str(self.stat.st_uid)
 
     @lazy_property
     def group(self):
         try:
             return getgrgid(self.stat.st_gid)[0]
-        except Exception:
+        except KeyError:
             return str(self.stat.st_gid)
 
     for attr in ('video', 'audio', 'image', 'media', 'document', 'container'):
@@ -220,7 +221,7 @@ class FileSystemObject(  # pylint: disable=too-many-instance-attributes
     def mimetype(self):
         try:
             return self._mimetype
-        except Exception:
+        except AttributeError:
             self.set_mimetype()
             return self._mimetype
 
@@ -228,7 +229,7 @@ class FileSystemObject(  # pylint: disable=too-many-instance-attributes
     def mimetype_tuple(self):
         try:
             return self._mimetype_tuple
-        except Exception:
+        except AttributeError:
             self.set_mimetype()
             return self._mimetype_tuple
 
@@ -245,7 +246,7 @@ class FileSystemObject(  # pylint: disable=too-many-instance-attributes
         if self.is_link:
             try:
                 return realpath(self.path)
-            except Exception:
+            except OSError:
                 return None  # it is impossible to get the link destination
         return self.path
 
@@ -279,7 +280,7 @@ class FileSystemObject(  # pylint: disable=too-many-instance-attributes
                 if self.is_link:
                     new_stat = stat(path)
                 self.exists = True
-            except Exception:
+            except OSError:
                 self.exists = False
 
         # Set some attributes
diff --git a/ranger/container/history.py b/ranger/container/history.py
index 31830b55..b3a1943e 100644
--- a/ranger/container/history.py
+++ b/ranger/container/history.py
@@ -35,7 +35,7 @@ class History(object):
         if self.unique:
             try:
                 self._history.remove(item)
-            except Exception:
+            except ValueError:
                 pass
         else:
             if self._history and self._history[-1] == item:
@@ -51,9 +51,10 @@ class History(object):
         if self._history and unique:
             try:
                 self._history.remove(item)
-                self._index -= 1
-            except Exception:
+            except ValueError:
                 pass
+            else:
+                self._index -= 1
         try:
             self._history[self._index] = item
         except IndexError:
@@ -101,13 +102,13 @@ class History(object):
         try:
             return self._history[-1]
         except IndexError:
-            raise HistoryEmptyException()
+            raise HistoryEmptyException
 
     def bottom(self):
         try:
             return self._history[0]
         except IndexError:
-            raise HistoryEmptyException()
+            raise HistoryEmptyException
 
     def back(self):
         self._index -= 1
diff --git a/ranger/container/settings.py b/ranger/container/settings.py
index c42f50f2..012b5a2c 100644
--- a/ranger/container/settings.py
+++ b/ranger/container/settings.py
@@ -166,7 +166,7 @@ class Settings(SignalDispatcher, FileManagerAware):
         else:
             try:
                 localpath = self.fm.thisdir.path
-            except Exception:
+            except AttributeError:
                 localpath = path
 
         if localpath:
@@ -236,8 +236,7 @@ class Settings(SignalDispatcher, FileManagerAware):
             if path not in self._localsettings:
                 try:
                     regex = re.compile(path)
-                except Exception:
-                    # Bad regular expression
+                except re.error:  # Bad regular expression
                     return
                 self._localregexes[path] = regex
                 self._localsettings[path] = dict()
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 5e840058..baf98563 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -33,6 +33,7 @@ from ranger.container.file import File
 from ranger.core.loader import CommandLoader, CopyLoader
 from ranger.container.settings import ALLOWED_SETTINGS, ALLOWED_VALUES
 
+
 MACRO_FAIL = "<\x01\x01MACRO_HAS_NO_VALUE\x01\01>"
 
 LOG = getLogger(__name__)
@@ -57,7 +58,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
         Exit the program.
         """
-        raise SystemExit()
+        raise SystemExit
 
     def reset(self):
         """:reset
@@ -143,25 +144,35 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         """
         try:
             cwd = self.thisdir
-        except Exception:
+        except AttributeError:
             pass
         else:
             cwd.unload()
             cwd.load_content()
 
-    def notify(self, text, duration=4, bad=False):
+    def notify(self, obj, duration=4, bad=False, exception=None):
         """:notify <text>
 
         Display the text in the statusbar.
         """
-        if isinstance(text, Exception):
+        if isinstance(obj, Exception):
             if ranger.args.debug:
-                raise text
+                raise obj
+            exception = obj
             bad = True
-        elif bad is True and ranger.args.debug:
-            raise Exception(str(text))
-        text = str(text)
-        LOG.debug("Command notify invoked: [Bad: %s, Text: '%s']", bad, text)
+        elif bad and ranger.args.debug:
+            raise Exception(str(obj))
+
+        text = str(obj)
+
+        text_log = 'Notification: {0}'.format(text)
+        if bad:
+            LOG.error(text_log)
+        else:
+            LOG.info(text_log)
+        if exception:
+            LOG.exception(exception)
+
         if self.ui and self.ui.is_on:
             self.ui.status.notify("  ".join(text.split("\n")),
                                   duration=duration, bad=bad)
@@ -175,7 +186,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         """
         try:
             item = self.loader.queue[0]
-        except Exception:
+        except IndexError:
             self.notify("Type Q or :quit<Enter> to exit ranger")
         else:
             self.notify("Aborting: " + item.get_description())
@@ -230,7 +241,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                     return self.notify(ex)
         try:
             cmd_class(string, quantifier=quantifier).execute()
-        except Exception as ex:
+        except Exception as ex:  # pylint: disable=broad-except
             if ranger.args.debug:
                 raise
             else:
@@ -296,7 +307,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         for i in range(1, 10):
             try:
                 tab = self.fm.tabs[i]
-            except Exception:
+            except KeyError:
                 continue
             tabdir = tab.thisdir
             if not tabdir:
@@ -365,7 +376,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                     continue
                 try:
                     self.execute_console(line)
-                except Exception as ex:
+                except Exception as ex:  # pylint: disable=broad-except
                     if ranger.args.debug:
                         raise
                     else:
@@ -396,7 +407,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                     fobj.path + "\n" for fobj in self.fm.thistab.get_selection()))
 
             if ranger.args.choosefile or ranger.args.choosefiles:
-                raise SystemExit()
+                raise SystemExit
 
         if isinstance(files, set):
             files = list(files)
@@ -440,10 +451,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
             steps = direction.left()
             if narg is not None:
                 steps *= narg
-            try:
-                directory = os.path.join(*(['..'] * steps))
-            except Exception:
-                return
+            directory = os.path.join(*(['..'] * steps))
             self.thistab.enter_dir(directory)
             self.change_mode('normal')
         if cwd and cwd.accessible and cwd.content_loaded:
@@ -468,7 +476,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 if self.mode == 'visual':
                     try:
                         startpos = cwd.index(self._visual_start)
-                    except Exception:
+                    except ValueError:
                         self._visual_start = None
                         startpos = min(self._visual_start_pos, len(cwd))
                     # The files between here and _visual_start_pos
@@ -703,7 +711,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         if isinstance(text, str) and regexp:
             try:
                 text = re.compile(text, re.UNICODE | re.IGNORECASE)  # pylint: disable=no-member
-            except Exception:
+            except re.error:
                 return False
         self.thistab.last_search = text
         self.search_next(order='search', offset=offset)
@@ -839,7 +847,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     def draw_possible_programs(self):
         try:
             target = self.thistab.get_selection()[0]
-        except Exception:
+        except IndexError:
             self.ui.browser.draw_info = []
             return
         programs = [program for program in self.rifle.list_commands([target.path], None)]
@@ -860,7 +868,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     def display_command_help(self, console_widget):
         try:
             command = console_widget._get_cmd_class()  # pylint: disable=protected-access
-        except Exception:
+        except KeyError:
             self.notify("Feature not available!", bad=True)
             return
 
@@ -912,9 +920,10 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     def update_preview(self, path):
         try:
             del self.previews[path]
-            self.ui.need_redraw = True
-        except Exception:
+        except KeyError:
             return False
+        self.ui.need_redraw = True
+        return True
 
     @staticmethod
     def sha1_encode(path):
@@ -941,7 +950,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
             # PDF file.
             try:
                 data = self.previews[path]
-            except Exception:
+            except KeyError:
                 data = self.previews[path] = {'loading': False}
             else:
                 if data['loading']:
@@ -959,7 +968,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
             if found is False:
                 try:
                     stat_ = os.stat(self.settings.preview_script)
-                except Exception:
+                except OSError:
                     self.fm.notify(
                         "Preview Script `%s' doesn't exist!" % self.settings.preview_script,
                         bad=True,
@@ -1047,7 +1056,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 def on_destroy(signal):  # pylint: disable=unused-argument
                     try:
                         del self.previews[path]
-                    except Exception:
+                    except KeyError:
                         pass
                 loadable.signal_bind('after', on_after)
                 loadable.signal_bind('destroy', on_destroy)
@@ -1058,7 +1067,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         else:
             try:
                 return codecs.open(path, 'r', errors='ignore')
-            except Exception:
+            except OSError:
                 return None
 
     # --------------------------
@@ -1317,31 +1326,34 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     def paste_symlink(self, relative=False):
         copied_files = self.copy_buffer
         for fobj in copied_files:
-            self.notify(next_available_filename(fobj.basename))
+            new_name = next_available_filename(fobj.basename)
+            self.notify(new_name)
             try:
-                new_name = next_available_filename(fobj.basename)
                 if relative:
                     relative_symlink(fobj.path, join(getcwd(), new_name))
                 else:
                     symlink(fobj.path, join(getcwd(), new_name))
-            except Exception as ex:
-                self.notify(ex)
+            except OSError as ex:
+                self.notify('Failed to paste symlink: View log for more info',
+                            bad=True, exception=ex)
 
     def paste_hardlink(self):
         for fobj in self.copy_buffer:
+            new_name = next_available_filename(fobj.basename)
             try:
-                new_name = next_available_filename(fobj.basename)
                 link(fobj.path, join(getcwd(), new_name))
-            except Exception as ex:
-                self.notify(ex)
+            except OSError as ex:
+                self.notify('Failed to paste hardlink: View log for more info',
+                            bad=True, exception=ex)
 
     def paste_hardlinked_subtree(self):
         for fobj in self.copy_buffer:
             try:
                 target_path = join(getcwd(), fobj.basename)
                 self._recurse_hardlinked_tree(fobj.path, target_path)
-            except Exception as ex:
-                self.notify(ex)
+            except OSError as ex:
+                self.notify('Failed to paste hardlinked subtree: View log for more info',
+                            bad=True, exception=ex)
 
     def _recurse_hardlinked_tree(self, source_path, target_path):
         if isdir(source_path):
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index 87c8b9f1..f524bbc4 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -7,7 +7,6 @@ from __future__ import (absolute_import, division, print_function)
 
 from time import time
 from collections import deque
-import logging
 import mimetypes
 import os.path
 import pwd
@@ -33,9 +32,6 @@ from ranger.core.loader import Loader
 from ranger.ext import logutils
 
 
-LOG = logging.getLogger(__name__)
-
-
 class FM(Actions,  # pylint: disable=too-many-instance-attributes
          SignalDispatcher):
     input_blocked = False
@@ -74,7 +70,7 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
 
         try:
             self.username = pwd.getpwuid(os.geteuid()).pw_name
-        except Exception:
+        except KeyError:
             self.username = 'uid:' + str(os.geteuid())
         self.hostname = socket.gethostname()
         self.home_path = os.path.expanduser('~')
@@ -195,13 +191,13 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
         if self.ui:
             try:
                 self.ui.destroy()
-            except Exception:
+            except Exception:  # pylint: disable=broad-except
                 if debug:
                     raise
         if self.loader:
             try:
                 self.loader.destroy()
-            except Exception:
+            except Exception:  # pylint: disable=broad-except
                 if debug:
                     raise
 
@@ -209,10 +205,10 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
     def get_log():
         """Return the current log
 
-        The log is returned as a list of string
+        The log is returned as a generator over its entries' lines
         """
-        for log in logutils.log_queue:
-            for line in log.split('\n'):
+        for entry in logutils.QUEUE:
+            for line in entry.splitlines():
                 yield line
 
     def _get_image_displayer(self):
@@ -270,10 +266,10 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
                         print(ranger.args.confdir)
                         print("To run ranger without the need for configuration")
                         print("files, use the --clean option.")
-                        raise SystemExit()
+                        raise SystemExit
                 try:
                     shutil.copy(self.relpath(src), self.confpath(dest))
-                except Exception as ex:
+                except OSError as ex:
                     sys.stderr.write("  ERROR: %s\n" % str(ex))
         if which == 'rifle' or which == 'all':
             copy('config/rifle.conf', 'rifle.conf')
@@ -313,13 +309,13 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
         """returns the path relative to rangers library directory"""
         return os.path.join(ranger.RANGERDIR, *paths)
 
-    def get_directory(self, path):
+    def get_directory(self, path, **dir_kwargs):
         """Get the directory object at the given path"""
         path = os.path.abspath(path)
         try:
             return self.directories[path]
         except KeyError:
-            obj = Directory(path)
+            obj = Directory(path, **dir_kwargs)
             self.directories[path] = obj
             return obj
 
diff --git a/ranger/core/loader.py b/ranger/core/loader.py
index 022dfe6b..59ce7391 100644
--- a/ranger/core/loader.py
+++ b/ranger/core/loader.py
@@ -18,7 +18,7 @@ from ranger.ext.human_readable import human_readable
 try:
     import chardet  # pylint: disable=import-error
     HAVE_CHARDET = True
-except Exception:
+except ImportError:
     HAVE_CHARDET = False
 
 
@@ -40,7 +40,7 @@ class Loadable(object):
     def unpause(self):
         try:
             del self.paused
-        except Exception:
+        except AttributeError:
             pass
 
     def destroy(self):
@@ -74,7 +74,7 @@ class CopyLoader(Loadable, FileManagerAware):  # pylint: disable=too-many-instan
             else:
                 try:
                     fstat = os.stat(fname)
-                except Exception:
+                except OSError:
                     continue
                 size += max(step, math.ceil(fstat.st_size / step) * step)
         return size
@@ -239,7 +239,7 @@ class CommandLoader(  # pylint: disable=too-many-instance-attributes
                 return
             try:
                 self.process.send_signal(20)
-            except Exception:
+            except OSError:
                 pass
             Loadable.pause(self)
             self.signal_emit('pause', process=self.process, loader=self)
@@ -248,7 +248,7 @@ class CommandLoader(  # pylint: disable=too-many-instance-attributes
         if not self.finished and self.paused:
             try:
                 self.process.send_signal(18)
-            except Exception:
+            except OSError:
                 pass
             Loadable.unpause(self)
             self.signal_emit('unpause', process=self.process, loader=self)
@@ -409,9 +409,6 @@ class Loader(FileManagerAware):
                 self.fm.ui.status.request_redraw()
         except StopIteration:
             self._remove_current_process(item)
-        except Exception as err:
-            self.fm.notify(err)
-            self._remove_current_process(item)
 
     def _remove_current_process(self, item):
         item.load_generator = None
diff --git a/ranger/core/main.py b/ranger/core/main.py
index bd837aa1..23debc76 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -39,7 +39,7 @@ def main(
 
     try:
         locale.setlocale(locale.LC_ALL, '')
-    except Exception:
+    except locale.Error:
         print("Warning: Unable to set locale.  Expect encoding problems.")
 
     # so that programs can know that ranger spawned them:
@@ -66,7 +66,7 @@ def main(
                 fobj = open(fm.confpath('tagged'), 'r', errors='replace')
             else:
                 fobj = open(fm.confpath('tagged'), 'r')
-        except Exception:
+        except OSError:
             pass
         else:
             for line in fobj.readlines():
@@ -164,7 +164,7 @@ def main(
             profile = pstats.Stats(tempfile.gettempdir() + '/ranger_profile', stream=sys.stderr)
         else:
             fm.loop()
-    except Exception:
+    except Exception:  # pylint: disable=broad-except
         import traceback
         crash_traceback = traceback.format_exc()
     except SystemExit as error:
@@ -173,7 +173,7 @@ def main(
         if crash_traceback:
             try:
                 filepath = fm.thisfile.path if fm.thisfile else "None"
-            except Exception:
+            except AttributeError:
                 filepath = "None"
         try:
             fm.ui.destroy()
@@ -187,7 +187,7 @@ def main(
             print("Locale: %s" % '.'.join(str(s) for s in locale.getlocale()))
             try:
                 print("Current file: %s" % filepath)
-            except Exception:
+            except NameError:
                 pass
             print(crash_traceback)
             print("ranger crashed.  "
@@ -318,13 +318,14 @@ def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many
         allow_access_to_confdir(ranger.args.confdir, True)
 
         # XXX Load plugins (experimental)
+        plugindir = fm.confpath('plugins')
         try:
-            plugindir = fm.confpath('plugins')
-            plugins = [p[:-3] for p in os.listdir(plugindir)
-                       if p.endswith('.py') and not p.startswith('_')]
-        except Exception:
-            pass
+            plugin_files = os.listdir(plugindir)
+        except OSError:
+            LOG.debug('Unable to access plugin directory: %s', plugindir)
         else:
+            plugins = [p[:-3] for p in plugin_files
+                       if p.endswith('.py') and not p.startswith('_')]
             if not os.path.exists(fm.confpath('plugins', '__init__.py')):
                 LOG.debug("Creating missing '__init__.py' file in plugin folder")
                 fobj = open(fm.confpath('plugins', '__init__.py'), 'w')
@@ -344,7 +345,7 @@ def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many
                         module = importlib.import_module('plugins.' + plugin)
                         fm.commands.load_commands_from_module(module)
                     LOG.debug("Loaded plugin '%s'", plugin)
-                except Exception as ex:
+                except Exception as ex:  # pylint: disable=broad-except
                     ex_msg = "Error while loading plugin '{0}'".format(plugin)
                     LOG.error(ex_msg)
                     LOG.exception(ex)
@@ -389,7 +390,7 @@ def allow_access_to_confdir(confdir, allow):
                 print(confdir)
                 print("To run ranger without the need for configuration")
                 print("files, use the --clean option.")
-                raise SystemExit()
+                raise SystemExit
         else:
             LOG.debug("Created config directory '%s'", confdir)
         if confdir not in sys.path:
diff --git a/ranger/core/runner.py b/ranger/core/runner.py
index 6fc7b60d..8c3e3162 100644
--- a/ranger/core/runner.py
+++ b/ranger/core/runner.py
@@ -24,6 +24,7 @@ t: run application in a new terminal window
 
 from __future__ import (absolute_import, division, print_function)
 
+import logging
 import os
 import sys
 from subprocess import Popen, PIPE
@@ -31,6 +32,9 @@ from ranger.ext.get_executables import get_executables, get_term
 from ranger.ext.popen_forked import Popen_forked
 
 
+LOG = logging.getLogger(__name__)
+
+
 # TODO: Remove unused parts of runner.py
 # ALLOWED_FLAGS = 'sdpwcrtSDPWCRT'
 ALLOWED_FLAGS = 'cfrtCFRT'
@@ -81,7 +85,7 @@ class Context(object):  # pylint: disable=too-many-instance-attributes
     def filepaths(self):
         try:
             return [f.path for f in self.files]
-        except Exception:
+        except AttributeError:
             return []
 
     def __iter__(self):
@@ -117,13 +121,15 @@ class Runner(object):  # pylint: disable=too-few-public-methods
             if boolean:
                 try:
                     self.ui.initialize()
-                except Exception:
+                except Exception as ex:  # pylint: disable=broad-except
                     self._log("Failed to initialize UI")
+                    LOG.exception(ex)
             else:
                 try:
                     self.ui.suspend()
-                except Exception:
+                except Exception as ex:  # pylint: disable=broad-except
                     self._log("Failed to suspend UI")
+                    LOG.exception(ex)
 
     def __call__(
             # pylint: disable=too-many-branches,too-many-statements
@@ -235,7 +241,7 @@ class Runner(object):  # pylint: disable=too-few-public-methods
                     Popen_forked(**popen_kws)
                 else:
                     process = Popen(**popen_kws)
-            except Exception as ex:
+            except OSError as ex:
                 error = ex
                 self._log("Failed to run: %s\n%s" % (str(action), str(ex)))
             else:
diff --git a/ranger/core/tab.py b/ranger/core/tab.py
index 26f634da..160f590f 100644
--- a/ranger/core/tab.py
+++ b/ranger/core/tab.py
@@ -130,7 +130,7 @@ class Tab(FileManagerAware, SettingsAware):  # pylint: disable=too-many-instance
 
         try:
             os.chdir(path)
-        except Exception:
+        except OSError:
             return True
         self.path = path
         self.thisdir = new_thisdir
diff --git a/ranger/ext/cached_function.py b/ranger/ext/cached_function.py
index 643b6304..4255443c 100644
--- a/ranger/ext/cached_function.py
+++ b/ranger/ext/cached_function.py
@@ -10,7 +10,7 @@ def cached_function(fnc):
     def inner_cached_function(*args):
         try:
             return cache[args]
-        except Exception:
+        except KeyError:
             value = fnc(*args)
             cache[args] = value
             return value
diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py
index ecde944a..7268d5d6 100644
--- a/ranger/ext/direction.py
+++ b/ranger/ext/direction.py
@@ -38,19 +38,19 @@ class Direction(dict):
     def _get_bool(self, first, second, fallback=None):
         try:
             return self[first]
-        except Exception:
+        except KeyError:
             try:
                 return not self[second]
-            except Exception:
+            except KeyError:
                 return fallback
 
     def _get_direction(self, first, second, fallback=0):
         try:
             return self[first]
-        except Exception:
+        except KeyError:
             try:
                 return -self[second]
-            except Exception:
+            except KeyError:
                 return fallback
 
     def up(self):  # pylint: disable=invalid-name
@@ -98,7 +98,7 @@ class Direction(dict):
         for key in ('up', 'right', 'down', 'left'):
             try:
                 self[key] *= n
-            except Exception:
+            except KeyError:
                 pass
 
     def set(self, n):
diff --git a/ranger/ext/get_executables.py b/ranger/ext/get_executables.py
index 86296a2a..1646cb56 100644
--- a/ranger/ext/get_executables.py
+++ b/ranger/ext/get_executables.py
@@ -37,13 +37,13 @@ def get_executables_uncached(*paths):
     for path in paths:
         try:
             content = listdir(path)
-        except Exception:
+        except OSError:
             continue
         for item in content:
             abspath = path + '/' + item
             try:
                 filestat = stat(abspath)
-            except Exception:
+            except OSError:
                 continue
             if filestat.st_mode & (S_IXOTH | S_IFREG):
                 executables.add(item)
diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py
index 1b20cc7b..b4deaa39 100644
--- a/ranger/ext/img_display.py
+++ b/ranger/ext/img_display.py
@@ -35,6 +35,10 @@ W3MIMGDISPLAY_PATHS = [
 ]
 
 
+class ImageDisplayError(Exception):
+    pass
+
+
 class ImgDisplayUnsupportedException(Exception):
     pass
 
@@ -83,9 +87,9 @@ class W3MImageDisplayer(ImageDisplayer):
         for path in paths:
             if path is not None and os.path.exists(path):
                 return path
-        raise RuntimeError("No w3mimgdisplay executable found.  Please set "
-                           "the path manually by setting the %s environment variable.  (see "
-                           "man page)" % W3MIMGDISPLAY_ENV)
+        raise ImageDisplayError("No w3mimgdisplay executable found.  Please set "
+                                "the path manually by setting the %s environment variable.  (see "
+                                "man page)" % W3MIMGDISPLAY_ENV)
 
     def _get_font_dimensions(self):
         # Get the height and width of a character displayed in the terminal in
@@ -110,7 +114,11 @@ class W3MImageDisplayer(ImageDisplayer):
     def draw(self, path, start_x, start_y, width, height):
         if not self.is_initialized or self.process.poll() is not None:
             self.initialize()
-        self.process.stdin.write(self._generate_w3m_input(path, start_x, start_y, width, height))
+        try:
+            input_gen = self._generate_w3m_input(path, start_x, start_y, width, height)
+        except ImageDisplayError:
+            raise
+        self.process.stdin.write(input_gen)
         self.process.stdin.flush()
         self.process.stdout.readline()
 
@@ -146,7 +154,7 @@ class W3MImageDisplayer(ImageDisplayer):
         """
         fontw, fonth = self._get_font_dimensions()
         if fontw == 0 or fonth == 0:
-            raise ImgDisplayUnsupportedException()
+            raise ImgDisplayUnsupportedException
 
         max_width_pixels = max_width * fontw
         max_height_pixels = max_height * fonth - 2
@@ -161,7 +169,7 @@ class W3MImageDisplayer(ImageDisplayer):
         output = self.process.stdout.readline().split()
 
         if len(output) != 2:
-            raise Exception('Failed to execute w3mimgdisplay', output)
+            raise ImageDisplayError('Failed to execute w3mimgdisplay', output)
 
         width = int(output[0])
         height = int(output[1])
@@ -257,13 +265,8 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware):
     @staticmethod
     def _encode_image_content(path):
         """Read and encode the contents of path"""
-        fobj = open(path, 'rb')
-        try:
+        with open(path, 'rb') as fobj:
             return base64.b64encode(fobj.read())
-        except Exception:
-            return ""
-        finally:
-            fobj.close()
 
     @staticmethod
     def _get_image_dimensions(path):
@@ -296,7 +299,7 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware):
                     size = struct.unpack('>H', file_handle.read(2))[0] - 2
                 file_handle.seek(1, 1)
                 height, width = struct.unpack('>HH', file_handle.read(4))
-            except Exception:
+            except OSError:
                 file_handle.close()
                 return 0, 0
         else:
diff --git a/ranger/ext/keybinding_parser.py b/ranger/ext/keybinding_parser.py
index 34282767..e5fb6a86 100644
--- a/ranger/ext/keybinding_parser.py
+++ b/ranger/ext/keybinding_parser.py
@@ -153,8 +153,11 @@ def _unbind_traverse(pointer, keys, pos=0):
     elif len(keys) == pos + 1:
         try:
             del pointer[keys[pos]]
+        except KeyError:
+            pass
+        try:
             keys.pop()
-        except Exception:
+        except IndexError:
             pass
 
 
@@ -174,7 +177,7 @@ class KeyMaps(dict):
     def _clean_input(self, context, keys):
         try:
             pointer = self[context]
-        except Exception:
+        except KeyError:
             self[context] = pointer = dict()
         if PY3:
             keys = keys.encode('utf-8').decode('latin-1')
@@ -191,7 +194,7 @@ class KeyMaps(dict):
                     pointer = pointer[key]
                 else:
                     pointer[key] = pointer = dict()
-            except Exception:
+            except KeyError:
                 pointer[key] = pointer = dict()
         pointer[last_key] = leaf
 
@@ -202,7 +205,7 @@ class KeyMaps(dict):
         for key in clean_source:
             try:
                 pointer = pointer[key]
-            except Exception:
+            except KeyError:
                 raise KeyError("Tried to copy the keybinding `%s',"
                                " but it was not found." % source)
         self.bind(context, target, copy.deepcopy(pointer))
diff --git a/ranger/ext/logutils.py b/ranger/ext/logutils.py
index b00aa605..38fc7cda 100644
--- a/ranger/ext/logutils.py
+++ b/ranger/ext/logutils.py
@@ -3,10 +3,6 @@ from __future__ import (absolute_import, division, print_function)
 import logging
 from collections import deque
 
-LOG_FORMAT = "[%(levelname)s] %(message)s"
-LOG_FORMAT_EXT = "%(asctime)s,%(msecs)d [%(name)s] |%(levelname)s| %(message)s"
-LOG_DATA_FORMAT = "%H:%M:%S"
-
 
 class QueueHandler(logging.Handler):
     """
@@ -30,11 +26,11 @@ class QueueHandler(logging.Handler):
         self.enqueue(self.format(record))
 
 
-# pylint: disable=invalid-name
-log_queue = deque(maxlen=1000)
-concise_formatter = logging.Formatter(fmt=LOG_FORMAT, datefmt=LOG_DATA_FORMAT)
-extended_formatter = logging.Formatter(fmt=LOG_FORMAT_EXT, datefmt=LOG_DATA_FORMAT)
-# pylint: enable=invalid-name
+QUEUE = deque(maxlen=1000)
+FMT_NORMAL = logging.Formatter(
+    fmt='%(asctime)s %(levelname).4s %(message)s', datefmt='%H:%M:%S')
+FMT_DEBUG = logging.Formatter(
+    fmt='%(asctime)s.%(msecs)03d %(levelname).4s [%(name)s] %(message)s', datefmt='%H:%M:%S')
 
 
 def setup_logging(debug=True, logfile=None):
@@ -57,17 +53,14 @@ def setup_logging(debug=True, logfile=None):
     root_logger = logging.getLogger()
 
     if debug:
-        # print all logging in extended format
         log_level = logging.DEBUG
-        formatter = extended_formatter
+        formatter = FMT_DEBUG
     else:
-        # print only warning and critical message
-        # in a concise format
         log_level = logging.INFO
-        formatter = concise_formatter
+        formatter = FMT_NORMAL
 
     handlers = []
-    handlers.append(QueueHandler(log_queue))
+    handlers.append(QueueHandler(QUEUE))
     if logfile:
         if logfile == '-':
             handlers.append(logging.StreamHandler())
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index 551dd288..c6b96f73 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -94,7 +94,7 @@ def _is_terminal():
         os.ttyname(0)
         os.ttyname(1)
         os.ttyname(2)
-    except Exception:
+    except OSError:
         return False
     return True
 
@@ -168,17 +168,13 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
             line = line.strip()
             if line.startswith('#') or line == '':
                 continue
-            try:
-                if self.delimiter1 not in line:
-                    raise Exception("Line without delimiter")
-                tests, command = line.split(self.delimiter1, 1)
-                tests = tests.split(self.delimiter2)
-                tests = tuple(tuple(f.strip().split(None, 1)) for f in tests)
-                command = command.strip()
-                self.rules.append((command, tests))
-            except Exception as ex:
-                self.hook_logger(
-                    "Syntax error in %s line %d (%s)" % (config_file, lineno, str(ex)))
+            if self.delimiter1 not in line:
+                raise ValueError("Line without delimiter")
+            tests, command = line.split(self.delimiter1, 1)
+            tests = tests.split(self.delimiter2)
+            tests = tuple(tuple(f.strip().split(None, 1)) for f in tests)
+            command = command.strip()
+            self.rules.append((command, tests))
         fobj.close()
 
     def _eval_condition(self, condition, files, label):
diff --git a/ranger/ext/shutil_generatorized.py b/ranger/ext/shutil_generatorized.py
index 338ed5b8..f1cf5a46 100644
--- a/ranger/ext/shutil_generatorized.py
+++ b/ranger/ext/shutil_generatorized.py
@@ -95,17 +95,17 @@ def copystat(src, dst):
     if hasattr(os, 'utime'):
         try:
             os.utime(dst, (fstat.st_atime, fstat.st_mtime))
-        except Exception:
+        except OSError:
             pass
     if hasattr(os, 'chmod'):
         try:
             os.chmod(dst, mode)
-        except Exception:
+        except OSError:
             pass
     if hasattr(os, 'chflags') and hasattr(fstat, 'st_flags'):
         try:
             os.chflags(dst, fstat.st_flags)  # pylint: disable=no-member
-        except Exception:
+        except OSError:
             pass
 
 
@@ -182,7 +182,7 @@ def copytree(src, dst,  # pylint: disable=too-many-locals,too-many-branches
     errors = []
     try:
         os.makedirs(dst)
-    except Exception as err:
+    except OSError:
         if not overwrite:
             dst = get_safe_path(dst)
             os.makedirs(dst)
@@ -212,10 +212,10 @@ def copytree(src, dst,  # pylint: disable=too-many-locals,too-many-branches
                 done += n
         # catch the Error from the recursive copytree so that we can
         # continue with other files
-        except Error as err:
-            errors.extend(err.args[0])
-        except EnvironmentError as why:
-            errors.append((srcname, dstname, str(why)))
+        except Error as ex:
+            errors.extend(ex.args[0])
+        except EnvironmentError as ex:
+            errors.append((srcname, dstname, str(ex)))
     try:
         copystat(src, dst)
     except OSError as why:
diff --git a/ranger/ext/signals.py b/ranger/ext/signals.py
index 4d2ee25e..50141565 100644
--- a/ranger/ext/signals.py
+++ b/ranger/ext/signals.py
@@ -137,7 +137,7 @@ class SignalDispatcher(object):
         assert isinstance(weak, bool)
         try:
             handlers = self._signals[signal_name]
-        except Exception:
+        except KeyError:
             handlers = self._signals[signal_name] = []
         nargs = function.__code__.co_argcount
 
@@ -180,13 +180,13 @@ class SignalDispatcher(object):
         try:
             handlers = self._signals[
                 signal_handler._signal_name]  # pylint: disable=protected-access
-        except Exception:
+        except KeyError:
             pass
         else:
+            signal_handler._function = None  # pylint: disable=protected-access
             try:
-                signal_handler._function = None  # pylint: disable=protected-access
                 handlers.remove(signal_handler)
-            except Exception:
+            except IndexError:
                 pass
 
     def signal_garbage_collect(self):
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index 8b13badd..00f51152 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -9,22 +9,14 @@ import os
 import subprocess
 import threading
 import time
-from logging import getLogger
 
 from ranger.ext import spawn
 
-# Python2 compatibility
+# Python 2 compatibility
 try:
     import queue
 except ImportError:
     import Queue as queue  # pylint: disable=import-error
-try:
-    FileNotFoundError
-except NameError:
-    FileNotFoundError = OSError  # pylint: disable=redefined-builtin
-
-
-LOG = getLogger(__name__)
 
 
 class VcsError(Exception):
@@ -133,7 +125,7 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
             else:
                 with open(os.devnull, mode='w') as fd_devnull:
                     subprocess.check_call(cmd, cwd=path, stdout=fd_devnull, stderr=fd_devnull)
-        except (subprocess.CalledProcessError, FileNotFoundError):
+        except (subprocess.CalledProcessError, OSError):
             raise VcsError('{0:s}: {1:s}'.format(str(cmd), path))
 
     def _get_repotype(self, path):
@@ -238,9 +230,8 @@ class VcsRoot(Vcs):  # pylint: disable=abstract-method
             self.branch = self.data_branch()
             self.obj.vcsremotestatus = self.data_status_remote()
             self.obj.vcsstatus = self.data_status_root()
-        except VcsError as error:
-            LOG.exception(error)
-            self.obj.fm.notify('VCS Exception: View log for more info', bad=True)
+        except VcsError as ex:
+            self.obj.fm.notify('VCS Exception: View log for more info', bad=True, exception=ex)
             return False
         self.rootinit = True
         return True
@@ -253,9 +244,8 @@ class VcsRoot(Vcs):  # pylint: disable=abstract-method
             self.status_subpaths = self.data_status_subpaths()
             self.obj.vcsremotestatus = self.data_status_remote()
             self.obj.vcsstatus = self._status_root()
-        except VcsError as error:
-            LOG.exception(error)
-            self.obj.fm.notify('VCS Exception: View log for more info', bad=True)
+        except VcsError as ex:
+            self.obj.fm.notify('VCS Exception: View log for more info', bad=True, exception=ex)
             return False
         self.rootinit = True
         self.updatetime = time.time()
@@ -479,9 +469,8 @@ class VcsThread(threading.Thread):  # pylint: disable=too-many-instance-attribut
                             column.need_redraw = True
                     self.ui.status.need_redraw = True
                     self.ui.redraw()
-            except Exception as error:  # pylint: disable=broad-except
-                LOG.exception(error)
-                self.ui.fm.notify('VCS Exception: View log for more info', bad=True)
+            except Exception as ex:  # pylint: disable=broad-except
+                self.ui.fm.notify('VCS Exception: View log for more info', bad=True, exception=ex)
 
     def pause(self):
         """Pause thread"""
diff --git a/ranger/ext/widestring.py b/ranger/ext/widestring.py
index 23e3d050..ed66f3c7 100644
--- a/ranger/ext/widestring.py
+++ b/ranger/ext/widestring.py
@@ -62,10 +62,7 @@ class WideString(object):  # pylint: disable=too-few-public-methods
         except UnicodeEncodeError:
             # Here I assume that string is a "unicode" object, because why else
             # would str(string) raise a UnicodeEncodeError?
-            try:
-                self.string = string.encode('latin-1', 'ignore')
-            except Exception:
-                self.string = ""
+            self.string = string.encode('latin-1', 'ignore')
         if chars is None:
             self.chars = string_to_charlist(string)
         else:
diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py
index 4bfaa0a0..5cb2ab90 100644
--- a/ranger/gui/ansi.py
+++ b/ranger/gui/ansi.py
@@ -51,7 +51,7 @@ def text_with_fg_bg_attr(ansi_text):  # pylint: disable=too-many-branches,too-ma
                         n = int(arg)
                     else:                         # empty code means reset
                         n = 0
-                except Exception:
+                except ValueError:
                     continue
 
                 if n == 0:                        # reset colors and attributes
diff --git a/ranger/gui/color.py b/ranger/gui/color.py
index 7f2b73af..45f983e8 100644
--- a/ranger/gui/color.py
+++ b/ranger/gui/color.py
@@ -30,7 +30,7 @@ def get_color(fg, bg):
         size = len(COLOR_PAIRS)
         try:
             curses.init_pair(size, fg, bg)
-        except Exception:
+        except curses.error:
             # If curses.use_default_colors() failed during the initialization
             # of curses, then using -1 as fg or bg will fail as well, which
             # we need to handle with fallback-defaults:
@@ -41,7 +41,7 @@ def get_color(fg, bg):
 
             try:
                 curses.init_pair(size, fg, bg)
-            except Exception:
+            except curses.error:
                 # If this fails too, colors are probably not supported
                 pass
         COLOR_PAIRS[key] = size
diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py
index c5aa0e31..75a82146 100644
--- a/ranger/gui/colorscheme.py
+++ b/ranger/gui/colorscheme.py
@@ -37,6 +37,10 @@ from ranger.ext.cached_function import cached_function
 from ranger.ext.iter_tools import flatten
 
 
+class ColorSchemeError(Exception):
+    pass
+
+
 class ColorScheme(object):
     """This is the class that colorschemes must inherit from.
 
@@ -96,7 +100,7 @@ def _colorscheme_name_to_class(signal):  # pylint: disable=too-many-branches
     def is_scheme(cls):
         try:
             return issubclass(cls, ColorScheme)
-        except Exception:
+        except TypeError:
             return False
 
     # create ~/.config/ranger/colorschemes/__init__.py if it doesn't exist
@@ -120,7 +124,7 @@ def _colorscheme_name_to_class(signal):  # pylint: disable=too-many-branches
             signal.value = signal.previous
         else:
             signal.value = ColorScheme()
-        raise Exception("Cannot locate colorscheme `%s'" % scheme_name)
+        raise ColorSchemeError("Cannot locate colorscheme `%s'" % scheme_name)
     else:
         if usecustom:
             allow_access_to_confdir(ranger.args.confdir, True)
@@ -137,7 +141,7 @@ def _colorscheme_name_to_class(signal):  # pylint: disable=too-many-branches
                     signal.value = var()
                     break
             else:
-                raise Exception("The module contains no valid colorscheme!")
+                raise ColorSchemeError("The module contains no valid colorscheme!")
 
 
 def get_all_colorschemes():
diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py
index 2c411058..648292c5 100644
--- a/ranger/gui/curses_shortcuts.py
+++ b/ranger/gui/curses_shortcuts.py
@@ -36,13 +36,13 @@ class CursesShortcuts(SettingsAware):
 
         try:
             self.win.addstr(*args)
-        except Exception:
+        except curses.error:
             if len(args) > 1:
                 self.win.move(y, x)
 
                 try:
                     self.win.addstr(*_fix_surrogates(args))
-                except Exception:
+                except curses.error:
                     pass
 
     def addnstr(self, *args):
@@ -50,13 +50,13 @@ class CursesShortcuts(SettingsAware):
 
         try:
             self.win.addnstr(*args)
-        except Exception:
+        except curses.error:
             if len(args) > 2:
                 self.win.move(y, x)
 
                 try:
                     self.win.addnstr(*_fix_surrogates(args))
-                except Exception:
+                except curses.error:
                     pass
 
     def addch(self, *args):
@@ -64,7 +64,7 @@ class CursesShortcuts(SettingsAware):
             args = [args[1], args[0]] + list(args[2:])
         try:
             self.win.addch(*args)
-        except Exception:
+        except curses.error:
             pass
 
     def color(self, *keys):
diff --git a/ranger/gui/displayable.py b/ranger/gui/displayable.py
index 2ce23165..926fd857 100644
--- a/ranger/gui/displayable.py
+++ b/ranger/gui/displayable.py
@@ -3,6 +3,8 @@
 
 from __future__ import (absolute_import, division, print_function)
 
+import curses
+
 from ranger.core.shared import FileManagerAware
 from ranger.gui.curses_shortcuts import CursesShortcuts
 
@@ -176,13 +178,13 @@ class Displayable(  # pylint: disable=too-many-instance-attributes
             window_is_cleared = True
             try:
                 self.win.resize(hei, wid)
-            except Exception:
+            except curses.error:
                 # Not enough space for resizing...
                 try:
                     self.win.mvderwin(0, 0)
                     do_move = True
                     self.win.resize(hei, wid)
-                except Exception:
+                except curses.error:
                     pass
                     # raise ValueError("Resizing Failed!")
 
@@ -195,7 +197,7 @@ class Displayable(  # pylint: disable=too-many-instance-attributes
             # log("moving " + str(self))
             try:
                 self.win.mvderwin(y, x)
-            except Exception:
+            except curses.error:
                 pass
 
             self.paryx = self.win.getparyx()
diff --git a/ranger/gui/mouse_event.py b/ranger/gui/mouse_event.py
index 160351d3..0eccbf57 100644
--- a/ranger/gui/mouse_event.py
+++ b/ranger/gui/mouse_event.py
@@ -32,7 +32,7 @@ class MouseEvent(object):
         """Returns whether the mouse key n is pressed"""
         try:
             return (self.bstate & MouseEvent.PRESSED[n]) != 0
-        except Exception:
+        except IndexError:
             return False
 
     def mouse_wheel_direction(self):
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 6c84580b..20e01536 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -91,12 +91,12 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
         curses.halfdelay(20)
         try:
             curses.curs_set(int(bool(self.settings.show_cursor)))
-        except Exception:
+        except curses.error:
             pass
         curses.start_color()
         try:
             curses.use_default_colors()
-        except Exception:
+        except curses.error:
             pass
 
         self.settings.signal_bind('setopt.mouse_enabled', _setup_mouse)
@@ -130,7 +130,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
         curses.echo()
         try:
             curses.curs_set(1)
-        except Exception:
+        except curses.error:
             pass
         if self.settings.mouse_enabled:
             _setup_mouse(dict(value=False))
@@ -346,14 +346,16 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
             try:
                 fixed_cwd = cwd.encode('utf-8', 'surrogateescape'). \
                     decode('utf-8', 'replace')
-                sys.stdout.write("%sranger:%s%s" % (
+                fmt_tup = (
                     curses.tigetstr('tsl').decode('latin-1'),
                     fixed_cwd,
                     curses.tigetstr('fsl').decode('latin-1'),
-                ))
-                sys.stdout.flush()
-            except Exception:
+                )
+            except UnicodeError:
                 pass
+            else:
+                sys.stdout.write("%sranger:%s%s" % fmt_tup)
+                sys.stdout.flush()
 
         self.win.refresh()
 
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 6f890dab..b0435785 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -5,6 +5,7 @@
 
 from __future__ import (absolute_import, division, print_function)
 
+import curses
 import stat
 from time import time
 from os.path import splitext
@@ -76,13 +77,14 @@ class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
                 elif event.pressed(3):
                     try:
                         clicked_file = self.target.files[index]
+                    except IndexError:
+                        pass
+                    else:
                         if clicked_file.is_directory:
                             self.fm.enter_dir(clicked_file.path)
                         elif self.level == 0:
                             self.fm.thisdir.move_to_obj(clicked_file)
                             self.fm.execute_file(clicked_file)
-                    except Exception:
-                        pass
 
         else:
             if self.level > 0 and not direction:
@@ -102,7 +104,7 @@ class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
         """
         try:
             self.win.move(line, 0)
-        except Exception:
+        except curses.error:
             return
         for entry in commands:
             text, attr = entry
@@ -363,11 +365,10 @@ class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
             predisplay_left += textstring
             space -= textstringlen
 
+            assert space >= 0, "Error: there is not enough space to write the text. " \
+                "I have computed spaces wrong."
             if space > 0:
                 predisplay_left.append([' ' * space, []])
-            elif space < 0:
-                raise Exception("Error: there is not enough space to write "
-                                "the text. I have computed spaces wrong.")
 
             # Computing display data. Now we compute the display_data list
             # ready to display in curses. It is a list of lists [string, attr]
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 0fa941d4..a2e058ad 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -42,8 +42,8 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
             self.historypath = self.fm.confpath('history')
             try:
                 fobj = open(self.historypath, 'r')
-            except Exception:
-                pass
+            except OSError as ex:
+                self.fm.notify('Unable to read history file', bad=True, exception=ex)
             else:
                 for line in fobj:
                     self.history.add(line[:-1])
@@ -70,8 +70,8 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
         if self.historypath:
             try:
                 fobj = open(self.historypath, 'w')
-            except Exception:
-                pass
+            except OSError as ex:
+                self.fm.notify('Unable to write history file', bad=True, exception=ex)
             else:
                 for entry in self.history_backup:
                     try:
@@ -102,13 +102,13 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
         if self.question_queue:
             try:
                 move(self.y, len(self.question_queue[0][0]))
-            except Exception:
+            except curses.error:
                 pass
         else:
             try:
                 pos = uwid(self.line[0:self.pos]) + len(self.prompt)
                 move(self.y, self.x + min(self.wid - 1, pos))
-            except Exception:
+            except curses.error:
                 pass
 
     def open(self, string='', prompt=None, position=None):
@@ -121,7 +121,7 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
         if self.last_cursor_mode is None:
             try:
                 self.last_cursor_mode = curses.curs_set(1)
-            except Exception:
+            except curses.error:
                 pass
         self.allow_close = False
         self.tab_deque = None
@@ -150,14 +150,11 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
         if trigger_cancel_function:
             cmd = self._get_cmd(quiet=True)
             if cmd:
-                try:
-                    cmd.cancel()
-                except Exception as error:
-                    self.fm.notify(error)
+                cmd.cancel()
         if self.last_cursor_mode is not None:
             try:
                 curses.curs_set(self.last_cursor_mode)
-            except Exception:
+            except curses.error:
                 pass
             self.last_cursor_mode = None
         self.fm.hide_console_info()
@@ -446,8 +443,6 @@ class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-
             if not quiet:
                 error = "Command not found: `%s'" % self.line.split()[0]
                 self.fm.notify(error, bad=True)
-        except Exception:
-            return None
         else:
             return command_class(self.line)
 
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index 2ecf65c0..bf7ca5f4 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -5,6 +5,8 @@
 
 from __future__ import (absolute_import, division, print_function)
 
+import curses
+
 from ranger.gui import ansi
 from ranger.ext.direction import Direction
 from ranger.ext.img_display import ImgDisplayUnsupportedException
@@ -99,7 +101,7 @@ class Pager(Widget):  # pylint: disable=too-many-instance-attributes
                                              self.wid, self.hei)
             except ImgDisplayUnsupportedException:
                 self.fm.settings.preview_images = False
-            except Exception as ex:
+            except Exception as ex:  # pylint: disable=broad-except
                 self.fm.notify(ex, bad=True)
             else:
                 self.image_drawn = True
@@ -110,7 +112,7 @@ class Pager(Widget):  # pylint: disable=too-many-instance-attributes
         elif self.markup == 'ansi':
             try:
                 self.win.move(i, 0)
-            except Exception:
+            except curses.error:
                 pass
             else:
                 for chunk in ansi.text_with_fg_bg_attr(line):
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index c9133415..8e9e259e 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -10,6 +10,7 @@ such as the space used by all the files in this directory.
 
 from __future__ import (absolute_import, division, print_function)
 
+import curses
 import os
 from os import getuid, readlink
 from pwd import getpwuid
@@ -77,7 +78,7 @@ class StatusBar(Widget):  # pylint: disable=too-many-instance-attributes
             self.fm.thisfile.load_if_outdated()
             try:
                 ctime = self.fm.thisfile.stat.st_ctime
-            except Exception:
+            except AttributeError:
                 ctime = -1
         else:
             ctime = -1
@@ -131,7 +132,7 @@ class StatusBar(Widget):  # pylint: disable=too-many-instance-attributes
 
             try:
                 self.addnstr(0, starting_point, string, space_left)
-            except Exception:
+            except curses.error:
                 break
             space_left -= len(string)
             starting_point += len(string)
@@ -150,7 +151,7 @@ class StatusBar(Widget):  # pylint: disable=too-many-instance-attributes
                 return
         try:
             stat = target.stat
-        except Exception:
+        except AttributeError:
             return
         if stat is None:
             return
@@ -172,7 +173,7 @@ class StatusBar(Widget):  # pylint: disable=too-many-instance-attributes
             how = 'good' if target.exists else 'bad'
             try:
                 dest = readlink(target.path)
-            except Exception:
+            except OSError:
                 dest = '?'
             left.add(' -> ' + dest, 'link', how)
         else:
diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py
index 425dd64c..a2c54516 100644
--- a/ranger/gui/widgets/titlebar.py
+++ b/ranger/gui/widgets/titlebar.py
@@ -74,10 +74,7 @@ class TitleBar(Widget):
                 elif i == 2:
                     self.fm.enter_dir("/")
                 else:
-                    try:
-                        self.fm.enter_dir(part.directory)
-                    except Exception:
-                        pass
+                    self.fm.enter_dir(part.directory)
                 return True
         return False
 
diff --git a/ranger/gui/widgets/view_base.py b/ranger/gui/widgets/view_base.py
index 8807628b..5cdb2615 100644
--- a/ranger/gui/widgets/view_base.py
+++ b/ranger/gui/widgets/view_base.py
@@ -52,15 +52,15 @@ class ViewBase(Widget, DisplayableContainer):  # pylint: disable=too-many-instan
         if self.pager is not None and self.pager.visible:
             try:
                 self.fm.ui.win.move(self.main_column.y, self.main_column.x)
-            except Exception:
+            except curses.error:
                 pass
         else:
+            col_x = self.main_column.x
+            col_y = self.main_column.y + self.main_column.target.pointer \
+                - self.main_column.scroll_begin
             try:
-                col_x = self.main_column.x
-                col_y = self.main_column.y + self.main_column.target.pointer \
-                    - self.main_column.scroll_begin
                 self.fm.ui.win.move(col_y, col_x)
-            except Exception:
+            except curses.error:
                 pass
 
     def _draw_bookmarks(self):
@@ -127,7 +127,7 @@ class ViewBase(Widget, DisplayableContainer):  # pylint: disable=too-many-instan
         self.addnstr(ystart - 1, 0, "key          command".ljust(self.wid), self.wid)
         try:
             self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
-        except Exception:
+        except curses.error:
             pass
         whitespace = " " * self.wid
         i = ystart
diff --git a/ranger/gui/widgets/view_miller.py b/ranger/gui/widgets/view_miller.py
index 569b93fa..e79ade79 100644
--- a/ranger/gui/widgets/view_miller.py
+++ b/ranger/gui/widgets/view_miller.py
@@ -155,7 +155,7 @@ class ViewMiller(ViewBase):  # pylint: disable=too-many-ancestors,too-many-insta
                 self.addch(0, x, curses.ACS_TTEE, 0)
                 self.addch(y, x, curses.ACS_BTEE, 0)
                 # pylint: enable=no-member
-            except Exception:
+            except curses.error:
                 # in case it's off the boundaries
                 pass
 
@@ -186,7 +186,7 @@ class ViewMiller(ViewBase):  # pylint: disable=too-many-ancestors,too-many-insta
                     self.fm.settings.use_preview_script:
                 try:
                     result = not self.fm.previews[target.realpath]['foundpreview']
-                except Exception:
+                except KeyError:
                     return self.old_collapse
 
         self.old_collapse = result