summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.pylintrc15
-rw-r--r--.travis.yml23
-rw-r--r--Makefile8
-rwxr-xr-xdoc/tools/convert_papermode_to_metadata.py10
-rwxr-xr-xdoc/tools/print_colors.py15
-rwxr-xr-xdoc/tools/print_keys.py21
-rw-r--r--examples/plugin_chmod_keybindings.py7
-rw-r--r--examples/plugin_file_filter.py11
-rw-r--r--examples/plugin_hello_world.py5
-rw-r--r--examples/plugin_ipc.py15
-rw-r--r--examples/plugin_linemode.py4
-rw-r--r--examples/plugin_new_macro.py14
-rw-r--r--examples/plugin_new_sorting_method.py4
-rw-r--r--examples/plugin_pmount.py11
-rwxr-xr-xranger.py14
-rw-r--r--ranger/__init__.py3
-rw-r--r--ranger/api/__init__.py11
-rw-r--r--ranger/api/commands.py54
-rw-r--r--ranger/api/options.py11
-rw-r--r--ranger/colorschemes/default.py8
-rw-r--r--ranger/colorschemes/jungle.py2
-rw-r--r--ranger/colorschemes/snow.py2
-rw-r--r--ranger/colorschemes/solarized.py8
-rw-r--r--ranger/config/.pylintrc8
-rwxr-xr-xranger/config/commands.py171
-rw-r--r--ranger/config/commands_sample.py9
-rw-r--r--ranger/container/bookmarks.py16
-rw-r--r--ranger/container/directory.py63
-rw-r--r--ranger/container/file.py17
-rw-r--r--ranger/container/fsobject.py63
-rw-r--r--ranger/container/history.py6
-rw-r--r--ranger/container/settings.py20
-rw-r--r--ranger/container/tags.py34
-rw-r--r--ranger/core/actions.py255
-rw-r--r--ranger/core/fm.py52
-rw-r--r--ranger/core/linemode.py7
-rw-r--r--ranger/core/loader.py119
-rw-r--r--ranger/core/main.py89
-rw-r--r--ranger/core/metadata.py21
-rw-r--r--ranger/core/runner.py27
-rw-r--r--ranger/core/shared.py6
-rw-r--r--ranger/core/tab.py13
-rw-r--r--ranger/ext/accumulator.py6
-rw-r--r--ranger/ext/cached_function.py2
-rw-r--r--ranger/ext/curses_interrupt_handler.py9
-rw-r--r--ranger/ext/direction.py7
-rw-r--r--ranger/ext/get_executables.py7
-rw-r--r--ranger/ext/human_readable.py3
-rw-r--r--ranger/ext/img_display.py75
-rw-r--r--ranger/ext/iter_tools.py1
-rw-r--r--ranger/ext/keybinding_parser.py55
-rw-r--r--ranger/ext/lazy_property.py3
-rw-r--r--ranger/ext/logutils.py2
-rw-r--r--ranger/ext/popen_forked.py4
-rwxr-xr-xranger/ext/rifle.py62
-rw-r--r--ranger/ext/shell_escape.py4
-rw-r--r--ranger/ext/shutil_generatorized.py52
-rw-r--r--ranger/ext/signals.py26
-rw-r--r--ranger/ext/widestring.py44
-rw-r--r--ranger/gui/ansi.py9
-rw-r--r--ranger/gui/bar.py13
-rw-r--r--ranger/gui/color.py3
-rw-r--r--ranger/gui/colorscheme.py11
-rw-r--r--ranger/gui/context.py17
-rw-r--r--ranger/gui/curses_shortcuts.py2
-rw-r--r--ranger/gui/displayable.py11
-rw-r--r--ranger/gui/ui.py23
-rw-r--r--ranger/gui/widgets/browsercolumn.py30
-rw-r--r--ranger/gui/widgets/console.py36
-rw-r--r--ranger/gui/widgets/pager.py20
-rw-r--r--ranger/gui/widgets/statusbar.py20
-rw-r--r--ranger/gui/widgets/taskview.py5
-rw-r--r--ranger/gui/widgets/titlebar.py12
-rw-r--r--ranger/gui/widgets/view_base.py23
-rw-r--r--ranger/gui/widgets/view_miller.py22
-rw-r--r--ranger/gui/widgets/view_multipane.py6
-rwxr-xr-xsetup.py18
-rw-r--r--tests/ranger/container/test_bookmarks.py1
-rw-r--r--tests/ranger/container/test_container.py96
-rw-r--r--tests/ranger/container/test_fsobject.py11
-rw-r--r--tox.ini2
81 files changed, 1105 insertions, 920 deletions
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 00000000..db515b53
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,15 @@
+[BASIC]
+good-names=i,j,k,n,x,y,ex,Run,_,fm,ui,fg,bg
+bad-names=foo,baz,toto,tutu,tata
+
+[DESIGN]
+max-args=6
+max-branches=16
+
+[FORMAT]
+max-line-length = 99
+disable=locally-disabled,locally-enabled,missing-docstring,duplicate-code,fixme,consider-iterating-dictionary,broad-except,cyclic-import,attribute-defined-outside-init,access-member-before-definition
+
+[TYPECHECK]
+ignored-classes=ranger.ext.openstruct.OpenStruct,ranger.core.runner.Context,ranger.core.actions.Actions,ranger.gui.widgets.view_base.ViewBase,ranger.gui.curses_shortcuts.CursesShortcuts
+generated-members=ranger.arg
diff --git a/.travis.yml b/.travis.yml
index 2bc017d3..9b194d4b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,18 +1,15 @@
-language: python
+dist: 'trusty'
+language: 'python'
 python:
-  - "2.6"
-  - "2.7"
-  - "3.2"
-  - "3.3"
-  - "3.4"
-  - "3.5"
-  - "3.5-dev" # 3.5 development branch
-  - "nightly" # currently points to 3.6-dev
+    - '2.6'
+    - '2.7'
+    - '3.2'
+    - '3.3'
+    - '3.4'
+    - '3.5'
 
-# command to install dependencies
 install:
-  - pip install pytest
+    - 'pip install pytest pylint flake8'
 
-# command to run tests
 script:
-  - make test
+    - 'make test'
diff --git a/Makefile b/Makefile
index 870901da..beb0a325 100644
--- a/Makefile
+++ b/Makefile
@@ -60,7 +60,15 @@ doc: cleandoc
 		pydoc.writedocs("$(CWD)")'
 	find . -name \*.html -exec sed -i 's|'"$(CWD)"'|../..|g' -- {} \;
 
+TEST_PATHS_MAIN = doc/tools/*.py examples/*.py $(filter-out ranger/__pycache__ ranger/config ranger/data, $(wildcard ranger/*)) tests *.py
+TEST_PATH_CONFIG = ranger/config
+
 test:
+	@echo "Running pylint..."
+	pylint $(TEST_PATHS_MAIN)
+	pylint --rcfile=$(TEST_PATH_CONFIG)/.pylintrc $(TEST_PATH_CONFIG)
+	@echo "Running flake8..."
+	flake8 $(TEST_PATHS_MAIN) $(TEST_PATH_CONFIG)
 	@echo "Running doctests..."
 	@for FILE in $(shell grep -IHm 1 doctest -r ranger | grep $(FILTER) | cut -d: -f1); do \
 		echo "Testing $$FILE..."; \
diff --git a/doc/tools/convert_papermode_to_metadata.py b/doc/tools/convert_papermode_to_metadata.py
index 61427c83..e73df344 100755
--- a/doc/tools/convert_papermode_to_metadata.py
+++ b/doc/tools/convert_papermode_to_metadata.py
@@ -14,10 +14,9 @@ import json
 import os
 import sys
 
-if sys.version < '3.':
-    getuserinput = raw_input
-else:
-    getuserinput = input
+if sys.version_info[0] < 3:
+    input = raw_input  # NOQA pylint: disable=undefined-variable,redefined-builtin,invalid-name
+
 
 FIELDS = ["name", "year", "title", "authors", "url"]
 
@@ -30,7 +29,7 @@ def replace(source, target):
     # Ask for user confirmation if the target file already exists
     if os.path.exists(target):
         sys.stdout.write("Warning: target file `%s' exists! Overwrite? [y/N]")
-        userinput = getuserinput()
+        userinput = input()
         if not (userinput.startswith("y") or userinput.startswith("Y")):
             print("Skipping file `%s'" % source)
             return
@@ -63,6 +62,7 @@ def replace(source, target):
     else:
         print("Skipping writing `%s' due to a lack of data" % target)
 
+
 if __name__ == "__main__":
     if set(['--help', '-h']) & set(sys.argv[1:]):
         print(__doc__.strip())
diff --git a/doc/tools/print_colors.py b/doc/tools/print_colors.py
index 8cc944ed..c4b6e736 100755
--- a/doc/tools/print_colors.py
+++ b/doc/tools/print_colors.py
@@ -5,27 +5,26 @@ It will exit after a keypress.
 """
 
 import curses
-from curses import *
 
 
-@wrapper
+@curses.wrapper
 def main(win):
     def print_all_colors(attr):
-        for c in range(-1, curses.COLORS):
+        for color in range(-1, curses.COLORS):
             try:
-                init_pair(c, c, 0)
+                curses.init_pair(color, color, 0)
             except Exception:
                 pass
             else:
-                win.addstr(str(c) + ' ', color_pair(c) | attr)
-    start_color()
+                win.addstr(str(color) + ' ', curses.color_pair(color) | attr)
+    curses.start_color()
     try:
-        use_default_colors()
+        curses.use_default_colors()
     except Exception:
         pass
     win.addstr("available colors: %d\n\n" % curses.COLORS)
     print_all_colors(0)
     win.addstr("\n\n")
-    print_all_colors(A_BOLD)
+    print_all_colors(curses.A_BOLD)
     win.refresh()
     win.getch()
diff --git a/doc/tools/print_keys.py b/doc/tools/print_keys.py
index 73091db4..cf915591 100755
--- a/doc/tools/print_keys.py
+++ b/doc/tools/print_keys.py
@@ -3,18 +3,19 @@
 You can use this tool to find out values of keypresses
 """
 
-from curses import *
+import curses
 
-sep = '; '
 
+SEPARATOR = '; '
 
-@wrapper
-def main(w):
-    mousemask(ALL_MOUSE_EVENTS)
-    mouseinterval(0)
+
+@curses.wrapper
+def main(window):
+    curses.mousemask(curses.ALL_MOUSE_EVENTS)
+    curses.mouseinterval(0)
     while True:
-        ch = w.getch()
-        if ch == KEY_MOUSE:
-            w.addstr(repr(getmouse()) + sep)
+        char = window.getch()
+        if char == curses.KEY_MOUSE:
+            window.addstr(repr(curses.getmouse()) + SEPARATOR)
         else:
-            w.addstr(str(ch) + sep)
+            window.addstr(str(char) + SEPARATOR)
diff --git a/examples/plugin_chmod_keybindings.py b/examples/plugin_chmod_keybindings.py
index 63f42b0e..72e90121 100644
--- a/examples/plugin_chmod_keybindings.py
+++ b/examples/plugin_chmod_keybindings.py
@@ -5,11 +5,13 @@
 # for the "chmod" command.
 
 import ranger.api
-old_hook_init = ranger.api.hook_init
+
+
+HOOK_INIT_OLD = ranger.api.hook_init
 
 
 def hook_init(fm):
-    old_hook_init(fm)
+    HOOK_INIT_OLD(fm)
 
     # Generate key bindings for the chmod command
     command = "map {0}{1}{2} shell -d chmod {1}{0}{2} %s"
@@ -18,4 +20,5 @@ def hook_init(fm):
             fm.execute_console(command.format('-', mode, perm))
             fm.execute_console(command.format('+', mode, perm))
 
+
 ranger.api.hook_init = hook_init
diff --git a/examples/plugin_file_filter.py b/examples/plugin_file_filter.py
index 5d5f1466..486e59d9 100644
--- a/examples/plugin_file_filter.py
+++ b/examples/plugin_file_filter.py
@@ -5,19 +5,20 @@
 
 # Save the original filter function
 import ranger.container.directory
-old_accept_file = ranger.container.directory.accept_file
 
-HIDE_FILES = ("/boot", "/sbin", "/proc", "/sys")
 
-# Define a new one
+ACCEPT_FILE_OLD = ranger.container.directory.accept_file
+
+HIDE_FILES = ("/boot", "/sbin", "/proc", "/sys")
 
 
+# Define a new one
 def custom_accept_file(file, filters):
     if not file.fm.settings.show_hidden and file.path in HIDE_FILES:
         return False
     else:
-        return old_accept_file(file, filters)
+        return ACCEPT_FILE_OLD(file, filters)
+
 
 # Overwrite the old function
-import ranger.container.directory
 ranger.container.directory.accept_file = custom_accept_file
diff --git a/examples/plugin_hello_world.py b/examples/plugin_hello_world.py
index 0158a653..dc4c3fee 100644
--- a/examples/plugin_hello_world.py
+++ b/examples/plugin_hello_world.py
@@ -9,7 +9,7 @@ import ranger.api
 
 # Save the previously existing hook, because maybe another module already
 # extended that hook and we don't want to lose it:
-old_hook_ready = ranger.api.hook_ready
+HOOK_READY_OLD = ranger.api.hook_ready
 
 # Create a replacement for the hook that...
 
@@ -19,7 +19,8 @@ def hook_ready(fm):
     fm.notify("Hello World")
     # ...and calls the saved hook.  If you don't care about the return value,
     # simply return the return value of the previous hook to be safe.
-    return old_hook_ready(fm)
+    return HOOK_READY_OLD(fm)
+
 
 # Finally, "monkey patch" the existing hook_ready function with our replacement:
 ranger.api.hook_ready = hook_ready
diff --git a/examples/plugin_ipc.py b/examples/plugin_ipc.py
index 47fb1c84..ef87c3c5 100644
--- a/examples/plugin_ipc.py
+++ b/examples/plugin_ipc.py
@@ -9,15 +9,16 @@
 
 import ranger.api
 
-old_hook_init = ranger.api.hook_init
+
+HOOK_INIT_OLD = ranger.api.hook_init
 
 
 def hook_init(fm):
     try:
         # Create a FIFO.
         import os
-        IPC_FIFO = "/tmp/ranger-ipc." + str(os.getpid())
-        os.mkfifo(IPC_FIFO)
+        ipc_fifo = "/tmp/ranger-ipc." + str(os.getpid())
+        os.mkfifo(ipc_fifo)
 
         # Start the reader thread.
         try:
@@ -30,7 +31,7 @@ def hook_init(fm):
                 with open(filepath, 'r') as fifo:
                     line = fifo.read()
                     fm.execute_console(line.strip())
-        thread.start_new_thread(ipc_reader, (IPC_FIFO,))
+        thread.start_new_thread(ipc_reader, (ipc_fifo,))
 
         # Remove the FIFO on ranger exit.
         def ipc_cleanup(filepath):
@@ -39,10 +40,12 @@ def hook_init(fm):
             except IOError:
                 pass
         import atexit
-        atexit.register(ipc_cleanup, IPC_FIFO)
+        atexit.register(ipc_cleanup, ipc_fifo)
     except IOError:
         # IPC support disabled
         pass
     finally:
-        old_hook_init(fm)
+        HOOK_INIT_OLD(fm)
+
+
 ranger.api.hook_init = hook_init
diff --git a/examples/plugin_linemode.py b/examples/plugin_linemode.py
index 851d6213..6f16743d 100644
--- a/examples/plugin_linemode.py
+++ b/examples/plugin_linemode.py
@@ -6,6 +6,7 @@
 # the default linemode.
 
 import codecs
+
 import ranger.api
 from ranger.core.linemode import LinemodeBase
 
@@ -16,3 +17,6 @@ class MyLinemode(LinemodeBase):
 
     def filetitle(self, file, metadata):
         return codecs.encode(file.relative_path, "rot_13")
+
+    def infostring(self, file, metadata):
+        raise NotImplementedError
diff --git a/examples/plugin_new_macro.py b/examples/plugin_new_macro.py
index 8dbe435d..1c69b841 100644
--- a/examples/plugin_new_macro.py
+++ b/examples/plugin_new_macro.py
@@ -4,18 +4,20 @@
 # date in commands that allow macros.  You can test it with the command
 # ":shell echo %date; read"
 
-# Save the original macro function
+import time
+
 import ranger.core.actions
-old_get_macros = ranger.core.actions.Actions._get_macros
 
-# Define a new macro function
-import time
+# Save the original macro function
+GET_MACROS_OLD = ranger.core.actions.Actions._get_macros  # pylint: disable=protected-access
 
 
+# Define a new macro function
 def get_macros_with_date(self):
-    macros = old_get_macros(self)
+    macros = GET_MACROS_OLD(self)
     macros['date'] = time.strftime('%m/%d/%Y')
     return macros
 
+
 # Overwrite the old one
-ranger.core.actions.Actions._get_macros = get_macros_with_date
+ranger.core.actions.Actions._get_macros = get_macros_with_date  # pylint: disable=protected-access
diff --git a/examples/plugin_new_sorting_method.py b/examples/plugin_new_sorting_method.py
index 012bd7a2..2dd50257 100644
--- a/examples/plugin_new_sorting_method.py
+++ b/examples/plugin_new_sorting_method.py
@@ -3,6 +3,8 @@
 # This plugin adds the sorting algorithm called 'random'.  To enable it, type
 # ":set sort=random" or create a key binding with ":map oz set sort=random"
 
-from ranger.container.directory import Directory
 from random import random
+
+from ranger.container.directory import Directory
+
 Directory.sort_dict['random'] = lambda path: random()
diff --git a/examples/plugin_pmount.py b/examples/plugin_pmount.py
index fe0adffb..6cd325f7 100644
--- a/examples/plugin_pmount.py
+++ b/examples/plugin_pmount.py
@@ -15,7 +15,8 @@ MOUNT_KEY = '<alt>m'
 UMOUNT_KEY = '<alt>M'
 LIST_MOUNTS_KEY = '<alt>N'
 
-old_hook_init = ranger.api.hook_init
+
+HOOK_INIT_OLD = ranger.api.hook_init
 
 
 def hook_init(fm):
@@ -33,6 +34,10 @@ def hook_init(fm):
                 )
                 fm.execute_console("map {key}{0}{1} chain cd; shell pumount sd{0}{1}".format(
                     disk, part, key=UMOUNT_KEY))
-    finally:
-        return old_hook_init(fm)
+    except Exception:
+        pass
+
+    return HOOK_INIT_OLD(fm)
+
+
 ranger.api.hook_init = hook_init
diff --git a/ranger.py b/ranger.py
index 864acf27..f8c00213 100755
--- a/ranger.py
+++ b/ranger.py
@@ -20,20 +20,20 @@ if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
 fi
 rm -f -- "$tempfile"
 return $returnvalue
-""" and None
+""" and None  # pylint: disable=pointless-statement
 
-import sys
-from os.path import exists, abspath
+import sys  # NOQA pylint: disable=wrong-import-position
+from os.path import exists, abspath  # NOQA pylint: disable=wrong-import-position
 
 # Need to find out whether or not the flag --clean was used ASAP,
 # because --clean is supposed to disable bytecode compilation
-argv = sys.argv[1:sys.argv.index('--')] if '--' in sys.argv else sys.argv[1:]
-sys.dont_write_bytecode = '-c' in argv or '--clean' in argv
+ARGV = sys.argv[1:sys.argv.index('--')] if '--' in sys.argv else sys.argv[1:]
+sys.dont_write_bytecode = '-c' in ARGV or '--clean' in ARGV
 
 # Don't import ./ranger when running an installed binary at /usr/.../ranger
 if __file__[:4] == '/usr' and exists('ranger') and abspath('.') in sys.path:
     sys.path.remove(abspath('.'))
 
 # Start ranger
-import ranger
-sys.exit(ranger.main())
+import ranger  # NOQA pylint: disable=wrong-import-position,import-self
+sys.exit(ranger.main())  # pylint: disable=no-member
diff --git a/ranger/__init__.py b/ranger/__init__.py
index c2aa804e..e8f05c4d 100644
--- a/ranger/__init__.py
+++ b/ranger/__init__.py
@@ -10,7 +10,6 @@ program you want to use to open your files with.
 
 import sys
 import os
-import tempfile
 
 # Information
 __license__ = 'GPL3'
@@ -34,4 +33,4 @@ VERSION = 'ranger-master %s\n\nPython %s' % (__version__, sys.version)
 # and the configuration directory will be $XDG_CONFIG_HOME/ranger instead.
 CONFDIR = '~/.config/ranger'
 
-from ranger.core.main import main
+from ranger.core.main import main  # NOQA pylint: disable=wrong-import-position
diff --git a/ranger/api/__init__.py b/ranger/api/__init__.py
index 5981c31a..3c7818b8 100644
--- a/ranger/api/__init__.py
+++ b/ranger/api/__init__.py
@@ -3,10 +3,13 @@
 
 """Files in this module contain helper functions used in configuration files."""
 
-# Hooks for use in plugins:
+import ranger  # NOQA
+
+from ranger.core.linemode import LinemodeBase  # NOQA
 
 
-def hook_init(fm):
+# Hooks for use in plugins:
+def hook_init(fm):  # pylint: disable=unused-argument
     """A hook that is called when ranger starts up.
 
     Parameters:
@@ -20,7 +23,7 @@ def hook_init(fm):
     """
 
 
-def hook_ready(fm):
+def hook_ready(fm):  # pylint: disable=unused-argument
     """A hook that is called after the ranger UI is initialized.
 
     Parameters:
@@ -32,8 +35,6 @@ def hook_ready(fm):
     NOT print anything to stdout anymore from here on.  Use fm.notify instead.
     """
 
-from ranger.core.linemode import LinemodeBase
-
 
 def register_linemode(linemode_class):
     """Add a custom linemode class.  See ranger.core.linemode"""
diff --git a/ranger/api/commands.py b/ranger/api/commands.py
index 46075f26..76fe588a 100644
--- a/ranger/api/commands.py
+++ b/ranger/api/commands.py
@@ -4,11 +4,14 @@
 # TODO: Add an optional "!" to all commands and set a flag if it's there
 
 import os
-import ranger
 import re
 import inspect
-from collections import deque
-from ranger.api import *
+# COMPAT pylint: disable=unused-import
+from collections import deque  # NOQA
+from ranger.api import LinemodeBase, hook_init, hook_ready, register_linemode  # NOQA
+# pylint: enable=unused-import
+
+import ranger
 from ranger.core.shared import FileManagerAware
 from ranger.ext.lazy_property import lazy_property
 
@@ -26,10 +29,12 @@ class CommandContainer(object):
     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:
@@ -51,9 +56,11 @@ class CommandContainer(object):
             attribute = getattr(obj, attribute_name)
             if hasattr(attribute, '__call__'):
                 cmd = type(attribute_name, (FunctionCommand, ), dict(__doc__=attribute.__doc__))
+                # pylint: disable=protected-access
                 cmd._based_function = attribute
                 cmd._function_name = attribute.__name__
                 cmd._object_name = obj.__class__.__name__
+                # pylint: enable=protected-access
                 self.commands[attribute_name] = cmd
 
     def get_command(self, name, abbrev=True):
@@ -99,12 +106,12 @@ class Command(FileManagerAware):
             self.firstpart = ''
 
     @classmethod
-    def get_name(self):
-        classdict = self.__mro__[0].__dict__
+    def get_name(cls):
+        classdict = cls.__mro__[0].__dict__
         if 'name' in classdict and classdict['name']:
-            return self.name
+            return cls.name
         else:
-            return self.__name__
+            return cls.__name__
 
     def execute(self):
         """Override this"""
@@ -150,9 +157,6 @@ class Command(FileManagerAware):
         self._setting_line = None
         self._shifted += 1
 
-    def tabinsert(self, word):
-        return ''.join([self._tabinsert_left, word, self._tabinsert_right])
-
     def parse_setting_line(self):
         """
         Parses the command line argument that is passed to the `:set` command.
@@ -242,18 +246,6 @@ class Command(FileManagerAware):
         import logging
         return logging.getLogger('ranger.commands.' + self.__class__.__name__)
 
-    # XXX: Lazy properties? Not so smart? self.line can change after all!
-    @lazy_property
-    def _tabinsert_left(self):
-        try:
-            return self.line[:self.line[0:self.pos].rindex(' ') + 1]
-        except ValueError:
-            return ''
-
-    @lazy_property
-    def _tabinsert_right(self):
-        return self.line[self.pos:]
-
     # COMPAT: this is still used in old commands.py configs
     def _tab_only_directories(self):
         from os.path import dirname, basename, expanduser, join
@@ -301,7 +293,7 @@ class Command(FileManagerAware):
             return (self.start(1) + join(rel_dirname, dirname)
                     for dirname in dirnames)
 
-    def _tab_directory_content(self):
+    def _tab_directory_content(self):  # pylint: disable=too-many-locals
         from os.path import dirname, basename, expanduser, join
 
         cwd = self.fm.thisdir.path
@@ -383,14 +375,16 @@ class FunctionCommand(Command):
     _object_name = ""
     _function_name = "unknown"
 
-    def execute(self):
+    def execute(self):  # pylint: disable=too-many-branches
         if not self._based_function:
             return
         if len(self.args) == 1:
             try:
+                # pylint: disable=not-callable
                 return self._based_function(**{'narg': self.quantifier})
+                # pylint: enable=not-callable
             except TypeError:
-                return self._based_function()
+                return self._based_function()  # pylint: disable=not-callable
 
         args, keywords = list(), dict()
         for arg in self.args[1:]:
@@ -403,7 +397,7 @@ class FunctionCommand(Command):
                     value = (value == 'True')
                 else:
                     try:
-                        value = float(value)
+                        value = float(value)  # pylint: disable=redefined-variable-type
                     except Exception:
                         pass
 
@@ -417,13 +411,13 @@ class FunctionCommand(Command):
 
         try:
             if self.quantifier is None:
-                return self._based_function(*args, **keywords)
+                return self._based_function(*args, **keywords)  # pylint: disable=not-callable
             else:
                 try:
-                    return self._based_function(*args, **keywords)
+                    return self._based_function(*args, **keywords)  # pylint: disable=not-callable
                 except TypeError:
                     del keywords['narg']
-                    return self._based_function(*args, **keywords)
+                    return self._based_function(*args, **keywords)  # pylint: disable=not-callable
         except TypeError:
             if ranger.arg.debug:
                 raise
@@ -448,7 +442,7 @@ class AliasCommand(Command):
     def tab(self, tabnum):
         cmd = self._make_cmd()
         args = inspect.signature(cmd.tab).parameters if self.fm.py3 else \
-            inspect.getargspec(cmd.tab).args
+            inspect.getargspec(cmd.tab).args  # pylint: disable=deprecated-method
         return cmd.tab(tabnum) if 'tabnum' in args else cmd.tab()
 
     def cancel(self):
diff --git a/ranger/api/options.py b/ranger/api/options.py
index 0cce1364..3161a2c6 100644
--- a/ranger/api/options.py
+++ b/ranger/api/options.py
@@ -2,7 +2,10 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
 # THIS WHOLE FILE IS OBSOLETE AND EXISTS FOR BACKWARDS COMPATIBILITIY
-import re
-from re import compile as regexp
-from ranger.api import *
-from ranger.gui import color
+# pylint: disable=unused-import
+import re  # NOQA
+from re import compile as regexp  # NOQA
+
+from ranger.api import LinemodeBase, hook_init, hook_ready, register_linemode  # NOQA
+from ranger.gui import color  # NOQA
+# pylint: enable=unused-import
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index 8d1958c7..557b6075 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -2,13 +2,17 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
 from ranger.gui.colorscheme import ColorScheme
-from ranger.gui.color import *
+from ranger.gui.color import (
+    black, blue, cyan, green, magenta, red, white, yellow, default,
+    normal, bold, reverse,
+    default_colors,
+)
 
 
 class Default(ColorScheme):
     progress_bar_color = blue
 
-    def use(self, context):
+    def use(self, context):  # pylint: disable=too-many-branches,too-many-statements
         fg, bg, attr = default_colors
 
         if context.reset:
diff --git a/ranger/colorschemes/jungle.py b/ranger/colorschemes/jungle.py
index fbcfab7d..b56bb6ed 100644
--- a/ranger/colorschemes/jungle.py
+++ b/ranger/colorschemes/jungle.py
@@ -1,8 +1,8 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
-from ranger.gui.color import *
 from ranger.colorschemes.default import Default
+from ranger.gui.color import green, red, blue
 
 
 class Scheme(Default):
diff --git a/ranger/colorschemes/snow.py b/ranger/colorschemes/snow.py
index 95b4bfc3..315b4259 100644
--- a/ranger/colorschemes/snow.py
+++ b/ranger/colorschemes/snow.py
@@ -2,7 +2,7 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
 from ranger.gui.colorscheme import ColorScheme
-from ranger.gui.color import *
+from ranger.gui.color import default_colors, reverse, bold
 
 
 class Snow(ColorScheme):
diff --git a/ranger/colorschemes/solarized.py b/ranger/colorschemes/solarized.py
index c3c0cad6..09d66bff 100644
--- a/ranger/colorschemes/solarized.py
+++ b/ranger/colorschemes/solarized.py
@@ -6,13 +6,17 @@
 # This is a modification of Roman Zimbelmann's default colorscheme.
 
 from ranger.gui.colorscheme import ColorScheme
-from ranger.gui.color import *
+from ranger.gui.color import (
+    cyan, magenta, red, white, default,
+    normal, bold, reverse,
+    default_colors,
+)
 
 
 class Solarized(ColorScheme):
     progress_bar_color = 33
 
-    def use(self, context):
+    def use(self, context):  # pylint: disable=too-many-branches,too-many-statements
         fg, bg, attr = default_colors
 
         if context.reset:
diff --git a/ranger/config/.pylintrc b/ranger/config/.pylintrc
new file mode 100644
index 00000000..adec41f1
--- /dev/null
+++ b/ranger/config/.pylintrc
@@ -0,0 +1,8 @@
+[BASIC]
+good-names=i,j,k,n,ex,Run,_,fm,ui,fg,bg
+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
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index 92c9f385..a3a44278 100755
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -73,17 +73,21 @@
 # File objects (for example self.fm.thisfile) have these useful attributes and
 # methods:
 #
-# cf.path: The path to the file.
-# cf.basename: The base name only.
-# cf.load_content(): Force a loading of the directories content (which
+# tfile.path: The path to the file.
+# tfile.basename: The base name only.
+# tfile.load_content(): Force a loading of the directories content (which
 #      obviously works with directories only)
-# cf.is_directory: True/False depending on whether it's a directory.
+# tfile.is_directory: True/False depending on whether it's a directory.
 #
 # For advanced commands it is unavoidable to dive a bit into the source code
 # of ranger.
 # ===================================================================
 
-from ranger.api.commands import *
+from collections import deque
+import os
+import re
+
+from ranger.api.commands import Command
 
 
 class alias(Command):
@@ -121,7 +125,6 @@ class cd(Command):
     """
 
     def execute(self):
-        import os.path
         if self.arg(1) == '-r':
             self.shift()
             destination = os.path.realpath(self.rest(1))
@@ -140,7 +143,6 @@ class cd(Command):
             self.fm.cd(destination)
 
     def tab(self, tabnum):
-        import os
         from os.path import dirname, basename, expanduser, join
 
         cwd = self.fm.thisdir.path
@@ -254,7 +256,7 @@ class open_with(Command):
     def tab(self, tabnum):
         return self._tab_through_executables()
 
-    def _get_app_flags_mode(self, string):
+    def _get_app_flags_mode(self, string):  # pylint: disable=too-many-branches,too-many-statements
         """Extracts the application, flags and mode from a string.
 
         examples:
@@ -329,11 +331,13 @@ class open_with(Command):
     def _is_app(self, arg):
         return not self._is_flags(arg) and not arg.isdigit()
 
-    def _is_flags(self, arg):
+    @staticmethod
+    def _is_flags(arg):
         from ranger.core.runner import ALLOWED_FLAGS
         return all(x in ALLOWED_FLAGS for x in arg)
 
-    def _is_mode(self, arg):
+    @staticmethod
+    def _is_mode(arg):
         return all(x in '0123456789' for x in arg)
 
 
@@ -354,7 +358,7 @@ class set_(Command):
         else:
             self.fm.set_option_from_string(name, value)
 
-    def tab(self, tabnum):
+    def tab(self, tabnum):  # pylint: disable=too-many-return-statements
         from ranger.gui.colorscheme import get_all_colorschemes
         name, value, name_done = self.parse_setting_line()
         settings = self.fm.settings
@@ -388,7 +392,6 @@ class setlocal(set_):
     PATH_RE = re.compile(r'^\s*path="?(.*?)"?\s*$')
 
     def execute(self):
-        import os.path
         match = self.PATH_RE.match(self.arg(1))
         if match:
             path = os.path.normpath(os.path.expanduser(match.group(1)))
@@ -420,7 +423,6 @@ class setintag(setlocal):
 class default_linemode(Command):
 
     def execute(self):
-        import re
         from ranger.container.fsobject import FileSystemObject
 
         if len(self.args) < 2:
@@ -441,13 +443,13 @@ class default_linemode(Command):
             self.shift()
 
         # Extract and validate the line mode from the command line
-        linemode = self.rest(1)
-        if linemode not in FileSystemObject.linemode_dict:
+        lmode = self.rest(1)
+        if lmode not in FileSystemObject.linemode_dict:
             self.fm.notify("Invalid linemode: %s; should be %s" %
-                           (linemode, "/".join(FileSystemObject.linemode_dict)), bad=True)
+                           (lmode, "/".join(FileSystemObject.linemode_dict)), bad=True)
 
         # Add the prepared entry to the fm.default_linemodes
-        entry = [method, argument, linemode]
+        entry = [method, argument, lmode]
         self.fm.default_linemodes.appendleft(entry)
 
         # Redraw the columns
@@ -456,13 +458,12 @@ class default_linemode(Command):
                 col.need_redraw = True
 
     def tab(self, tabnum):
-        mode = self.arg(1)
-        return (self.arg(0) + " " + linemode
-                for linemode in self.fm.thisfile.linemode_dict.keys()
-                if linemode.startswith(self.arg(1)))
+        return (self.arg(0) + " " + lmode
+                for lmode in self.fm.thisfile.linemode_dict.keys()
+                if lmode.startswith(self.arg(1)))
 
 
-class quit(Command):
+class quit(Command):  # pylint: disable=redefined-builtin
     """:quit
 
     Closes the current tab.  If there is only one tab, quit the program.
@@ -522,33 +523,29 @@ class delete(Command):
     escape_macros_for_shell = True
 
     def execute(self):
-        import os
         import shlex
         from functools import partial
-        from ranger.container.file import File
 
-        def is_directory_with_files(f):
-            import os.path
-            return (os.path.isdir(f) and not os.path.islink(f)
-                    and len(os.listdir(f)) > 0)
+        def is_directory_with_files(path):
+            return (os.path.isdir(path) and not os.path.islink(path)
+                    and len(os.listdir(path)) > 0)
 
         if self.rest(1):
             files = shlex.split(self.rest(1))
             many_files = (len(files) > 1 or is_directory_with_files(files[0]))
         else:
             cwd = self.fm.thisdir
-            cf = self.fm.thisfile
-            if not cwd or not cf:
+            tfile = self.fm.thisfile
+            if not cwd or not tfile:
                 self.fm.notify("Error: no file selected for deletion!", bad=True)
                 return
 
             # relative_path used for a user-friendly output in the confirmation.
             files = [f.relative_path for f in self.fm.thistab.get_selection()]
-            many_files = (cwd.marked_items or is_directory_with_files(cf.path))
+            many_files = (cwd.marked_items or is_directory_with_files(tfile.path))
 
         confirm = self.fm.settings.confirm_on_delete
         if confirm != 'never' and (confirm != 'multiple' or many_files):
-            filename_list = files
             self.fm.ui.console.ask("Confirm deletion of: %s (y/N)" %
                                    ', '.join(files),
                                    partial(self._question_callback, files), ('n', 'N', 'y', 'Y'))
@@ -617,13 +614,13 @@ class load_copy_buffer(Command):
         from os.path import exists
         try:
             fname = self.fm.confpath(self.copy_buffer_filename)
-            f = open(fname, 'r')
+            fobj = open(fname, 'r')
         except Exception:
             return self.fm.notify("Cannot open %s" %
                                   (fname or self.copy_buffer_filename), bad=True)
         self.fm.copy_buffer = set(File(g)
-                                  for g in f.read().split("\n") if exists(g))
-        f.close()
+                                  for g in fobj.read().split("\n") if exists(g))
+        fobj.close()
         self.fm.ui.redraw_main_column()
 
 
@@ -638,12 +635,12 @@ class save_copy_buffer(Command):
         fname = None
         try:
             fname = self.fm.confpath(self.copy_buffer_filename)
-            f = open(fname, 'w')
+            fobj = open(fname, 'w')
         except Exception:
             return self.fm.notify("Cannot open %s" %
                                   (fname or self.copy_buffer_filename), bad=True)
-        f.write("\n".join(f.path for f in self.fm.copy_buffer))
-        f.close()
+        fobj.write("\n".join(fobj.path for fobj in self.fm.copy_buffer))
+        fobj.close()
 
 
 class unmark_tag(mark_tag):
@@ -732,17 +729,16 @@ class eval_(Command):
         else:
             code = self.rest(1)
             quiet = False
-        import ranger
-        global cmd, fm, p, quantifier
+        global cmd, fm, p, quantifier  # pylint: disable=invalid-name,global-variable-undefined
         fm = self.fm
         cmd = self.fm.execute_console
         p = fm.notify
         quantifier = self.quantifier
         try:
             try:
-                result = eval(code)
+                result = eval(code)  # pylint: disable=eval-used
             except SyntaxError:
-                exec(code)
+                exec(code)  # pylint: disable=exec-used
             else:
                 if result and not quiet:
                     p(result)
@@ -764,10 +760,10 @@ class rename(Command):
 
         tagged = {}
         old_name = self.fm.thisfile.relative_path
-        for f in self.fm.tags.tags:
-            if str(f).startswith(self.fm.thisfile.path):
-                tagged[f] = self.fm.tags.tags[f]
-                self.fm.tags.remove(f)
+        for fobj in self.fm.tags.tags:
+            if str(fobj).startswith(self.fm.thisfile.path):
+                tagged[fobj] = self.fm.tags.tags[fobj]
+                self.fm.tags.remove(fobj)
 
         if not new_name:
             return self.fm.notify('Syntax: rename <newname>', bad=True)
@@ -779,19 +775,20 @@ class rename(Command):
             return self.fm.notify("Can't rename: file already exists!", bad=True)
 
         if self.fm.rename(self.fm.thisfile, new_name):
-            f = File(new_name)
+            fobj = File(new_name)
             # Update bookmarks that were pointing on the previous name
             obsoletebookmarks = [b for b in self.fm.bookmarks
                                  if b[1].path == self.fm.thisfile]
             if obsoletebookmarks:
                 for key, _ in obsoletebookmarks:
-                    self.fm.bookmarks[key] = f
+                    self.fm.bookmarks[key] = fobj
                 self.fm.bookmarks.update_if_outdated()
 
-            self.fm.thisdir.pointed_obj = f
-            self.fm.thisfile = f
-            for t in tagged:
-                self.fm.tags.tags[t.replace(old_name, new_name)] = tagged[t]
+            self.fm.thisdir.pointed_obj = fobj
+            self.fm.thisfile = fobj
+
+            for fobj in tagged:
+                self.fm.tags.tags[fobj.replace(old_name, new_name)] = tagged[fobj]
                 self.fm.tags.dump()
 
     def tab(self, tabnum):
@@ -801,13 +798,14 @@ class rename(Command):
 class rename_append(Command):
     """:rename_append
 
-    Creates an open_console for the rename command, automatically placing the cursor before the file extension.
+    Creates an open_console for the rename command, automatically placing
+    the cursor before the file extension.
     """
 
     def execute(self):
-        cf = self.fm.thisfile
-        path = cf.relative_path.replace("%", "%%")
-        if path.find('.') != 0 and path.rfind('.') != -1 and not cf.is_directory:
+        tfile = self.fm.thisfile
+        path = tfile.relative_path.replace("%", "%%")
+        if path.find('.') != 0 and path.rfind('.') != -1 and not tfile.is_directory:
             self.fm.open_console('rename ' + path, position=(7 + path.rfind('.')))
         else:
             self.fm.open_console('rename ' + path)
@@ -826,12 +824,12 @@ class chmod(Command):
     """
 
     def execute(self):
-        mode = self.rest(1)
-        if not mode:
-            mode = str(self.quantifier)
+        mode_str = self.rest(1)
+        if not mode_str:
+            mode_str = str(self.quantifier)
 
         try:
-            mode = int(mode, 8)
+            mode = int(mode_str, 8)
             if mode < 0 or mode > 0o777:
                 raise ValueError
         except ValueError:
@@ -863,7 +861,7 @@ class bulkrename(Command):
     After you close it, it will be executed.
     """
 
-    def execute(self):
+    def execute(self):  # pylint: disable=too-many-locals,too-many-statements
         import sys
         import tempfile
         from ranger.container.file import File
@@ -939,29 +937,27 @@ class relink(Command):
     """
 
     def execute(self):
-        from ranger.container.file import File
-
         new_path = self.rest(1)
-        cf = self.fm.thisfile
+        tfile = self.fm.thisfile
 
         if not new_path:
             return self.fm.notify('Syntax: relink <newpath>', bad=True)
 
-        if not cf.is_link:
-            return self.fm.notify('%s is not a symlink!' % cf.relative_path, bad=True)
+        if not tfile.is_link:
+            return self.fm.notify('%s is not a symlink!' % tfile.relative_path, bad=True)
 
-        if new_path == os.readlink(cf.path):
+        if new_path == os.readlink(tfile.path):
             return
 
         try:
-            os.remove(cf.path)
-            os.symlink(new_path, cf.path)
+            os.remove(tfile.path)
+            os.symlink(new_path, tfile.path)
         except OSError as err:
             self.fm.notify(err)
 
         self.fm.reset()
-        self.fm.thisdir.pointed_obj = cf
-        self.fm.thisfile = cf
+        self.fm.thisdir.pointed_obj = tfile
+        self.fm.thisfile = tfile
 
     def tab(self, tabnum):
         if not self.rest(1):
@@ -990,10 +986,11 @@ class help_(Command):
             elif answer == "s":
                 self.fm.dump_settings()
 
-        c = self.fm.ui.console.ask("View [m]an page, [k]ey bindings,"
-                                   " [c]ommands or [s]ettings? (press q to abort)",
-                                   callback,
-                                   list("mkcsq") + [chr(27)])
+        self.fm.ui.console.ask(
+            "View [m]an page, [k]ey bindings, [c]ommands or [s]ettings? (press q to abort)",
+            callback,
+            list("mkcsq") + [chr(27)]
+        )
 
 
 class copymap(Command):
@@ -1164,7 +1161,7 @@ class scout(Command):
         self._regex = None
         self.flags, self.pattern = self.parse_flags()
 
-    def execute(self):
+    def execute(self):  # pylint: disable=too-many-branches
         thisdir = self.fm.thisdir
         flags = self.flags
         pattern = self.pattern
@@ -1177,12 +1174,12 @@ class scout(Command):
         if (self.MARK in flags or self.UNMARK in flags) and thisdir.files:
             value = flags.find(self.MARK) > flags.find(self.UNMARK)
             if self.FILTER in flags:
-                for f in thisdir.files:
-                    thisdir.mark_item(f, value)
+                for fobj in thisdir.files:
+                    thisdir.mark_item(fobj, value)
             else:
-                for f in thisdir.files:
-                    if regex.search(f.relative_path):
-                        thisdir.mark_item(f, value)
+                for fobj in thisdir.files:
+                    if regex.search(fobj.relative_path):
+                        thisdir.mark_item(fobj, value)
 
         if self.PERM_FILTER in flags:
             thisdir.filter = regex if pattern else None
@@ -1356,8 +1353,8 @@ class flat(Command):
 
     def execute(self):
         try:
-            level = self.rest(1)
-            level = int(level)
+            level_str = self.rest(1)
+            level = int(level_str)
         except ValueError:
             level = self.quantifier
         if level < -1:
@@ -1456,12 +1453,11 @@ class meta(prompt_metadata):
 
     def execute(self):
         key = self.arg(1)
-        value = self.rest(1)
         update_dict = dict()
         update_dict[key] = self.rest(2)
         selection = self.fm.thistab.get_selection()
-        for f in selection:
-            self.fm.metadata.set_metadata(f.path, update_dict)
+        for fobj in selection:
+            self.fm.metadata.set_metadata(fobj.path, update_dict)
         self._process_command_stack()
 
     def tab(self, tabnum):
@@ -1488,13 +1484,14 @@ class linemode(default_linemode):
         mode = self.arg(1)
 
         if mode == "normal":
+            from ranger.core.linemode import DEFAULT_LINEMODE
             mode = DEFAULT_LINEMODE
 
         if mode not in self.fm.thisfile.linemode_dict:
             self.fm.notify("Unhandled linemode: `%s'" % mode, bad=True)
             return
 
-        self.fm.thisdir._set_linemode_of_children(mode)
+        self.fm.thisdir._set_linemode_of_children(mode)  # pylint: disable=protected-access
 
         # Ask the browsercolumns to redraw
         for col in self.fm.ui.browser.columns:
diff --git a/ranger/config/commands_sample.py b/ranger/config/commands_sample.py
index 1386e84e..4333823c 100644
--- a/ranger/config/commands_sample.py
+++ b/ranger/config/commands_sample.py
@@ -4,19 +4,18 @@
 # documentation.  Do NOT add them all here, or you may end up with defunct
 # commands when upgrading ranger.
 
-# You always need to import ranger.api.commands here to get the Command class:
-from ranger.api.commands import *
-
 # A simple command for demonstration purposes follows.
 # -----------------------------------------------------------------------------
 
 # You can import any python module as needed.
 import os
 
-# Any class that is a subclass of "Command" will be integrated into ranger as a
-# command.  Try typing ":my_edit<ENTER>" in ranger!
+# You always need to import ranger.api.commands here to get the Command class:
+from ranger.api.commands import Command
 
 
+# Any class that is a subclass of "Command" will be integrated into ranger as a
+# command.  Try typing ":my_edit<ENTER>" in ranger!
 class my_edit(Command):
     # The so-called doc-string of the class will be visible in the built-in
     # help that is accessible by typing "?c" inside ranger.
diff --git a/ranger/container/bookmarks.py b/ranger/container/bookmarks.py
index 98de84ec..6b188de0 100644
--- a/ranger/container/bookmarks.py
+++ b/ranger/container/bookmarks.py
@@ -151,16 +151,16 @@ class Bookmarks(object):
         if self.path is None:
             return
         if os.access(self.path, os.W_OK):
-            f = open(self.path + ".new", 'w')
+            fobj = open(self.path + ".new", 'w')
             for key, value in self.dct.items():
                 if isinstance(key, str)\
                         and key in ALLOWED_KEYS:
                     try:
-                        f.write("{0}:{1}\n".format(str(key), str(value)))
+                        fobj.write("{0}:{1}\n".format(str(key), str(value)))
                     except Exception:
                         pass
 
-            f.close()
+            fobj.close()
             old_perms = os.stat(self.path)
             try:
                 os.chown(self.path + ".new", old_perms.st_uid, old_perms.st_gid)
@@ -178,19 +178,19 @@ class Bookmarks(object):
 
         if not os.path.exists(self.path):
             try:
-                f = open(self.path, 'w')
+                fobj = open(self.path, 'w')
             except Exception:
                 raise OSError('Cannot read the given path')
-            f.close()
+            fobj.close()
 
         if os.access(self.path, os.R_OK):
-            f = open(self.path, 'r')
-            for line in f:
+            fobj = open(self.path, 'r')
+            for line in fobj:
                 if self.load_pattern.match(line):
                     key, value = line[0], line[2:-1]
                     if key in ALLOWED_KEYS:
                         dct[key] = self.bookmarktype(value)
-            f.close()
+            fobj.close()
             return dct
         else:
             raise OSError('Cannot read the given path')
diff --git a/ranger/container/directory.py b/ranger/container/directory.py
index d48bd7ca..72a9cd33 100644
--- a/ranger/container/directory.py
+++ b/ranger/container/directory.py
@@ -3,10 +3,9 @@
 
 import locale
 import os.path
+from os import stat as os_stat, lstat as os_lstat
 import random
 import re
-
-from os import stat as os_stat, lstat as os_lstat
 from collections import deque
 from time import time
 
@@ -65,8 +64,8 @@ def accept_file(file, filters):
                   returns True if file shall be shown,
                   otherwise False.
     """
-    for filter in filters:
-        if filter and not filter(file):
+    for filt in filters:
+        if filt and not filt(file):
             return False
     return True
 
@@ -85,14 +84,15 @@ def walklevel(some_dir, level):
 
 def mtimelevel(path, level):
     mtime = os.stat(path).st_mtime
-    for dirpath, dirnames, filenames in walklevel(path, level):
+    for dirpath, dirnames, _ in walklevel(path, level):
         dirlist = [os.path.join("/", dirpath, d) for d in dirnames
                    if level == -1 or dirpath.count(os.path.sep) - path.count(os.path.sep) <= level]
         mtime = max(mtime, max([-1] + [os.stat(d).st_mtime for d in dirlist]))
     return mtime
 
 
-class Directory(FileSystemObject, Accumulator, Loadable):
+class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public-methods
+        FileSystemObject, Accumulator, Loadable):
     is_directory = True
     enterable = False
     load_generator = None
@@ -172,7 +172,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
         return self.files
 
     def mark_item(self, item, val):
-        item._mark(val)
+        item._mark(val)  # pylint: disable=protected-access
         if val:
             if item in self.files and item not in self.marked_items:
                 self.marked_items.append(item)
@@ -207,7 +207,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
 
     def _clear_marked_items(self):
         for item in self.marked_items:
-            item._mark(False)
+            item._mark(False)  # pylint: disable=protected-access
         del self.marked_items[:]
 
     def get_selection(self):
@@ -256,6 +256,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
         self.move_to_obj(self.pointed_obj)
 
     # XXX: Check for possible race conditions
+    # pylint: disable=too-many-locals,too-many-branches,too-many-statements
     def load_bit_by_bit(self):
         """An iterator that loads a part on every next() call
 
@@ -269,7 +270,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
 
         basename_is_rel_to = self.path if self.flat else None
 
-        try:
+        try:  # pylint: disable=too-many-nested-blocks
             if self.runnable:
                 yield
                 mypath = self.path
@@ -351,16 +352,22 @@ class Directory(FileSystemObject, Accumulator, Loadable):
                             if item.vcs.is_root_pointer:
                                 has_vcschild = True
                             else:
-                                item.vcsstatus = item.vcs.rootvcs.status_subpath(
-                                    os.path.join(self.realpath, item.basename), is_directory=True)
+                                item.vcsstatus = \
+                                    item.vcs.rootvcs.status_subpath(  # pylint: disable=no-member
+                                        os.path.join(self.realpath, item.basename),
+                                        is_directory=True,
+                                    )
                     else:
-                        item = File(name, preload=stats, path_is_abs=True,
-                                    basename_is_rel_to=basename_is_rel_to)
+                        item = File(  # pylint: disable=redefined-variable-type
+                            name, preload=stats, path_is_abs=True,
+                            basename_is_rel_to=basename_is_rel_to
+                        )
                         item.load()
                         disk_usage += item.size
                         if self.vcs and self.vcs.track:
-                            item.vcsstatus = self.vcs.rootvcs.status_subpath(
-                                os.path.join(self.realpath, item.basename))
+                            item.vcsstatus = \
+                                self.vcs.rootvcs.status_subpath(  # pylint: disable=no-member
+                                    os.path.join(self.realpath, item.basename))
 
                     files.append(item)
                     self.percent = 100 * len(files) // len(filenames)
@@ -374,10 +381,10 @@ class Directory(FileSystemObject, Accumulator, Loadable):
                 self._clear_marked_items()
                 for item in self.files_all:
                     if item.path in marked_paths:
-                        item._mark(True)
+                        item._mark(True)  # pylint: disable=protected-access
                         self.marked_items.append(item)
                     else:
-                        item._mark(False)
+                        item._mark(False)  # pylint: disable=protected-access
 
                 self.sort()
 
@@ -401,6 +408,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
             self.fm.signal_emit("finished_loading_dir", directory=self)
             if self.vcs:
                 self.fm.ui.vcsthread.process(self)
+    # pylint: enable=too-many-locals,too-many-branches,too-many-statements
 
     def unload(self):
         self.loading = False
@@ -479,8 +487,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
             return 0
         cum = 0
         realpath = os.path.realpath
-        for dirpath, dirnames, filenames in os.walk(self.path,
-                                                    onerror=lambda _: None):
+        for dirpath, _, filenames in os.walk(self.path, onerror=lambda _: None):
             for file in filenames:
                 try:
                     if dirpath == self.path:
@@ -499,7 +506,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
             human_readable(self.size)
 
     @lazy_property
-    def size(self):
+    def size(self):  # pylint: disable=method-hidden
         try:
             if self.fm.settings.automatically_count_files:
                 size = len(os.listdir(self.path))
@@ -520,15 +527,15 @@ class Directory(FileSystemObject, Accumulator, Loadable):
             return size
 
     @lazy_property
-    def infostring(self):
-        self.size  # trigger the lazy property initializer
+    def infostring(self):  # pylint: disable=method-hidden
+        self.size  # trigger the lazy property initializer pylint: disable=pointless-statement
         if self.is_link:
             return '->' + self.infostring
         return self.infostring
 
     @lazy_property
-    def runnable(self):
-        self.size  # trigger the lazy property initializer
+    def runnable(self):  # pylint: disable=method-hidden
+        self.size  # trigger the lazy property initializer pylint: disable=pointless-statement
         return self.runnable
 
     def sort_if_outdated(self):
@@ -539,7 +546,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
             return True
         return False
 
-    def move_to_obj(self, arg):
+    def move_to_obj(self, arg, attr=None):
         try:
             arg = arg.path
         except Exception:
@@ -642,7 +649,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
             return True
         return self.last_used + seconds < time()
 
-    def go(self, history=True):
+    def go(self, history=True):  # pylint: disable=invalid-name
         """enter the directory if the filemanager is running"""
         if self.fm:
             return self.fm.enter_dir(self.path, history=history)
@@ -653,8 +660,8 @@ class Directory(FileSystemObject, Accumulator, Loadable):
         return self.files is None or len(self.files) == 0
 
     def _set_linemode_of_children(self, mode):
-        for f in self.files:
-            f._set_linemode(mode)
+        for file in self.files:
+            file._set_linemode(mode)  # pylint: disable=protected-access
 
     def __nonzero__(self):
         """Always True"""
diff --git a/ranger/container/file.py b/ranger/container/file.py
index 7bb15c84..f6ab880c 100644
--- a/ranger/container/file.py
+++ b/ranger/container/file.py
@@ -5,7 +5,7 @@ import re
 from ranger.container.fsobject import FileSystemObject
 
 N_FIRST_BYTES = 256
-control_characters = set(chr(n) for n in
+control_characters = set(chr(n) for n in  # pylint: disable=invalid-name
                          set(range(0, 9)) | set(range(14, 32)))
 
 # Don't even try to preview files which match this regular expression:
@@ -45,26 +45,27 @@ class File(FileSystemObject):
     preview_loading = False
 
     _linemode = "filename"
+    _firstbytes = None
 
     @property
     def firstbytes(self):
-        try:
-            return self._firstbytes
-        except Exception:
+        if self._firstbytes is None:
             try:
-                f = open(self.path, 'r')
-                self._firstbytes = f.read(N_FIRST_BYTES)
-                f.close()
+                fobj = open(self.path, 'r')
+                self._firstbytes = fobj.read(N_FIRST_BYTES)
+                fobj.close()
                 return self._firstbytes
             except Exception:
                 pass
+        else:
+            return self._firstbytes
 
     def is_binary(self):
         if self.firstbytes and control_characters & set(self.firstbytes):
             return True
         return False
 
-    def has_preview(self):
+    def has_preview(self):  # pylint: disable=too-many-return-statements
         if not self.fm.settings.preview_files:
             return False
         if self.is_socket or self.is_fifo or self.is_device:
diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py
index e4571ffb..619f5df5 100644
--- a/ranger/container/fsobject.py
+++ b/ranger/container/fsobject.py
@@ -1,23 +1,15 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
-CONTAINER_EXTENSIONS = ('7z', 'ace', 'ar', 'arc', 'bz', 'bz2', 'cab', 'cpio',
-                        'cpt', 'deb', 'dgc', 'dmg', 'gz', 'iso', 'jar', 'msi', 'pkg', 'rar',
-                        'shar', 'tar', 'tbz', 'tgz', 'xar', 'xpi', 'xz', 'zip')
-DOCUMENT_EXTENSIONS = ('cfg', 'css', 'cvs', 'djvu', 'doc', 'docx', 'gnm',
-                       'gnumeric', 'htm', 'html', 'md', 'odf', 'odg', 'odp', 'ods', 'odt', 'pdf',
-                       'pod', 'ps', 'rtf', 'sxc', 'txt', 'xls', 'xlw', 'xml', 'xslx')
-DOCUMENT_BASENAMES = ('bugs', 'bugs', 'changelog', 'copying', 'credits',
-                      'hacking', 'help', 'install', 'license', 'readme', 'todo')
-
-BAD_INFO = '?'
-
 import re
 from grp import getgrgid
-from os import lstat, stat, getcwd
+from os import lstat, stat
 from os.path import abspath, basename, dirname, realpath, splitext, extsep, relpath
 from pwd import getpwuid
-from ranger.core.linemode import *
+from ranger.core.linemode import (
+    DEFAULT_LINEMODE, DefaultLinemode, TitleLinemode,
+    PermissionsLinemode, FileInfoLinemode, MtimeLinemode, SizeMtimeLinemode,
+)
 from ranger.core.shared import FileManagerAware, SettingsAware
 from ranger.ext.shell_escape import shell_escape
 from ranger.ext import spawn
@@ -25,20 +17,37 @@ from ranger.ext.lazy_property import lazy_property
 from ranger.ext.human_readable import human_readable
 
 if hasattr(str, 'maketrans'):
-    maketrans = str.maketrans
+    maketrans = str.maketrans  # pylint: disable=invalid-name
 else:
-    from string import maketrans
+    from string import maketrans  # pylint: disable=no-name-in-module
+
+
+CONTAINER_EXTENSIONS = ('7z', 'ace', 'ar', 'arc', 'bz', 'bz2', 'cab', 'cpio',
+                        'cpt', 'deb', 'dgc', 'dmg', 'gz', 'iso', 'jar', 'msi', 'pkg', 'rar',
+                        'shar', 'tar', 'tbz', 'tgz', 'xar', 'xpi', 'xz', 'zip')
+DOCUMENT_EXTENSIONS = ('cfg', 'css', 'cvs', 'djvu', 'doc', 'docx', 'gnm',
+                       'gnumeric', 'htm', 'html', 'md', 'odf', 'odg', 'odp', 'ods', 'odt', 'pdf',
+                       'pod', 'ps', 'rtf', 'sxc', 'txt', 'xls', 'xlw', 'xml', 'xslx')
+DOCUMENT_BASENAMES = ('bugs', 'bugs', 'changelog', 'copying', 'credits',
+                      'hacking', 'help', 'install', 'license', 'readme', 'todo')
+
+BAD_INFO = '?'
+
+
+# pylint: disable=invalid-name
 _unsafe_chars = '\n' + ''.join(map(chr, range(32))) + ''.join(map(chr, range(128, 256)))
 _safe_string_table = maketrans(_unsafe_chars, '?' * len(_unsafe_chars))
 _extract_number_re = re.compile(r'(\d+|\D)')
 _integers = set("0123456789")
+# pylint: enable=invalid-name
 
 
 def safe_path(path):
     return path.translate(_safe_string_table)
 
 
-class FileSystemObject(FileManagerAware, SettingsAware):
+class FileSystemObject(  # pylint: disable=too-many-instance-attributes
+        FileManagerAware, SettingsAware):
     (basename,
      relative_path,
      relative_path_lower,
@@ -164,8 +173,8 @@ class FileSystemObject(FileManagerAware, SettingsAware):
             return str(self.stat.st_gid)
 
     for attr in ('video', 'audio', 'image', 'media', 'document', 'container'):
-        exec("%s = lazy_property("
-             "lambda self: self.set_mimetype() or self.%s)" % (attr, attr))
+        exec(  # pylint: disable=exec-used
+            "%s = lazy_property(lambda self: self.set_mimetype() or self.%s)" % (attr, attr))
 
     def __str__(self):
         """returns a string containing the absolute path"""
@@ -179,10 +188,10 @@ class FileSystemObject(FileManagerAware, SettingsAware):
 
     def set_mimetype(self):
         """assign attributes such as self.video according to the mimetype"""
-        basename = self.basename
+        bname = self.basename
         if self.extension == 'part':
-            basename = basename[0:-5]
-        self._mimetype = self.fm.mimetypes.guess_type(basename, False)[0]
+            bname = bname[0:-5]
+        self._mimetype = self.fm.mimetypes.guess_type(bname, False)[0]
         if self._mimetype is None:
             self._mimetype = ''
 
@@ -217,7 +226,7 @@ class FileSystemObject(FileManagerAware, SettingsAware):
             self.set_mimetype()
             return self._mimetype_tuple
 
-    def mark(self, boolean):
+    def mark(self, _):
         directory = self.fm.get_directory(self.dirname)
         directory.mark_item(self)
 
@@ -249,7 +258,7 @@ class FileSystemObject(FileManagerAware, SettingsAware):
         self.permissions = None
         new_stat = None
         path = self.path
-        is_link = False
+        self.is_link = False
         if self.preload:
             new_stat = self.preload[1]
             self.is_link = new_stat.st_mode & 0o170000 == 0o120000
@@ -272,16 +281,16 @@ class FileSystemObject(FileManagerAware, SettingsAware):
         self.accessible = True if new_stat else False
         mode = new_stat.st_mode if new_stat else 0
 
-        format = mode & 0o170000
-        if format == 0o020000 or format == 0o060000:  # stat.S_IFCHR/BLK
+        fmt = mode & 0o170000
+        if fmt == 0o020000 or fmt == 0o060000:  # stat.S_IFCHR/BLK
             self.is_device = True
             self.size = 0
             self.infostring = 'dev'
-        elif format == 0o010000:  # stat.S_IFIFO
+        elif fmt == 0o010000:  # stat.S_IFIFO
             self.is_fifo = True
             self.size = 0
             self.infostring = 'fifo'
-        elif format == 0o140000:  # stat.S_IFSOCK
+        elif fmt == 0o140000:  # stat.S_IFSOCK
             self.is_socket = True
             self.size = 0
             self.infostring = 'sock'
diff --git a/ranger/container/history.py b/ranger/container/history.py
index 082a951a..c72d5c3b 100644
--- a/ranger/container/history.py
+++ b/ranger/container/history.py
@@ -13,8 +13,10 @@ class History(object):
     def __init__(self, maxlen=None, unique=True):
         assert maxlen is not None, "maxlen cannot be None"
         if isinstance(maxlen, History):
+            # pylint: disable=protected-access
             self._history = list(maxlen._history)
             self._index = maxlen._index
+            # pylint: enable=protected-access
             self.maxlen = maxlen.maxlen
             self.unique = maxlen.unique
         else:
@@ -75,9 +77,9 @@ class History(object):
             future_length = len(self._history) - self._index - 1
 
         self._history[:self._index] = list(
-            other_history._history[:other_history._index + 1])
+            other_history._history[:other_history._index + 1])  # pylint: disable=protected-access
         if len(self._history) > self.maxlen:
-            self._history = self._history[-self.maxlen:]
+            self._history = self._history[-self.maxlen:]  # pylint: disable=protected-access
 
         self._index = len(self._history) - future_length - 1
         assert self._index < len(self._history)
diff --git a/ranger/container/settings.py b/ranger/container/settings.py
index 9b6a3917..5424bffb 100644
--- a/ranger/container/settings.py
+++ b/ranger/container/settings.py
@@ -1,12 +1,13 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
+import re
+import os.path
 from inspect import isfunction
-from ranger.ext.signals import SignalDispatcher, Signal
+
+from ranger.ext.signals import SignalDispatcher
 from ranger.core.shared import FileManagerAware
 from ranger.gui.colorscheme import _colorscheme_name_to_class
-import re
-import os.path
 
 # Use these priority constants to trigger events at specific points in time
 # during processing of the signals "setopt" and "setopt.<some_setting_name>"
@@ -199,10 +200,11 @@ class Settings(SignalDispatcher, FileManagerAware):
             return self.get(name, None)
 
     def __iter__(self):
-        for x in self._settings:
-            yield x
+        for setting in self._settings:
+            yield setting
 
-    def types_of(self, name):
+    @staticmethod
+    def types_of(name):
         try:
             typ = ALLOWED_SETTINGS[name]
         except KeyError:
@@ -259,7 +261,7 @@ class Settings(SignalDispatcher, FileManagerAware):
         self._raw_set(signal.setting, signal.value, signal.path, signal.tags)
 
 
-class LocalSettings():
+class LocalSettings():  # pylint: disable=too-few-public-methods
 
     def __init__(self, path, parent):
         self.__dict__['_parent'] = parent
@@ -278,8 +280,8 @@ class LocalSettings():
             return self._parent.get(name, self._path)
 
     def __iter__(self):
-        for x in self._parent._settings:
-            yield x
+        for setting in self._parent._settings:  # pylint: disable=protected-access
+            yield setting
 
     __getitem__ = __getattr__
     __setitem__ = __setattr__
diff --git a/ranger/container/tags.py b/ranger/container/tags.py
index cf2f359d..128f984e 100644
--- a/ranger/container/tags.py
+++ b/ranger/container/tags.py
@@ -39,7 +39,7 @@ class Tags(object):
         self.sync()
         for item in items:
             try:
-                del(self.tags[item])
+                del self.tags[item]
             except KeyError:
                 pass
         self.dump()
@@ -56,7 +56,7 @@ class Tags(object):
         for item in items:
             try:
                 if item in self and tag in (self.tags[item], self.default_tag):
-                    del(self.tags[item])
+                    del self.tags[item]
                 else:
                     self.tags[item] = tag
             except KeyError:
@@ -72,35 +72,35 @@ class Tags(object):
     def sync(self):
         try:
             if sys.version_info[0] >= 3:
-                f = open(self._filename, 'r', errors='replace')
+                fobj = open(self._filename, 'r', errors='replace')
             else:
-                f = open(self._filename, 'r')
+                fobj = open(self._filename, 'r')
         except OSError:
             pass
         else:
-            self.tags = self._parse(f)
-            f.close()
+            self.tags = self._parse(fobj)
+            fobj.close()
 
     def dump(self):
         try:
-            f = open(self._filename, 'w')
+            fobj = open(self._filename, 'w')
         except OSError:
             pass
         else:
-            self._compile(f)
-            f.close()
+            self._compile(fobj)
+            fobj.close()
 
-    def _compile(self, f):
+    def _compile(self, fobj):
         for path, tag in self.tags.items():
             if tag == self.default_tag:
                 # COMPAT: keep the old format if the default tag is used
-                f.write(path + '\n')
+                fobj.write(path + '\n')
             elif tag in ALLOWED_KEYS:
-                f.write('{0}:{1}\n'.format(tag, path))
+                fobj.write('{0}:{1}\n'.format(tag, path))
 
-    def _parse(self, f):
+    def _parse(self, fobj):
         result = dict()
-        for line in f:
+        for line in fobj:
             line = line.strip()
             if len(line) > 2 and line[1] == ':':
                 tag, path = line[0], line[2:]
@@ -122,7 +122,7 @@ class TagsDummy(Tags):
     It acts like there are no tags and avoids writing any changes.
     """
 
-    def __init__(self, filename):
+    def __init__(self, filename):  # pylint: disable=super-init-not-called
         self.tags = dict()
 
     def __contains__(self, item):
@@ -146,8 +146,8 @@ class TagsDummy(Tags):
     def dump(self):
         pass
 
-    def _compile(self, f):
+    def _compile(self, fobj):
         pass
 
-    def _parse(self, f):
+    def _parse(self, fobj):
         pass
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 05529000..4f3338d2 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -1,14 +1,15 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
+# pylint: disable=too-many-lines
 
 import codecs
 import os
+from os import link, symlink, getcwd, listdir, stat
+from os.path import join, isdir, realpath, exists
 import re
 import shutil
 import string
 import tempfile
-from os.path import join, isdir, realpath, exists
-from os import link, symlink, getcwd, listdir, stat
 from inspect import cleandoc
 from stat import S_IEXEC
 from hashlib import sha1
@@ -28,11 +29,10 @@ from ranger.container.directory import Directory
 from ranger.container.file import File
 from ranger.core.loader import CommandLoader, CopyLoader
 from ranger.container.settings import ALLOWED_SETTINGS, ALLOWED_VALUES
-from ranger.core.linemode import DEFAULT_LINEMODE
 
 MACRO_FAIL = "<\x01\x01MACRO_HAS_NO_VALUE\x01\01>"
 
-log = getLogger(__name__)
+LOG = getLogger(__name__)
 
 
 class _MacroTemplate(string.Template):
@@ -41,12 +41,15 @@ class _MacroTemplate(string.Template):
     idpattern = r"[_a-z0-9]*"
 
 
-class Actions(FileManagerAware, SettingsAware):
+class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-methods
+        FileManagerAware, SettingsAware):
+
     # --------------------------
     # -- Basic Commands
     # --------------------------
 
-    def exit(self):
+    @staticmethod
+    def exit():
         """:exit
 
         Exit the program.
@@ -105,7 +108,7 @@ class Actions(FileManagerAware, SettingsAware):
                 return False
             elif value.lower() in ('true', 'on', '1'):
                 return True
-        if type(None) in types and value.lower() == 'none':
+        if isinstance(None, types) and value.lower() == 'none':
             return None
         if int in types:
             try:
@@ -151,12 +154,12 @@ class Actions(FileManagerAware, SettingsAware):
         """
         if isinstance(text, Exception):
             if ranger.arg.debug:
-                raise
+                raise text
             bad = True
         elif bad is True and ranger.arg.debug:
             raise Exception(str(text))
         text = str(text)
-        log.debug("Command notify invoked: [Bad: {0}, Text: '{1}']".format(bad, text))
+        LOG.debug("Command notify invoked: [Bad: %s, Text: '%s']", bad, text)
         if self.ui and self.ui.is_on:
             self.ui.status.notify("  ".join(text.split("\n")),
                                   duration=duration, bad=bad)
@@ -177,8 +180,8 @@ class Actions(FileManagerAware, SettingsAware):
             self.loader.remove(index=0)
 
     def get_cumulative_size(self):
-        for f in self.thistab.get_selection() or ():
-            f.look_up_cumulative_size()
+        for fobj in self.thistab.get_selection() or ():
+            fobj.look_up_cumulative_size()
         self.ui.status.request_redraw()
         self.ui.redraw_main_column()
 
@@ -189,7 +192,8 @@ class Actions(FileManagerAware, SettingsAware):
         """
         self.ui.redraw_window()
 
-    def open_console(self, string='', prompt=None, position=None):
+    def open_console(self, string='',  # pylint: disable=redefined-outer-name
+                     prompt=None, position=None):
         """:open_console [string]
 
         Open the console.
@@ -197,7 +201,8 @@ class Actions(FileManagerAware, SettingsAware):
         self.change_mode('normal')
         self.ui.open_console(string, prompt=prompt, position=position)
 
-    def execute_console(self, string='', wildcards=[], quantifier=None):
+    def execute_console(self, string='',  # pylint: disable=redefined-outer-name
+                        wildcards=None, quantifier=None):
         """:execute_console [string]
 
         Execute a command for the console
@@ -210,28 +215,30 @@ class Actions(FileManagerAware, SettingsAware):
         cmd = cmd_class(string)
         if cmd.resolve_macros and _MacroTemplate.delimiter in string:
             macros = dict(('any%d' % i, key_to_string(char))
-                          for i, char in enumerate(wildcards))
+                          for i, char in enumerate(wildcards if wildcards is not None else []))
             if 'any0' in macros:
                 macros['any'] = macros['any0']
             try:
                 string = self.substitute_macros(string, additional=macros,
                                                 escape=cmd.escape_macros_for_shell)
-            except ValueError as e:
+            except ValueError as ex:
                 if ranger.arg.debug:
                     raise
                 else:
-                    return self.notify(e)
+                    return self.notify(ex)
         try:
             cmd_class(string, quantifier=quantifier).execute()
-        except Exception as e:
+        except Exception as ex:
             if ranger.arg.debug:
                 raise
             else:
-                self.notify(e)
+                self.notify(ex)
 
-    def substitute_macros(self, string, additional=dict(), escape=False):
+    def substitute_macros(self, string,  # pylint: disable=redefined-outer-name
+                          additional=None, escape=False):
         macros = self._get_macros()
-        macros.update(additional)
+        if additional:
+            macros.update(additional)
         if escape:
             for key, value in macros.items():
                 if isinstance(value, list):
@@ -247,7 +254,7 @@ class Actions(FileManagerAware, SettingsAware):
             raise ValueError("Could not apply macros to `%s'" % string)
         return result
 
-    def _get_macros(self):
+    def _get_macros(self):  # pylint: disable=too-many-branches,too-many-statements
         macros = {}
 
         macros['rangerdir'] = ranger.RANGERDIR
@@ -274,7 +281,7 @@ class Actions(FileManagerAware, SettingsAware):
 
         if self.fm.thisdir.files:
             macros['t'] = [fl.relative_path for fl in self.fm.thisdir.files
-                           if fl.realpath in (self.fm.tags or [])]
+                           if fl.realpath in self.fm.tags or []]
         else:
             macros['t'] = MACRO_FAIL
 
@@ -348,20 +355,20 @@ class Actions(FileManagerAware, SettingsAware):
         Load a config file.
         """
         filename = os.path.expanduser(filename)
-        log.debug("Sourcing config file '{0}'".format(filename))
-        with open(filename, 'r') as f:
-            for line in f:
+        LOG.debug("Sourcing config file '%s'", filename)
+        with open(filename, 'r') as fobj:
+            for line in fobj:
                 line = line.strip(" \r\n")
                 if line.startswith("#") or not line.strip():
                     continue
                 try:
                     self.execute_console(line)
-                except Exception as e:
+                except Exception as ex:
                     if ranger.arg.debug:
                         raise
                     else:
                         self.notify('Error in line `%s\':\n  %s' %
-                                    (line, str(e)), bad=True)
+                                    (line, str(ex)), bad=True)
 
     def execute_file(self, files, **kw):
         """Uses the "rifle" module to open/execute a file
@@ -385,14 +392,14 @@ class Actions(FileManagerAware, SettingsAware):
 
             if ranger.arg.choosefiles:
                 open(ranger.arg.choosefiles, 'w').write("".join(
-                    f.path + "\n" for f in self.fm.thistab.get_selection()))
+                    fobj.path + "\n" for fobj in self.fm.thistab.get_selection()))
 
             if ranger.arg.choosefile or ranger.arg.choosefiles:
                 raise SystemExit()
 
         if isinstance(files, set):
             files = list(files)
-        elif type(files) not in (list, tuple):
+        elif not isinstance(files, (list, tuple)):
             files = [files]
 
         flags = kw.get('flags', '')
@@ -400,7 +407,7 @@ class Actions(FileManagerAware, SettingsAware):
             files = [self.fm.thisfile]
 
         self.signal_emit('execute.before', keywords=kw)
-        filenames = [f.path for f in files]
+        filenames = [fobj.path for fobj in files]
         label = kw.get('label', kw.get('app', None))
         try:
             return self.rifle.execute(filenames, mode, label, flags, None)
@@ -411,7 +418,7 @@ class Actions(FileManagerAware, SettingsAware):
     # -- Moving Around
     # --------------------------
 
-    def move(self, narg=None, **kw):
+    def move(self, narg=None, **kw):  # pylint: disable=too-many-locals,too-many-branches
         """A universal movement method.
 
         Accepts these parameters:
@@ -443,9 +450,9 @@ class Actions(FileManagerAware, SettingsAware):
                 mode = 0
                 if narg is not None:
                     mode = narg
-                cf = self.thisfile
+                tfile = self.thisfile
                 selection = self.thistab.get_selection()
-                if not self.thistab.enter_dir(cf) and selection:
+                if not self.thistab.enter_dir(tfile) and selection:
                     result = self.execute_file(selection, mode=mode)
                     if result in (False, ASK_COMMAND):
                         self.open_console('open_with ')
@@ -473,15 +480,15 @@ class Actions(FileManagerAware, SettingsAware):
 
                     # Set theory anyone?
                     if not self._visual_reverse:
-                        for f in targets - current:
-                            cwd.mark_item(f, True)
-                        for f in current - old - targets:
-                            cwd.mark_item(f, False)
+                        for fobj in targets - current:
+                            cwd.mark_item(fobj, True)
+                        for fobj in current - old - targets:
+                            cwd.mark_item(fobj, False)
                     else:
-                        for f in targets & current:
-                            cwd.mark_item(f, False)
-                        for f in old - current - targets:
-                            cwd.mark_item(f, True)
+                        for fobj in targets & current:
+                            cwd.mark_item(fobj, False)
+                        for fobj in old - current - targets:
+                            cwd.mark_item(fobj, True)
                 if self.ui.pager.visible:
                     self.display_file()
 
@@ -521,8 +528,8 @@ class Actions(FileManagerAware, SettingsAware):
         cdpath = os.environ.get('CDPATH', None) or os.environ.get('cdpath', None)
         result = self.thistab.enter_dir(path, history=history)
         if result is False and cdpath:
-            for p in cdpath.split(':'):
-                curpath = os.path.join(p, path)
+            for comp in cdpath.split(':'):
+                curpath = os.path.join(comp, path)
                 if os.path.isdir(curpath):
                     result = self.thistab.enter_dir(curpath, history=history)
                     break
@@ -532,16 +539,16 @@ class Actions(FileManagerAware, SettingsAware):
             self.change_mode('normal')
         return result
 
-    def cd(self, path, remember=True):
+    def cd(self, path, remember=True):  # pylint: disable=invalid-name
         """enter the directory at the given path, remember=True"""
         self.enter_dir(path, remember=remember)
 
     def traverse(self):
         self.change_mode('normal')
-        cf = self.thisfile
+        tfile = self.thisfile
         cwd = self.thisdir
-        if cf is not None and cf.is_directory:
-            self.enter_dir(cf.path)
+        if tfile is not None and tfile.is_directory:
+            self.enter_dir(tfile.path)
         elif cwd.pointer >= len(cwd) - 1:
             while True:
                 self.move(left=1)
@@ -594,7 +601,7 @@ class Actions(FileManagerAware, SettingsAware):
             return
         self.execute_file(file, label='editor')
 
-    def toggle_option(self, string):
+    def toggle_option(self, string):  # pylint: disable=redefined-outer-name
         """:toggle_option <string>
 
         Toggle a boolean option named <string>.
@@ -627,7 +634,8 @@ class Actions(FileManagerAware, SettingsAware):
         if func is not None:
             self.settings['sort'] = str(func)
 
-    def mark_files(self, all=False, toggle=False, val=None, movedown=None, narg=None):
+    def mark_files(self, all=False,  # pylint: disable=redefined-builtin,too-many-arguments
+                   toggle=False, val=None, movedown=None, narg=None):
         """A wrapper for the directory.mark_xyz functions.
 
         Arguments:
@@ -714,11 +722,14 @@ class Actions(FileManagerAware, SettingsAware):
                 if arg is None:
                     return False
                 if hasattr(arg, 'search'):
-                    fnc = lambda x: arg.search(x.basename)
+                    def fnc(obj):
+                        return arg.search(obj.basename)
                 else:
-                    fnc = lambda x: arg in x.basename
+                    def fnc(obj):
+                        return arg in obj.basename
             elif order == 'tag':
-                fnc = lambda x: x.realpath in self.tags
+                def fnc(obj):
+                    return obj.realpath in self.tags
 
             return self.thisdir.search_fnc(fnc=fnc, offset=offset, forward=forward)
 
@@ -727,22 +738,27 @@ class Actions(FileManagerAware, SettingsAware):
             if original_order is not None or not cwd.cycle_list:
                 lst = list(cwd.files)
                 if order == 'size':
-                    fnc = lambda item: -item.size
+                    def fnc(item):
+                        return -item.size
                 elif order == 'mimetype':
-                    fnc = lambda item: item.mimetype or ''
+                    def fnc(item):
+                        return item.mimetype or ''
                 elif order == 'ctime':
-                    fnc = lambda item: -int(item.stat and item.stat.st_ctime)
+                    def fnc(item):
+                        return -int(item.stat and item.stat.st_ctime)
                 elif order == 'atime':
-                    fnc = lambda item: -int(item.stat and item.stat.st_atime)
+                    def fnc(item):
+                        return -int(item.stat and item.stat.st_atime)
                 elif order == 'mtime':
-                    fnc = lambda item: -int(item.stat and item.stat.st_mtime)
+                    def fnc(item):
+                        return -int(item.stat and item.stat.st_mtime)
                 lst.sort(key=fnc)
                 cwd.set_cycle_list(lst)
                 return cwd.cycle(forward=None)
 
             return cwd.cycle(forward=forward)
 
-    def set_search_method(self, order, forward=True):
+    def set_search_method(self, order, forward=True):  # pylint: disable=unused-argument
         if order in ('search', 'tag', 'size', 'mimetype', 'ctime',
                      'mtime', 'atime'):
             self.search_method = order
@@ -845,7 +861,7 @@ class Actions(FileManagerAware, SettingsAware):
 
     def display_command_help(self, console_widget):
         try:
-            command = console_widget._get_cmd_class()
+            command = console_widget._get_cmd_class()  # pylint: disable=protected-access
         except Exception:
             self.notify("Feature not available!", bad=True)
             return
@@ -887,11 +903,11 @@ class Actions(FileManagerAware, SettingsAware):
             return
 
         pager = self.ui.open_pager()
-        f = self.thisfile.get_preview_source(pager.wid, pager.hei)
+        fobj = self.thisfile.get_preview_source(pager.wid, pager.hei)
         if self.thisfile.is_image_preview():
-            pager.set_image(f)
+            pager.set_image(fobj)
         else:
-            pager.set_source(f)
+            pager.set_source(fobj)
 
     # --------------------------
     # -- Previews
@@ -903,17 +919,17 @@ class Actions(FileManagerAware, SettingsAware):
         except Exception:
             return False
 
-    if version_info[0] == 3:
-        def sha1_encode(self, path):
+    @staticmethod
+    def sha1_encode(path):
+        if version_info[0] < 3:
+            return os.path.join(ranger.arg.cachedir,
+                                sha1(path).hexdigest()) + '.jpg'
+        else:
             return os.path.join(ranger.arg.cachedir,
                                 sha1(path.encode('utf-8', 'backslashreplace'))
                                 .hexdigest()) + '.jpg'
-    else:
-        def sha1_encode(self, path):
-            return os.path.join(ranger.arg.cachedir,
-                                sha1(path).hexdigest()) + '.jpg'
 
-    def get_preview(self, file, width, height):
+    def get_preview(self, file, width, height):  # pylint: disable=too-many-return-statements
         pager = self.ui.get_pager()
         path = file.realpath
 
@@ -937,8 +953,8 @@ class Actions(FileManagerAware, SettingsAware):
                 if data['loading']:
                     return None
 
-            found = data.get((-1, -1), data.get((width, -1),
-                                                data.get((-1, height), data.get((width, height), False))))
+            found = data.get((-1, -1), data.get(
+                (width, -1), data.get((-1, height), data.get((width, height), False))))
             if found is False:
                 try:
                     stat_ = os.stat(self.settings.preview_script)
@@ -962,7 +978,8 @@ class Actions(FileManagerAware, SettingsAware):
                     return path
 
                 cacheimg = os.path.join(ranger.arg.cachedir, self.sha1_encode(path))
-                if (os.path.isfile(cacheimg) and os.path.getmtime(cacheimg) > os.path.getmtime(path)):
+                if os.path.isfile(cacheimg) and \
+                        os.path.getmtime(cacheimg) > os.path.getmtime(path):
                     data['foundpreview'] = True
                     data['imagepreview'] = True
                     pager.set_image(cacheimg)
@@ -975,34 +992,34 @@ class Actions(FileManagerAware, SettingsAware):
                                          silent=True, descr="Getting preview of %s" % path)
 
                 def on_after(signal):
-                    exit = signal.process.poll()
+                    rcode = signal.process.poll()
                     content = signal.loader.stdout_buffer
                     data['foundpreview'] = True
-                    if exit == 0:
+                    if rcode == 0:
                         data[(width, height)] = content
-                    elif exit == 3:
+                    elif rcode == 3:
                         data[(-1, height)] = content
-                    elif exit == 4:
+                    elif rcode == 4:
                         data[(width, -1)] = content
-                    elif exit == 5:
+                    elif rcode == 5:
                         data[(-1, -1)] = content
-                    elif exit == 6:
+                    elif rcode == 6:
                         data['imagepreview'] = True
-                    elif exit == 7:
+                    elif rcode == 7:
                         data['directimagepreview'] = True
-                    elif exit == 1:
+                    elif rcode == 1:
                         data[(-1, -1)] = None
                         data['foundpreview'] = False
-                    elif exit == 2:
-                        f = codecs.open(path, 'r', errors='ignore')
+                    elif rcode == 2:
+                        fobj = codecs.open(path, 'r', errors='ignore')
                         try:
-                            data[(-1, -1)] = f.read(1024 * 32)
+                            data[(-1, -1)] = fobj.read(1024 * 32)
                         except UnicodeDecodeError:
-                            f.close()
-                            f = codecs.open(path, 'r', encoding='latin-1',
-                                            errors='ignore')
-                            data[(-1, -1)] = f.read(1024 * 32)
-                        f.close()
+                            fobj.close()
+                            fobj = codecs.open(path, 'r', encoding='latin-1',
+                                               errors='ignore')
+                            data[(-1, -1)] = fobj.read(1024 * 32)
+                        fobj.close()
                     else:
                         data[(-1, -1)] = None
                     if self.thisfile and self.thisfile.realpath == path:
@@ -1020,7 +1037,7 @@ class Actions(FileManagerAware, SettingsAware):
                             pager.set_source(self.thisfile.get_preview_source(
                                 pager.wid, pager.hei))
 
-                def on_destroy(signal):
+                def on_destroy(signal):  # pylint: disable=unused-argument
                     try:
                         del self.previews[path]
                     except Exception:
@@ -1173,7 +1190,7 @@ class Actions(FileManagerAware, SettingsAware):
 
         temporary_file = tempfile.NamedTemporaryFile()
 
-        def write(string):
+        def write(string):  # pylint: disable=redefined-outer-name
             temporary_file.write(string.encode('utf-8'))
 
         def recurse(before, pointer):
@@ -1199,7 +1216,7 @@ class Actions(FileManagerAware, SettingsAware):
     def dump_commands(self):
         temporary_file = tempfile.NamedTemporaryFile()
 
-        def write(string):
+        def write(string):  # pylint: disable=redefined-outer-name
             temporary_file.write(string.encode('utf-8'))
 
         undocumented = []
@@ -1225,7 +1242,7 @@ class Actions(FileManagerAware, SettingsAware):
     def dump_settings(self):
         temporary_file = tempfile.NamedTemporaryFile()
 
-        def write(string):
+        def write(string):  # pylint: disable=redefined-outer-name
             temporary_file.write(string.encode('utf-8'))
 
         for setting in sorted(ALLOWED_SETTINGS):
@@ -1257,7 +1274,7 @@ class Actions(FileManagerAware, SettingsAware):
         assert mode in ('set', 'add', 'remove', 'toggle')
         cwd = self.thisdir
         if not narg and not dirarg:
-            selected = (f for f in self.thistab.get_selection() if f in cwd.files)
+            selected = (fobj for fobj in self.thistab.get_selection() if fobj in cwd.files)
         else:
             if not dirarg and narg:
                 direction = Direction(down=1)
@@ -1293,32 +1310,32 @@ class Actions(FileManagerAware, SettingsAware):
 
     def paste_symlink(self, relative=False):
         copied_files = self.copy_buffer
-        for f in copied_files:
-            self.notify(next_available_filename(f.basename))
+        for fobj in copied_files:
+            self.notify(next_available_filename(fobj.basename))
             try:
-                new_name = next_available_filename(f.basename)
+                new_name = next_available_filename(fobj.basename)
                 if relative:
-                    relative_symlink(f.path, join(getcwd(), new_name))
+                    relative_symlink(fobj.path, join(getcwd(), new_name))
                 else:
-                    symlink(f.path, join(getcwd(), new_name))
-            except Exception as x:
-                self.notify(x)
+                    symlink(fobj.path, join(getcwd(), new_name))
+            except Exception as ex:
+                self.notify(ex)
 
     def paste_hardlink(self):
-        for f in self.copy_buffer:
+        for fobj in self.copy_buffer:
             try:
-                new_name = next_available_filename(f.basename)
-                link(f.path, join(getcwd(), new_name))
-            except Exception as x:
-                self.notify(x)
+                new_name = next_available_filename(fobj.basename)
+                link(fobj.path, join(getcwd(), new_name))
+            except Exception as ex:
+                self.notify(ex)
 
     def paste_hardlinked_subtree(self):
-        for f in self.copy_buffer:
+        for fobj in self.copy_buffer:
             try:
-                target_path = join(getcwd(), f.basename)
-                self._recurse_hardlinked_tree(f.path, target_path)
-            except Exception as x:
-                self.notify(x)
+                target_path = join(getcwd(), fobj.basename)
+                self._recurse_hardlinked_tree(fobj.path, target_path)
+            except Exception as ex:
+                self.notify(ex)
 
     def _recurse_hardlinked_tree(self, source_path, target_path):
         if isdir(source_path):
@@ -1348,23 +1365,23 @@ class Actions(FileManagerAware, SettingsAware):
         self.notify("Deleting!")
         # COMPAT: old command.py use fm.delete() without arguments
         if files is None:
-            files = (f.path for f in self.thistab.get_selection())
-        files = [os.path.abspath(f) for f in files]
-        for f in files:
+            files = (fobj.path for fobj in self.thistab.get_selection())
+        files = [os.path.abspath(path) for path in files]
+        for path in files:
             # Untag the deleted files.
             for tag in self.fm.tags.tags:
-                if str(tag).startswith(f):
+                if str(tag).startswith(path):
                     self.fm.tags.remove(tag)
-        self.copy_buffer = set(filter(lambda f: f.path not in files, self.copy_buffer))
-        for f in files:
-            if isdir(f) and not os.path.islink(f):
+        self.copy_buffer = set(filter(lambda fobj: fobj.path not in files, self.copy_buffer))
+        for path in files:
+            if isdir(path) and not os.path.islink(path):
                 try:
-                    shutil.rmtree(f)
+                    shutil.rmtree(path)
                 except OSError as err:
                     self.notify(err)
             else:
                 try:
-                    os.remove(f)
+                    os.remove(path)
                 except OSError as err:
                     self.notify(err)
         self.thistab.ensure_correct_pointer()
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index 1a3a7cb3..8535a17c 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -21,7 +21,8 @@ from ranger.container.tags import Tags, TagsDummy
 from ranger.gui.ui import UI
 from ranger.container.bookmarks import Bookmarks
 from ranger.core.runner import Runner
-from ranger.ext.img_display import *
+from ranger.ext.img_display import (W3MImageDisplayer, ITerm2ImageDisplayer,
+                                    URXVTImageDisplayer, URXVTImageFSDisplayer, ImageDisplayer)
 from ranger.core.metadata import MetadataManager
 from ranger.ext.rifle import Rifle
 from ranger.container.directory import Directory
@@ -29,10 +30,12 @@ from ranger.ext.signals import SignalDispatcher
 from ranger.core.loader import Loader
 from ranger.ext import logutils
 
-log = logging.getLogger(__name__)
 
+LOG = logging.getLogger(__name__)
 
-class FM(Actions, SignalDispatcher):
+
+class FM(Actions,  # pylint: disable=too-many-instance-attributes,abstract-method
+         SignalDispatcher):
     input_blocked = False
     input_blocked_until = 0
     mode = 'normal'  # either 'normal' or 'visual'.
@@ -43,15 +46,12 @@ class FM(Actions, SignalDispatcher):
     _visual_start = None
     _visual_start_pos = None
 
-    def __init__(self, ui=None, bookmarks=None, tags=None, paths=['.']):
+    def __init__(self, ui=None, bookmarks=None, tags=None, paths=None):
         """Initialize FM."""
         Actions.__init__(self)
         SignalDispatcher.__init__(self)
-        if ui is None:
-            self.ui = UI()
-        else:
-            self.ui = ui
-        self.start_paths = paths
+        self.ui = ui if ui is not None else UI()
+        self.start_paths = paths if paths is not None else ['.']
         self.directories = dict()
         self.bookmarks = bookmarks
         self.current_tab = 1
@@ -65,6 +65,7 @@ class FM(Actions, SignalDispatcher):
         self.copy_buffer = set()
         self.do_cut = False
         self.metadata = MetadataManager()
+        self.image_displayer = None
 
         try:
             self.username = pwd.getpwuid(os.geteuid()).pw_name
@@ -107,7 +108,7 @@ class FM(Actions, SignalDispatcher):
         if not ranger.arg.clean and self.tags is None:
             self.tags = Tags(self.confpath('tagged'))
         elif ranger.arg.clean:
-            self.tags = TagsDummy("")
+            self.tags = TagsDummy("")  # pylint: disable=redefined-variable-type
 
         if self.bookmarks is None:
             if ranger.arg.clean:
@@ -203,7 +204,8 @@ class FM(Actions, SignalDispatcher):
                 if debug:
                     raise
 
-    def get_log(self):
+    @staticmethod
+    def get_log():
         """Return the current log
 
         The log is returned as a list of string
@@ -255,11 +257,11 @@ class FM(Actions, SignalDispatcher):
         import shutil
         from errno import EEXIST
 
-        def copy(_from, to):
-            if os.path.exists(self.confpath(to)):
-                sys.stderr.write("already exists: %s\n" % self.confpath(to))
+        def copy(src, dest):
+            if os.path.exists(self.confpath(dest)):
+                sys.stderr.write("already exists: %s\n" % self.confpath(dest))
             else:
-                sys.stderr.write("creating: %s\n" % self.confpath(to))
+                sys.stderr.write("creating: %s\n" % self.confpath(dest))
                 try:
                     os.makedirs(ranger.arg.confdir)
                 except OSError as err:
@@ -270,9 +272,9 @@ class FM(Actions, SignalDispatcher):
                         print("files, use the --clean option.")
                         raise SystemExit()
                 try:
-                    shutil.copy(self.relpath(_from), self.confpath(to))
-                except Exception as e:
-                    sys.stderr.write("  ERROR: %s\n" % str(e))
+                    shutil.copy(self.relpath(src), self.confpath(dest))
+                except Exception as ex:
+                    sys.stderr.write("  ERROR: %s\n" % str(ex))
         if which == 'rifle' or which == 'all':
             copy('config/rifle.conf', 'rifle.conf')
         if which == 'commands' or which == 'all':
@@ -298,14 +300,16 @@ class FM(Actions, SignalDispatcher):
         else:
             sys.stderr.write("Unknown config file `%s'\n" % which)
 
-    def confpath(self, *paths):
+    @staticmethod
+    def confpath(*paths):
         """returns the path relative to rangers configuration directory"""
         if ranger.arg.clean:
             assert 0, "Should not access relpath_conf in clean mode!"
         else:
             return os.path.join(ranger.arg.confdir, *paths)
 
-    def relpath(self, *paths):
+    @staticmethod
+    def relpath(*paths):
         """returns the path relative to rangers library directory"""
         return os.path.join(ranger.RANGERDIR, *paths)
 
@@ -319,7 +323,9 @@ class FM(Actions, SignalDispatcher):
             self.directories[path] = obj
             return obj
 
-    def garbage_collect(self, age, tabs=None):  # tabs=None is for COMPATibility
+    def garbage_collect(
+            self, age,
+            tabs=None):  # tabs=None is for COMPATibility pylint: disable=unused-argument
         """Delete unused directory objects"""
         for key in tuple(self.directories):
             value = self.directories[key]
@@ -346,8 +352,6 @@ class FM(Actions, SignalDispatcher):
 
         self.enter_dir(self.thistab.path)
 
-        gc_tick = 0
-
         # for faster lookup:
         ui = self.ui
         throbber = ui.throbber
@@ -357,7 +361,7 @@ class FM(Actions, SignalDispatcher):
 
         ranger.api.hook_ready(self)
 
-        try:
+        try:  # pylint: disable=too-many-nested-blocks
             while True:
                 loader.work()
                 if has_throbber:
diff --git a/ranger/core/linemode.py b/ranger/core/linemode.py
index 4b29443a..d9d91350 100644
--- a/ranger/core/linemode.py
+++ b/ranger/core/linemode.py
@@ -4,7 +4,8 @@
 # Author: Wojciech Siewierski <wojciech.siewierski@onet.pl>, 2015
 
 import sys
-from abc import *
+
+from abc import ABCMeta, abstractproperty, abstractmethod
 from datetime import datetime
 from ranger.ext.human_readable import human_readable
 from ranger.ext import spawn
@@ -51,7 +52,7 @@ class LinemodeBase(object):
         raise NotImplementedError
 
 
-class DefaultLinemode(LinemodeBase):
+class DefaultLinemode(LinemodeBase):  # pylint: disable=abstract-method
     name = "filename"
 
     def filetitle(self, file, metadata):
@@ -98,7 +99,7 @@ class FileInfoLinemode(LinemodeBase):
 
     def infostring(self, file, metadata):
         if not file.is_directory:
-            from subprocess import Popen, PIPE, CalledProcessError
+            from subprocess import CalledProcessError
             try:
                 fileinfo = spawn.check_output(["file", "-bL", file.path]).strip()
             except CalledProcessError:
diff --git a/ranger/core/loader.py b/ranger/core/loader.py
index 191dbf36..862e01b4 100644
--- a/ranger/core/loader.py
+++ b/ranger/core/loader.py
@@ -2,15 +2,17 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
 from collections import deque
-from time import time, sleep
 from subprocess import Popen, PIPE
-from ranger.core.shared import FileManagerAware
-from ranger.ext.signals import SignalDispatcher
-from ranger.ext.human_readable import human_readable
+from time import time, sleep
 import math
 import os.path
-import sys
 import select
+import sys
+import errno
+
+from ranger.core.shared import FileManagerAware
+from ranger.ext.signals import SignalDispatcher
+from ranger.ext.human_readable import human_readable
 try:
     import chardet
     HAVE_CHARDET = True
@@ -43,7 +45,7 @@ class Loadable(object):
         pass
 
 
-class CopyLoader(Loadable, FileManagerAware):
+class CopyLoader(Loadable, FileManagerAware):  # pylint: disable=too-many-instance-attributes
     progressbar_supported = True
 
     def __init__(self, copy_buffer, do_cut=False, overwrite=False):
@@ -60,7 +62,7 @@ class CopyLoader(Loadable, FileManagerAware):
     def _calculate_size(self, step):
         from os.path import join
         size = 0
-        stack = [f.path for f in self.copy_buffer]
+        stack = [fobj.path for fobj in self.copy_buffer]
         while stack:
             fname = stack.pop()
             if os.path.islink(fname):
@@ -89,50 +91,51 @@ class CopyLoader(Loadable, FileManagerAware):
                     self.description = "moving: " + self.one_file.path + size_str
                 else:
                     self.description = "moving files from: " + self.one_file.dirname + size_str
-                for f in self.copy_buffer:
-                    for tf in self.fm.tags.tags:
-                        if tf == f.path or str(tf).startswith(f.path):
-                            tag = self.fm.tags.tags[tf]
-                            self.fm.tags.remove(tf)
-                            self.fm.tags.tags[tf.replace(f.path, self.original_path
-                                                         + '/' + f.basename)] = tag
+                for fobj in self.copy_buffer:
+                    for path in self.fm.tags.tags:
+                        if path == fobj.path or str(path).startswith(fobj.path):
+                            tag = self.fm.tags.tags[path]
+                            self.fm.tags.remove(path)
+                            self.fm.tags.tags[path.replace(fobj.path, self.original_path
+                                                           + '/' + fobj.basename)] = tag
                             self.fm.tags.dump()
-                    d = 0
-                    for d in shutil_g.move(src=f.path,
+                    n = 0
+                    for n in shutil_g.move(src=fobj.path,
                                            dst=self.original_path,
                                            overwrite=self.overwrite):
-                        self.percent = float(done + d) / size * 100.
+                        self.percent = float(done + n) / size * 100.
                         yield
-                    done += d
+                    done += n
             else:
                 if len(self.copy_buffer) == 1:
                     self.description = "copying: " + self.one_file.path + size_str
                 else:
                     self.description = "copying files from: " + self.one_file.dirname + size_str
-                for f in self.copy_buffer:
-                    if os.path.isdir(f.path) and not os.path.islink(f.path):
-                        d = 0
-                        for d in shutil_g.copytree(src=f.path,
+                for fobj in self.copy_buffer:
+                    if os.path.isdir(fobj.path) and not os.path.islink(fobj.path):
+                        n = 0
+                        for n in shutil_g.copytree(src=fobj.path,
                                                    dst=os.path.join(
-                                                       self.original_path, f.basename),
+                                                       self.original_path, fobj.basename),
                                                    symlinks=True,
                                                    overwrite=self.overwrite):
-                            self.percent = float(done + d) / size * 100.
+                            self.percent = float(done + n) / size * 100.
                             yield
-                        done += d
+                        done += n
                     else:
-                        d = 0
-                        for d in shutil_g.copy2(f.path, self.original_path,
+                        n = 0
+                        for n in shutil_g.copy2(fobj.path, self.original_path,
                                                 symlinks=True,
                                                 overwrite=self.overwrite):
-                            self.percent = float(done + d) / size * 100.
+                            self.percent = float(done + n) / size * 100.
                             yield
-                        done += d
+                        done += n
             cwd = self.fm.get_directory(self.original_path)
             cwd.load_content()
 
 
-class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
+class CommandLoader(  # pylint: disable=too-many-instance-attributes
+        Loadable, SignalDispatcher, FileManagerAware):
     """Run an external command with the loader.
 
     Output from stderr will be reported.  Ensure that the process doesn't
@@ -142,7 +145,8 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
     finished = False
     process = None
 
-    def __init__(self, args, descr, silent=False, read=False, input=None,
+    def __init__(self, args, descr,  # pylint: disable=too-many-arguments
+                 silent=False, read=False, input=None,  # pylint: disable=redefined-builtin
                  kill_on_pause=False, popenArgs=None):
         SignalDispatcher.__init__(self)
         Loadable.__init__(self, self.generate(), descr)
@@ -152,18 +156,14 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
         self.stdout_buffer = ""
         self.input = input
         self.kill_on_pause = kill_on_pause
-        self.popenArgs = popenArgs
+        self.popenArgs = popenArgs  # pylint: disable=invalid-name
 
-    def generate(self):
+    def generate(self):  # pylint: disable=too-many-branches,too-many-statements
         py3 = sys.version_info[0] >= 3
-        if self.input:
-            stdin = PIPE
-        else:
-            stdin = open(os.devnull, 'r')
-        popenArgs = {} if self.popenArgs is None else self.popenArgs
-        popenArgs['stdout'] = popenArgs['stderr'] = PIPE
-        popenArgs['stdin'] = stdin
-        self.process = process = Popen(self.args, **popenArgs)
+        popenargs = {} if self.popenArgs is None else self.popenArgs
+        popenargs['stdout'] = popenargs['stderr'] = PIPE
+        popenargs['stdin'] = PIPE if self.input else open(os.devnull, 'r')
+        self.process = process = Popen(self.args, **popenargs)
         self.signal_emit('before', process=process, loader=self)
         if self.input:
             if py3:
@@ -173,11 +173,11 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
                 stdin = process.stdin
             try:
                 stdin.write(self.input)
-            except IOError as e:
-                if e.errno != errno.EPIPE and e.errno != errno.EINVAL:
+            except IOError as ex:
+                if ex.errno != errno.EPIPE and ex.errno != errno.EINVAL:
                     raise
             stdin.close()
-        if self.silent and not self.read:
+        if self.silent and not self.read:  # pylint: disable=too-many-nested-blocks
             while process.poll() is None:
                 yield
                 if self.finished:
@@ -194,17 +194,17 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
                 if self.finished:
                     break
                 try:
-                    rd, _, __ = select.select(selectlist, [], [], 0.03)
-                    if rd:
-                        rd = rd[0]
-                        if rd == process.stderr:
-                            read = rd.readline()
+                    robjs, _, _ = select.select(selectlist, [], [], 0.03)
+                    if robjs:
+                        robjs = robjs[0]
+                        if robjs == process.stderr:
+                            read = robjs.readline()
                             if py3:
                                 read = safeDecode(read)
                             if read:
                                 self.fm.notify(read, bad=True)
-                        elif rd == process.stdout:
-                            read = rd.read(512)
+                        elif robjs == process.stdout:
+                            read = robjs.read(512)
                             if py3:
                                 read = safeDecode(read)
                             if read:
@@ -260,10 +260,10 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
                 pass
 
 
-def safeDecode(string):
+def safeDecode(string):  # pylint: disable=invalid-name
     try:
         return string.decode("utf-8")
-    except (UnicodeDecodeError):
+    except UnicodeDecodeError:
         if HAVE_CHARDET:
             codec = chardet.detect(string)["encoding"]
             return string.decode(codec, 'ignore')
@@ -287,6 +287,7 @@ class Loader(FileManagerAware):
         self.throbber_status = 0
         self.rotate()
         self.old_item = None
+        self.status = None
 
     def rotate(self):
         """Rotate the throbber"""
@@ -315,19 +316,19 @@ class Loader(FileManagerAware):
         else:
             obj.unpause()
 
-    def move(self, _from, to):
+    def move(self, pos_src, pos_dest):
         try:
-            item = self.queue[_from]
+            item = self.queue[pos_src]
         except IndexError:
             return
 
-        del self.queue[_from]
+        del self.queue[pos_src]
 
-        if to == 0:
+        if pos_dest == 0:
             self.queue.appendleft(item)
-            if _from != 0:
+            if pos_src != 0:
                 self.queue[1].pause()
-        elif to == -1:
+        elif pos_dest == -1:
             self.queue.append(item)
         else:
             raise NotImplementedError
diff --git a/ranger/core/main.py b/ranger/core/main.py
index 6d4fdaa6..48e3a6f0 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -6,13 +6,18 @@
 import os.path
 import sys
 import tempfile
-from ranger import __version__
 from logging import getLogger
 
-log = getLogger(__name__)
+from ranger import __version__
+
+
+LOG = getLogger(__name__)
 
 
-def main():
+def main(
+        # pylint: disable=too-many-locals,too-many-return-statements
+        # pylint: disable=too-many-branches,too-many-statements
+):
     """initialize objects and run the filemanager"""
     import locale
     import ranger.api
@@ -24,9 +29,9 @@ def main():
     ranger.arg = arg = parse_arguments()
     setup_logging(debug=arg.debug, logfile=arg.logfile)
 
-    log.info("Ranger version {0}".format(__version__))
-    log.info('Running on Python ' + sys.version.replace('\n', ''))
-    log.info("Process ID is {0}".format(os.getpid()))
+    LOG.info("Ranger version %s", __version__)
+    LOG.info('Running on Python ' + sys.version.replace('\n', ''))
+    LOG.info("Process ID is %s", os.getpid())
 
     try:
         locale.setlocale(locale.LC_ALL, '')
@@ -43,8 +48,8 @@ def main():
     if 'SHELL' not in os.environ:
         os.environ['SHELL'] = 'sh'
 
-    log.debug("config dir: '{0}'".format(arg.confdir))
-    log.debug("cache dir: '{0}'".format(arg.cachedir))
+    LOG.debug("config dir: '%s'", arg.confdir)
+    LOG.debug("cache dir: '%s'", arg.cachedir)
 
     if arg.copy_config is not None:
         fm = FM()
@@ -54,13 +59,13 @@ def main():
         fm = FM()
         try:
             if sys.version_info[0] >= 3:
-                f = open(fm.confpath('tagged'), 'r', errors='replace')
+                fobj = open(fm.confpath('tagged'), 'r', errors='replace')
             else:
-                f = open(fm.confpath('tagged'), 'r')
+                fobj = open(fm.confpath('tagged'), 'r')
         except Exception:
             pass
         else:
-            for line in f.readlines():
+            for line in fobj.readlines():
                 if len(line) > 2 and line[1] == ':':
                     if line[0] in arg.list_tagged_files:
                         sys.stdout.write(line[2:])
@@ -68,7 +73,7 @@ def main():
                     sys.stdout.write(line)
         return 1 if arg.fail_unless_cd else 0  # COMPAT
 
-    SettingsAware._setup(Settings())
+    SettingsAware._setup(Settings())  # pylint: disable=protected-access
 
     if arg.selectfile:
         arg.selectfile = os.path.abspath(arg.selectfile)
@@ -102,14 +107,14 @@ def main():
     try:
         # Initialize objects
         fm = FM(paths=targets)
-        FileManagerAware._setup(fm)
+        FileManagerAware._setup(fm)  # pylint: disable=protected-access
         load_settings(fm, arg.clean)
 
         if arg.list_unused_keys:
             from ranger.ext.keybinding_parser import (special_keys,
                                                       reversed_special_keys)
             maps = fm.ui.keymaps['browser']
-            for key in sorted(special_keys.values(), key=lambda x: str(x)):
+            for key in sorted(special_keys.values(), key=str):
                 if key not in maps:
                     print("<%s>" % reversed_special_keys[key])
             for key in range(33, 127):
@@ -124,7 +129,7 @@ def main():
         if fm.username == 'root':
             fm.settings.preview_files = False
             fm.settings.use_preview_script = False
-            log.info("Running as root, disabling the file previews.")
+            LOG.info("Running as root, disabling the file previews.")
         if not arg.debug:
             from ranger.ext import curses_interrupt_handler
             curses_interrupt_handler.install_interrupt_handler()
@@ -150,7 +155,7 @@ def main():
             import cProfile
             import pstats
             profile = None
-            ranger.__fm = fm
+            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)
         else:
@@ -184,13 +189,13 @@ def main():
             print("ranger crashed.  "
                   "Please report this traceback at:")
             print("https://github.com/hut/ranger/issues")
-            return 1
-        return 0
+            return 1  # pylint: disable=lost-exception
+        return 0  # pylint: disable=lost-exception
 
 
 def parse_arguments():
     """Parse the program arguments"""
-    from optparse import OptionParser, SUPPRESS_HELP
+    from optparse import OptionParser, SUPPRESS_HELP  # pylint: disable=deprecated-module
     from os.path import expanduser
     from ranger import CONFDIR, CACHEDIR, USAGE, VERSION
     from ranger.ext.openstruct import OpenStruct
@@ -262,18 +267,21 @@ def parse_arguments():
     return arg
 
 
-def load_settings(fm, clean):
+COMMANDS_EXCLUDE = ['settings', 'notify']
+
+
+def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
+        fm, clean):
     from ranger.core.actions import Actions
     import ranger.core.shared
     import ranger.api.commands
-    from ranger.config import commands
+    from ranger.config import commands as commands_default
 
     # Load default commands
     fm.commands = ranger.api.commands.CommandContainer()
-    exclude = ['settings', 'notify']
-    include = [name for name in dir(Actions) if name not in exclude]
+    include = [name for name in dir(Actions) if name not in COMMANDS_EXCLUDE]
     fm.commands.load_commands_from_object(fm, include)
-    fm.commands.load_commands_from_module(commands)
+    fm.commands.load_commands_from_module(commands_default)
 
     if not clean:
         allow_access_to_confdir(ranger.arg.confdir, True)
@@ -284,13 +292,13 @@ def load_settings(fm, clean):
             old_bytecode_setting = sys.dont_write_bytecode
             sys.dont_write_bytecode = True
             try:
-                import commands
-                fm.commands.load_commands_from_module(commands)
-            except ImportError as e:
-                log.debug("Failed to import custom commands from '{0}'".format(custom_comm_path))
-                log.exception(e)
+                import commands as commands_custom
+                fm.commands.load_commands_from_module(commands_custom)
+            except ImportError as ex:
+                LOG.debug("Failed to import custom commands from '%s'", custom_comm_path)
+                LOG.exception(ex)
             else:
-                log.debug("Loaded custom commands from '{0}'".format(custom_comm_path))
+                LOG.debug("Loaded custom commands from '%s'", custom_comm_path)
             sys.dont_write_bytecode = old_bytecode_setting
 
         allow_access_to_confdir(ranger.arg.confdir, False)
@@ -315,9 +323,9 @@ def load_settings(fm, clean):
             pass
         else:
             if not os.path.exists(fm.confpath('plugins', '__init__.py')):
-                log.debug("Creating missing '__init__.py' file in plugin folder")
-                f = open(fm.confpath('plugins', '__init__.py'), 'w')
-                f.close()
+                LOG.debug("Creating missing '__init__.py' file in plugin folder")
+                fobj = open(fm.confpath('plugins', '__init__.py'), 'w')
+                fobj.close()
 
             ranger.fm = fm
             for plugin in sorted(plugins):
@@ -332,12 +340,12 @@ def load_settings(fm, clean):
                     else:
                         module = importlib.import_module('plugins.' + plugin)
                         fm.commands.load_commands_from_module(module)
-                    log.debug("Loaded plugin '{0}'".format(plugin))
-                except Exception as e:
-                    mex = "Error while loading plugin '{0}'".format(plugin)
-                    log.error(mex)
-                    log.exception(e)
-                    fm.notify(mex, bad=True)
+                    LOG.debug("Loaded plugin '%s'", plugin)
+                except Exception as ex:
+                    ex_msg = "Error while loading plugin '{0}'".format(plugin)
+                    LOG.error(ex_msg)
+                    LOG.exception(ex)
+                    fm.notify(ex_msg, bad=True)
             ranger.fm = None
 
         # COMPAT: Load the outdated options.py
@@ -367,7 +375,6 @@ Remove the options.py or discard stderr to get rid of this warning.
 
 
 def allow_access_to_confdir(confdir, allow):
-    import sys
     from errno import EEXIST
 
     if allow:
@@ -381,7 +388,7 @@ def allow_access_to_confdir(confdir, allow):
                 print("files, use the --clean option.")
                 raise SystemExit()
         else:
-            log.debug("Created config directory '{0}'".format(confdir))
+            LOG.debug("Created config directory '%s'", confdir)
         if confdir not in sys.path:
             sys.path[0:0] = [confdir]
     else:
diff --git a/ranger/core/metadata.py b/ranger/core/metadata.py
index 5afa2995..1d0e1cd5 100644
--- a/ranger/core/metadata.py
+++ b/ranger/core/metadata.py
@@ -11,14 +11,15 @@ The database is contained in a local .metadata.json file.
 # TODO: Update metadata keys if a file gets renamed/moved
 # TODO: A global metadata file, maybe as a replacement for tags
 
-METADATA_FILE_NAME = ".metadata.json"
-DEEP_SEARCH_DEFAULT = False
-
 import copy
 from os.path import join, dirname, exists, basename
 from ranger.ext.openstruct import DefaultOpenStruct as ostruct
 
 
+METADATA_FILE_NAME = ".metadata.json"
+DEEP_SEARCH_DEFAULT = False
+
+
 class MetadataManager(object):
 
     def __init__(self):
@@ -42,10 +43,6 @@ class MetadataManager(object):
                 return ostruct()
 
     def set_metadata(self, filename, update_dict):
-        import json
-        result = None
-        found = False
-
         if not self.deep_search:
             metafile = next(self._get_metafile_names(filename))
             return self._set_metadata_raw(filename, update_dict, metafile)
@@ -55,7 +52,6 @@ class MetadataManager(object):
 
     def _set_metadata_raw(self, filename, update_dict, metafile):
         import json
-        valid = (filename, basename(filename))
 
         entries = self._get_metafile_content(metafile)
         try:
@@ -86,14 +82,13 @@ class MetadataManager(object):
         self.metadata_cache[filename] = entry
         self.metafile_cache[metafile] = entries
 
-        with open(metafile, "w") as f:
-            json.dump(entries, f, check_circular=True, indent=2)
+        with open(metafile, "w") as fobj:
+            json.dump(entries, fobj, check_circular=True, indent=2)
 
     def _get_entry(self, filename):
         if filename in self.metadata_cache:
             return self.metadata_cache[filename]
         else:
-            valid = (filename, basename(filename))
 
             # Try to find an entry for this file in any of
             # the applicable .metadata.json files
@@ -120,9 +115,9 @@ class MetadataManager(object):
             return self.metafile_cache[metafile]
         else:
             if exists(metafile):
-                with open(metafile, "r") as f:
+                with open(metafile, "r") as fobj:
                     try:
-                        entries = json.load(f)
+                        entries = json.load(fobj)
                     except ValueError:
                         raise ValueError("Failed decoding JSON file %s" %
                                          metafile)
diff --git a/ranger/core/runner.py b/ranger/core/runner.py
index d00ebc80..1a9c8193 100644
--- a/ranger/core/runner.py
+++ b/ranger/core/runner.py
@@ -30,7 +30,7 @@ from ranger.ext.popen_forked import Popen_forked
 
 
 # TODO: Remove unused parts of runner.py
-#ALLOWED_FLAGS = 'sdpwcrtSDPWCRT'
+# ALLOWED_FLAGS = 'sdpwcrtSDPWCRT'
 ALLOWED_FLAGS = 'cfrtCFRT'
 
 
@@ -63,6 +63,8 @@ class Context(object):
     """
 
     def __init__(self, **keywords):
+        self.flags = None
+        self.wait = False
         self.__dict__ = keywords
 
     @property
@@ -85,7 +87,7 @@ class Context(object):
                 self.flags = ''.join(c for c in self.flags if c not in bad)
 
 
-class Runner(object):
+class Runner(object):  # pylint: disable=too-few-public-methods
 
     def __init__(self, ui=None, logfunc=None, fm=None):
         self.ui = ui
@@ -113,9 +115,12 @@ class Runner(object):
                 except Exception:
                     self._log("Failed to suspend UI")
 
-    def __call__(self, action=None, try_app_first=False,
-                 app='default', files=None, mode=0,
-                 flags='', wait=True, **popen_kws):
+    def __call__(
+            # pylint: disable=too-many-branches,too-many-statements
+            # pylint: disable=too-many-arguments,too-many-locals
+            self, action=None, try_app_first=False,
+            app='default', files=None, mode=0,
+            flags='', wait=True, **popen_kws):
         """Run the application in the way specified by the options.
 
         Returns False if nothing can be done, None if there was an error,
@@ -220,9 +225,9 @@ class Runner(object):
                     Popen_forked(**popen_kws)
                 else:
                     process = Popen(**popen_kws)
-            except Exception as e:
-                error = e
-                self._log("Failed to run: %s\n%s" % (str(action), str(e)))
+            except Exception as ex:
+                error = ex
+                self._log("Failed to run: %s\n%s" % (str(action), str(ex)))
             else:
                 if context.wait:
                     process.wait()
@@ -238,6 +243,6 @@ class Runner(object):
             if toggle_ui:
                 self._activate_ui(True)
             if pipe_output and process:
-                return self(action='less', app='pager', try_app_first=True,
-                            stdin=process.stdout)
-            return process
+                return self(action='less', app='pager',  # pylint: disable=lost-exception
+                            try_app_first=True, stdin=process.stdout)
+            return process  # pylint: disable=lost-exception
diff --git a/ranger/core/shared.py b/ranger/core/shared.py
index 38b0d35a..764c2384 100644
--- a/ranger/core/shared.py
+++ b/ranger/core/shared.py
@@ -3,17 +3,17 @@
 
 """Shared objects contain singletons for shared use."""
 
-from ranger.ext.lazy_property import lazy_property
+from ranger.ext.lazy_property import lazy_property  # NOQA pylint: disable=unused-import
 
 
-class FileManagerAware(object):
+class FileManagerAware(object):  # pylint: disable=too-few-public-methods
     """Subclass this to gain access to the global "FM" object."""
     @staticmethod
     def _setup(fm):
         FileManagerAware.fm = fm
 
 
-class SettingsAware(object):
+class SettingsAware(object):  # pylint: disable=too-few-public-methods
     """Subclass this to gain access to the global "SettingObject" object."""
     @staticmethod
     def _setup(settings):
diff --git a/ranger/core/tab.py b/ranger/core/tab.py
index 2a3bfeaf..56348111 100644
--- a/ranger/core/tab.py
+++ b/ranger/core/tab.py
@@ -2,16 +2,15 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
 import os
-import sys
 from os.path import abspath, normpath, join, expanduser, isdir
+import sys
 
 from ranger.container import settings
 from ranger.container.history import History
 from ranger.core.shared import FileManagerAware, SettingsAware
-from ranger.ext.signals import SignalDispatcher
 
 
-class Tab(FileManagerAware, SettingsAware):
+class Tab(FileManagerAware, SettingsAware):  # pylint: disable=too-many-instance-attributes
 
     def __init__(self, path):
         self.thisdir = None  # Current Working Directory
@@ -66,7 +65,7 @@ class Tab(FileManagerAware, SettingsAware):
                 return None
         else:
             directory = self.thisdir
-            for i in range(level):
+            for _ in range(level):
                 if directory is None:
                     return None
                 if directory.is_directory:
@@ -83,7 +82,7 @@ class Tab(FileManagerAware, SettingsAware):
                 return [self._thisfile]
         return []
 
-    def assign_cursor_positions_for_subdirs(self):
+    def assign_cursor_positions_for_subdirs(self):  # pylint: disable=invalid-name
         """Assign correct cursor positions for subdirectories"""
         last_path = None
         for path in reversed(self.pathway):
@@ -143,8 +142,8 @@ class Tab(FileManagerAware, SettingsAware):
         else:
             pathway = []
             currentpath = '/'
-            for dir in path.split('/'):
-                currentpath = join(currentpath, dir)
+            for comp in path.split('/'):
+                currentpath = join(currentpath, comp)
                 pathway.append(self.fm.get_directory(currentpath))
             self.pathway = tuple(pathway)
 
diff --git a/ranger/ext/accumulator.py b/ranger/ext/accumulator.py
index 9cc2f4e3..dcbfb4d9 100644
--- a/ranger/ext/accumulator.py
+++ b/ranger/ext/accumulator.py
@@ -89,10 +89,12 @@ class Accumulator(object):
     def sync_index(self, **kw):
         self.move_to_obj(self.pointed_obj, **kw)
 
-    def get_list(self):
+    @staticmethod
+    def get_list():
         """OVERRIDE THIS"""
         return []
 
-    def get_height(self):
+    @staticmethod
+    def get_height():
         """OVERRIDE THIS"""
         return 25
diff --git a/ranger/ext/cached_function.py b/ranger/ext/cached_function.py
index 6c1c7769..3c26d4f9 100644
--- a/ranger/ext/cached_function.py
+++ b/ranger/ext/cached_function.py
@@ -12,5 +12,5 @@ def cached_function(fnc):
             value = fnc(*args)
             cache[args] = value
             return value
-    inner_cached_function._cache = cache
+    inner_cached_function._cache = cache  # pylint: disable=protected-access
     return inner_cached_function
diff --git a/ranger/ext/curses_interrupt_handler.py b/ranger/ext/curses_interrupt_handler.py
index 606cbd62..d220d99c 100644
--- a/ranger/ext/curses_interrupt_handler.py
+++ b/ranger/ext/curses_interrupt_handler.py
@@ -11,12 +11,12 @@ a Ctrl+C (ASCII value 3) to the curses getch stack.
 import curses
 import signal
 
-_do_catch_interrupt = True
+_do_catch_interrupt = True  # pylint: disable=invalid-name
 
 
 def catch_interrupt(boolean=True):
     """Should interrupts be caught and simulate a ^C press in curses?"""
-    global _do_catch_interrupt
+    global _do_catch_interrupt  # pylint: disable=global-statement,invalid-name
     old_value = _do_catch_interrupt
     _do_catch_interrupt = bool(boolean)
     return old_value
@@ -24,15 +24,14 @@ def catch_interrupt(boolean=True):
 # The handler which will be used in signal.signal()
 
 
-def _interrupt_handler(a1, a2):
-    global _do_catch_interrupt
+def _interrupt_handler(signum, frame):
     # if a keyboard-interrupt occurs...
     if _do_catch_interrupt:
         # push a Ctrl+C (ascii value 3) to the curses getch stack
         curses.ungetch(3)
     else:
         # use the default handler
-        signal.default_int_handler(a1, a2)
+        signal.default_int_handler(signum, frame)
 
 
 def install_interrupt_handler():
diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py
index b5fea6aa..3de7e1c3 100644
--- a/ranger/ext/direction.py
+++ b/ranger/ext/direction.py
@@ -51,7 +51,7 @@ class Direction(dict):
             except Exception:
                 return fallback
 
-    def up(self):
+    def up(self):  # pylint: disable=invalid-name
         return -Direction.down(self)
 
     def down(self):
@@ -104,8 +104,8 @@ class Direction(dict):
             if key in self:
                 self[key] = n
 
-    def move(self, direction, override=None, minimum=0, maximum=9999,
-             current=0, pagesize=1, offset=0):
+    def move(self, direction, override=None, minimum=0,  # pylint: disable=too-many-arguments
+             maximum=9999, current=0, pagesize=1, offset=0):
         """Calculates the new position in a given boundary.
 
         Example:
@@ -147,6 +147,7 @@ class Direction(dict):
         selection = lst[min(current, dest):max(current, dest) + offset]
         return dest + offset - 1, selection
 
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod()
diff --git a/ranger/ext/get_executables.py b/ranger/ext/get_executables.py
index f2a31345..385ec45d 100644
--- a/ranger/ext/get_executables.py
+++ b/ranger/ext/get_executables.py
@@ -2,17 +2,18 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
 from stat import S_IXOTH, S_IFREG
-from ranger.ext.iter_tools import unique
 from os import listdir, environ, stat
 import shlex
 
+from ranger.ext.iter_tools import unique
+
 
-_cached_executables = None
+_cached_executables = None  # pylint: disable=invalid-name
 
 
 def get_executables():
     """Return all executable files in $PATH. Cached version."""
-    global _cached_executables
+    global _cached_executables  # pylint: disable=global-statement,invalid-name
     if _cached_executables is None:
         _cached_executables = get_executables_uncached()
     return _cached_executables
diff --git a/ranger/ext/human_readable.py b/ranger/ext/human_readable.py
index 50b95b99..e4b04525 100644
--- a/ranger/ext/human_readable.py
+++ b/ranger/ext/human_readable.py
@@ -2,7 +2,7 @@
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
 
-def human_readable(byte, separator=' '):
+def human_readable(byte, separator=' '):  # pylint: disable=too-many-return-statements
     """Convert a large number of bytes to an easily readable format.
 
     >>> human_readable(54)
@@ -42,6 +42,7 @@ def human_readable(byte, separator=' '):
         return '%.4g%sP' % (byte / 2**50.0, separator)
     return '>9000'
 
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod()
diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py
index 5b2632da..c40c084a 100644
--- a/ranger/ext/img_display.py
+++ b/ranger/ext/img_display.py
@@ -15,12 +15,13 @@ import errno
 import fcntl
 import imghdr
 import os
-import select
 import struct
 import sys
+from subprocess import Popen, PIPE
+
 import termios
+
 from ranger.core.shared import FileManagerAware
-from subprocess import Popen, PIPE
 
 W3MIMGDISPLAY_ENV = "W3MIMGDISPLAY_PATH"
 W3MIMGDISPLAY_OPTIONS = []
@@ -70,7 +71,8 @@ class W3MImageDisplayer(ImageDisplayer):
                              stdin=PIPE, stdout=PIPE, universal_newlines=True)
         self.is_initialized = True
 
-    def _find_w3mimgdisplay_executable(self):
+    @staticmethod
+    def _find_w3mimgdisplay_executable():
         paths = [os.environ.get(W3MIMGDISPLAY_ENV, None)] + W3MIMGDISPLAY_PATHS
         for path in paths:
             if path is not None and os.path.exists(path):
@@ -84,10 +86,10 @@ class W3MImageDisplayer(ImageDisplayer):
         # pixels.
         if self.binary_path is None:
             self.binary_path = self._find_w3mimgdisplay_executable()
-        s = struct.pack("HHHH", 0, 0, 0, 0)
+        farg = struct.pack("HHHH", 0, 0, 0, 0)
         fd_stdout = sys.stdout.fileno()
-        x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
-        rows, cols, xpixels, ypixels = struct.unpack("HHHH", x)
+        fretint = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, farg)
+        rows, cols, xpixels, ypixels = struct.unpack("HHHH", fretint)
         if xpixels == 0 and ypixels == 0:
             process = Popen([self.binary_path, "-test"],
                             stdout=PIPE, universal_newlines=True)
@@ -124,11 +126,11 @@ class W3MImageDisplayer(ImageDisplayer):
 
         try:
             self.process.stdin.write(cmd)
-        except IOError as e:
-            if e.errno == errno.EPIPE:
+        except IOError as ex:
+            if ex.errno == errno.EPIPE:
                 return
             else:
-                raise e
+                raise ex
         self.process.stdin.flush()
         self.process.stdout.readline()
 
@@ -236,19 +238,20 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware):
                 min_scale = min(width_scale, height_scale)
                 max_scale = max(width_scale, height_scale)
                 if width * max_scale <= max_width and height * max_scale <= max_height:
-                    return (width * max_scale)
+                    return width * max_scale
                 else:
-                    return (width * min_scale)
+                    return width * min_scale
             else:
                 scale = max_height / float(height)
-                return (width * scale)
+                return width * scale
         elif width > max_width:
             scale = max_width / float(width)
-            return (width * scale)
+            return width * scale
         else:
             return width
 
-    def _encode_image_content(self, path):
+    @staticmethod
+    def _encode_image_content(path):
         """Read and encode the contents of path"""
         file = open(path, 'rb')
         try:
@@ -258,7 +261,8 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware):
         finally:
             file.close()
 
-    def _get_image_dimensions(self, path):
+    @staticmethod
+    def _get_image_dimensions(path):
         """Determine image size using imghdr"""
         file_handle = open(path, 'rb')
         file_header = file_handle.read(24)
@@ -306,17 +310,19 @@ class URXVTImageDisplayer(ImageDisplayer, FileManagerAware):
 
     """
 
-    def _get_max_sizes(self):
+    @staticmethod
+    def _get_max_sizes():
         """Use the whole terminal."""
-        w = 100
-        h = 100
-        return w, h
+        pct_width = 100
+        pct_height = 100
+        return pct_width, pct_height
 
-    def _get_centered_offsets(self):
+    @staticmethod
+    def _get_centered_offsets():
         """Center the image."""
-        x = 50
-        y = 50
-        return x, y
+        pct_x = 50
+        pct_y = 50
+        return pct_x, pct_y
 
     def _get_sizes(self):
         """Return the width and height of the preview pane in relation to the
@@ -328,18 +334,18 @@ class URXVTImageDisplayer(ImageDisplayer, FileManagerAware):
 
         total_columns_ratio = sum(self.fm.settings.column_ratios)
         preview_column_ratio = self.fm.settings.column_ratios[-1]
-        w = int((100 * preview_column_ratio) / total_columns_ratio)
-        h = 100  # As much as possible while preserving the aspect ratio.
-        return w, h
+        pct_width = int((100 * preview_column_ratio) / total_columns_ratio)
+        pct_height = 100  # As much as possible while preserving the aspect ratio.
+        return pct_width, pct_height
 
     def _get_offsets(self):
         """Return the offsets of the image center."""
         if self.fm.ui.pager.visible:
             return self._get_centered_offsets()
 
-        x = 100  # Right-aligned.
-        y = 2    # TODO: Use the font size to calculate this offset.
-        return x, y
+        pct_x = 100  # Right-aligned.
+        pct_y = 2    # TODO: Use the font size to calculate this offset.
+        return pct_x, pct_y
 
     def draw(self, path, start_x, start_y, width, height):
         # The coordinates in the arguments are ignored as urxvt takes
@@ -347,10 +353,15 @@ class URXVTImageDisplayer(ImageDisplayer, FileManagerAware):
         # image center as a percentage of the terminal size. As a
         # result all values below are in percents.
 
-        x, y = self._get_offsets()
-        w, h = self._get_sizes()
+        pct_x, pct_y = self._get_offsets()
+        pct_width, pct_height = self._get_sizes()
 
-        sys.stdout.write("\033]20;{path};{w}x{h}+{x}+{y}:op=keep-aspect\a".format(**vars()))
+        sys.stdout.write(
+            "\033]20;{path};{pct_width}x{pct_height}+{pct_x}+{pct_y}:op=keep-aspect\a".format(
+                path=path, pct_width=pct_width, pct_height=pct_height,
+                pct_x=pct_x, pct_y=pct_y,
+            )
+        )
         sys.stdout.flush()
 
     def clear(self, start_x, start_y, width, height):
diff --git a/ranger/ext/iter_tools.py b/ranger/ext/iter_tools.py
index 838d8aff..a18276b9 100644
--- a/ranger/ext/iter_tools.py
+++ b/ranger/ext/iter_tools.py
@@ -42,6 +42,7 @@ def unique(iterable):
             already_seen.append(item)
     return type(iterable)(already_seen)
 
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod()
diff --git a/ranger/ext/keybinding_parser.py b/ranger/ext/keybinding_parser.py
index 441949f4..ec9d8034 100644
--- a/ranger/ext/keybinding_parser.py
+++ b/ranger/ext/keybinding_parser.py
@@ -6,12 +6,12 @@ import copy
 import curses.ascii
 
 PY3 = sys.version_info[0] >= 3
-digits = set(range(ord('0'), ord('9') + 1))
+digits = set(range(ord('0'), ord('9') + 1))  # pylint: disable=invalid-name
 
 # Arbitrary numbers which are not used with curses.KEY_XYZ
 ANYKEY, PASSIVE_ACTION, ALT_KEY, QUANT_KEY = range(9001, 9005)
 
-special_keys = {
+special_keys = {  # pylint: disable=invalid-name
     'bs': curses.KEY_BACKSPACE,
     'backspace': curses.KEY_BACKSPACE,
     'backspace2': curses.ascii.DEL,
@@ -38,33 +38,39 @@ special_keys = {
     'gt': ord('>'),
 }
 
-very_special_keys = {
+very_special_keys = {  # pylint: disable=invalid-name
     'any': ANYKEY,
     'alt': ALT_KEY,
     'bg': PASSIVE_ACTION,
     'allow_quantifiers': QUANT_KEY,
 }
 
-for key, val in tuple(special_keys.items()):
-    special_keys['a-' + key] = (ALT_KEY, val)
 
-for char in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!{}':
-    special_keys['a-' + char] = (ALT_KEY, ord(char))
+def special_keys_init():
+    for key, val in tuple(special_keys.items()):
+        special_keys['a-' + key] = (ALT_KEY, val)
 
-for char in 'abcdefghijklmnopqrstuvwxyz_':
-    special_keys['c-' + char] = ord(char) - 96
+    for char in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!{}':
+        special_keys['a-' + char] = (ALT_KEY, ord(char))
 
-special_keys['c-space'] = 0
+    for char in 'abcdefghijklmnopqrstuvwxyz_':
+        special_keys['c-' + char] = ord(char) - 96
 
-for n in range(64):
-    special_keys['f' + str(n)] = curses.KEY_F0 + n
+    special_keys['c-space'] = 0
+
+    for n in range(64):
+        special_keys['f' + str(n)] = curses.KEY_F0 + n
+
+
+special_keys_init()
 
 special_keys.update(very_special_keys)
 del very_special_keys
-reversed_special_keys = dict((v, k) for k, v in special_keys.items())
+reversed_special_keys = dict(  # pylint: disable=invalid-name
+    (v, k) for k, v in special_keys.items())
 
 
-def parse_keybinding(obj):
+def parse_keybinding(obj):  # pylint: disable=too-many-branches
     """Translate a keybinding to a sequence of integers
 
     >>> tuple(parse_keybinding("lol<CR>"))
@@ -86,9 +92,9 @@ def parse_keybinding(obj):
             yield char
     elif isinstance(obj, int):
         yield obj
-    elif isinstance(obj, str):
+    elif isinstance(obj, str):  # pylint: disable=too-many-nested-blocks
         in_brackets = False
-        bracket_content = None
+        bracket_content = []
         for char in obj:
             if in_brackets:
                 if char == '>':
@@ -103,8 +109,8 @@ def parse_keybinding(obj):
                             yield int(string)
                         else:
                             yield ord('<')
-                            for c in bracket_content:
-                                yield ord(c)
+                            for char in bracket_content:
+                                yield ord(char)
                             yield ord('>')
                     except TypeError:
                         yield keys  # it was no tuple, just an int
@@ -118,8 +124,8 @@ def parse_keybinding(obj):
                     yield ord(char)
         if in_brackets:
             yield ord('<')
-            for c in bracket_content:
-                yield ord(c)
+            for char in bracket_content:
+                yield ord(char)
 
 
 def construct_keybinding(iterable):
@@ -206,7 +212,7 @@ class KeyMaps(dict):
         _unbind_traverse(pointer, keys)
 
 
-class KeyBuffer(object):
+class KeyBuffer(object):  # pylint: disable=too-many-instance-attributes
     any_key = ANYKEY
     passive_key = PASSIVE_ACTION
     quantifier_key = QUANT_KEY
@@ -214,9 +220,6 @@ class KeyBuffer(object):
 
     def __init__(self, keymap=None):
         self.keymap = keymap
-        self.clear()
-
-    def clear(self):
         self.keys = []
         self.wildcards = []
         self.pointer = self.keymap
@@ -230,6 +233,9 @@ class KeyBuffer(object):
             if self.keymap[self.quantifier_key] == 'false':
                 self.finished_parsing_quantifier = True
 
+    def clear(self):
+        self.__init__(self.keymap)
+
     def add(self, key):
         self.keys.append(key)
         self.result = None
@@ -264,6 +270,7 @@ class KeyBuffer(object):
     def __str__(self):
         return "".join(key_to_string(c) for c in self.keys)
 
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod()
diff --git a/ranger/ext/lazy_property.py b/ranger/ext/lazy_property.py
index 4bdcda1e..43dc65af 100644
--- a/ranger/ext/lazy_property.py
+++ b/ranger/ext/lazy_property.py
@@ -1,7 +1,7 @@
 # From http://blog.pythonisito.com/2008/08/lazy-descriptors.html
 
 
-class lazy_property(object):
+class lazy_property(object):  # pylint: disable=invalid-name,too-few-public-methods
     """A @property-like decorator with lazy evaluation
 
     >>> class Foo:
@@ -29,6 +29,7 @@ class lazy_property(object):
         obj.__dict__[self.__name__] = result
         return result
 
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod()
diff --git a/ranger/ext/logutils.py b/ranger/ext/logutils.py
index 0de6c333..c8d887d0 100644
--- a/ranger/ext/logutils.py
+++ b/ranger/ext/logutils.py
@@ -28,9 +28,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
 
 
 def setup_logging(debug=True, logfile=None):
diff --git a/ranger/ext/popen_forked.py b/ranger/ext/popen_forked.py
index d4a75a48..82997f0c 100644
--- a/ranger/ext/popen_forked.py
+++ b/ranger/ext/popen_forked.py
@@ -5,7 +5,7 @@ import os
 import subprocess
 
 
-def Popen_forked(*args, **kwargs):
+def Popen_forked(*args, **kwargs):  # pylint: disable=invalid-name
     """Forks process and runs Popen with the given args and kwargs.
 
     Returns True if forking succeeded, otherwise False.
@@ -19,7 +19,7 @@ def Popen_forked(*args, **kwargs):
         kwargs['stdin'] = open(os.devnull, 'r')
         kwargs['stdout'] = kwargs['stderr'] = open(os.devnull, 'w')
         subprocess.Popen(*args, **kwargs)
-        os._exit(0)
+        os._exit(0)  # pylint: disable=protected-access
     else:
         os.wait()
     return True
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index 2577f468..842f289e 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -32,11 +32,11 @@ ENCODING = 'utf-8'
 try:
     from ranger.ext.get_executables import get_executables
 except ImportError:
-    _cached_executables = None
+    _cached_executables = None  # pylint: disable=invalid-name
 
     def get_executables():
         """Return all executable files in $PATH + Cache them."""
-        global _cached_executables
+        global _cached_executables  # pylint: disable=global-statement,invalid-name
         if _cached_executables is not None:
             return _cached_executables
 
@@ -70,7 +70,7 @@ except ImportError:
 try:
     from ranger.ext.popen_forked import Popen_forked
 except ImportError:
-    def Popen_forked(*args, **kwargs):
+    def Popen_forked(*args, **kwargs):  # pylint: disable=invalid-name
         """Forks process and runs Popen with the given args and kwargs."""
         try:
             pid = os.fork()
@@ -81,7 +81,7 @@ except ImportError:
             kwargs['stdin'] = open(os.devnull, 'r')
             kwargs['stdout'] = kwargs['stderr'] = open(os.devnull, 'w')
             Popen(*args, **kwargs)
-            os._exit(0)
+            os._exit(0)  # pylint: disable=protected-access
         return True
 
 
@@ -111,7 +111,7 @@ def squash_flags(flags):
     return ''.join(f for f in flags if f not in exclude)
 
 
-class Rifle(object):
+class Rifle(object):  # pylint: disable=too-many-instance-attributes
     delimiter1 = '='
     delimiter2 = ','
 
@@ -122,16 +122,20 @@ class Rifle(object):
     def hook_after_executing(self, command, mimetype, flags):
         pass
 
-    def hook_command_preprocessing(self, command):
+    @staticmethod
+    def hook_command_preprocessing(command):
         return command
 
-    def hook_command_postprocessing(self, command):
+    @staticmethod
+    def hook_command_postprocessing(command):
         return command
 
-    def hook_environment(self, env):
+    @staticmethod
+    def hook_environment(env):
         return env
 
-    def hook_logger(self, string):
+    @staticmethod
+    def hook_logger(string):
         sys.stderr.write(string + "\n")
 
     def __init__(self, config_file):
@@ -139,6 +143,9 @@ class Rifle(object):
         self._app_flags = ''
         self._app_label = None
         self._initialized_mimetypes = False
+        self._mimetype = None
+        self._skip = None
+        self.rules = None
 
         # get paths for mimetype files
         self._mimetype_known_files = [
@@ -152,10 +159,10 @@ class Rifle(object):
         """Replace the current configuration with the one in config_file"""
         if config_file is None:
             config_file = self.config_file
-        f = open(config_file, 'r')
+        fobj = open(config_file, 'r')
         self.rules = []
         lineno = 0
-        for line in f:
+        for line in fobj:
             lineno += 1
             line = line.strip()
             if line.startswith('#') or line == '':
@@ -168,10 +175,10 @@ class Rifle(object):
                 tests = tuple(tuple(f.strip().split(None, 1)) for f in tests)
                 command = command.strip()
                 self.rules.append((command, tests))
-            except Exception as e:
+            except Exception as ex:
                 self.hook_logger("Syntax error in %s line %d (%s)" %
-                                 (config_file, lineno, str(e)))
-        f.close()
+                                 (config_file, lineno, str(ex)))
+        fobj.close()
 
     def _eval_condition(self, condition, files, label):
         # Handle the negation of conditions starting with an exclamation mark,
@@ -184,7 +191,8 @@ class Rifle(object):
             return not self._eval_condition2(new_condition, files, label)
         return self._eval_condition2(condition, files, label)
 
-    def _eval_condition2(self, rule, files, label):
+    def _eval_condition2(  # pylint: disable=too-many-return-statements,too-many-branches
+            self, rule, files, label):
         # This function evaluates the condition, after _eval_condition() handled
         # negation of conditions starting with a "!".
 
@@ -249,7 +257,7 @@ class Rifle(object):
         for path in self._mimetype_known_files:
             if path not in mimetypes.knownfiles:
                 mimetypes.knownfiles.append(path)
-        self._mimetype, encoding = mimetypes.guess_type(fname)
+        self._mimetype, _ = mimetypes.guess_type(fname)
 
         if not self._mimetype:
             process = Popen(["file", "--mime-type", "-Lb", fname],
@@ -292,7 +300,8 @@ class Rifle(object):
                     count = self._skip
                 yield (count, cmd, self._app_label, self._app_flags)
 
-    def execute(self, files, number=0, label=None, flags="", mimetype=None):
+    def execute(self, files,  # pylint: disable=too-many-branches,too-many-statements
+                number=0, label=None, flags="", mimetype=None):
         """Executes the given list of files.
 
         By default, this executes the first command where all conditions apply,
@@ -327,7 +336,7 @@ class Rifle(object):
                 command = self._build_command(files, cmd, flags)
 
         # Execute command
-        if command is None:
+        if command is None:  # pylint: disable=too-many-nested-blocks
             if found_at_least_one:
                 if label:
                     self.hook_logger("Label '%s' is undefined" % label)
@@ -370,15 +379,14 @@ class Rifle(object):
                 if 'f' in flags or 't' in flags:
                     Popen_forked(cmd, env=self.hook_environment(os.environ))
                 else:
-                    p = Popen(cmd, env=self.hook_environment(os.environ))
-                    p.wait()
+                    process = Popen(cmd, env=self.hook_environment(os.environ))
+                    process.wait()
             finally:
                 self.hook_after_executing(command, self._mimetype, self._app_flags)
 
 
-def main():
+def main():  # pylint: disable=too-many-locals
     """The main function which is run when you start this program direectly."""
-    import sys
 
     # Find configuration file path
     if 'XDG_CONFIG_HOME' in os.environ and os.environ['XDG_CONFIG_HOME']:
@@ -399,7 +407,7 @@ def main():
             conf_path = os.path.join(ranger.__path__[0], "config", "rifle.conf")
 
     # Evaluate arguments
-    from optparse import OptionParser
+    from optparse import OptionParser  # pylint: disable=deprecated-module
     parser = OptionParser(usage="%prog [-fhlpw] [files]", version=__version__)
     parser.add_option('-f', type="string", default="", metavar="FLAGS",
                       help="use additional flags: f=fork, r=root, t=terminal. "
@@ -430,8 +438,8 @@ def main():
         label = options.p
 
     if options.w is not None and not options.l:
-        p = Popen([options.w] + list(positional))
-        p.wait()
+        process = Popen([options.w] + list(positional))
+        process.wait()
     else:
         # Start up rifle
         rifle = Rifle(conf_path)
@@ -445,7 +453,9 @@ def main():
                                    flags=options.f)
             if result == ASK_COMMAND:
                 # TODO: implement interactive asking for file type?
-                print("Unknown file type: %s" % rifle._get_mimetype(positional[0]))
+                print("Unknown file type: %s" %
+                      rifle._get_mimetype(positional[0]))  # pylint: disable=protected-access
+
 
 if __name__ == '__main__':
     if 'RANGER_DOCTEST' in os.environ:
diff --git a/ranger/ext/shell_escape.py b/ranger/ext/shell_escape.py
index 97401cd6..5cea1ec5 100644
--- a/ranger/ext/shell_escape.py
+++ b/ranger/ext/shell_escape.py
@@ -21,6 +21,6 @@ def shell_escape(arg):
     if UNESCAPABLE & set(arg):
         return shell_quote(arg)
     arg = arg.replace('\\', '\\\\')  # make sure this comes at the start
-    for k, v in META_DICT.items():
-        arg = arg.replace(k, v)
+    for key, value in META_DICT.items():
+        arg = arg.replace(key, value)
     return arg
diff --git a/ranger/ext/shutil_generatorized.py b/ranger/ext/shutil_generatorized.py
index 8c5fc805..f00e992d 100644
--- a/ranger/ext/shutil_generatorized.py
+++ b/ranger/ext/shutil_generatorized.py
@@ -6,9 +6,9 @@ XXX The functions here don't copy the resource fork or other metadata on Mac.
 """
 
 import os
+from os.path import abspath
 import sys
 import stat
-from os.path import abspath
 
 __all__ = ["copyfileobj", "copyfile", "copystat", "copy2", "BLOCK_SIZE",
            "copytree", "move", "rmtree", "Error", "SpecialFileError"]
@@ -25,10 +25,11 @@ class SpecialFileError(EnvironmentError):
     """Raised when trying to do a kind of operation (e.g. copying) which is
     not supported on a special file (e.g. a named pipe)"""
 
+
 try:
     WindowsError
 except NameError:
-    WindowsError = None
+    WindowsError = None  # pylint: disable=invalid-name
 
 
 def copyfileobj(fsrc, fdst, length=BLOCK_SIZE):
@@ -63,16 +64,16 @@ def copyfile(src, dst):
 
     fsrc = None
     fdst = None
-    for fn in [src, dst]:
+    for path in [src, dst]:
         try:
-            st = os.stat(fn)
+            fstat = os.stat(path)
         except OSError:
             # File most likely does not exist
             pass
         else:
             # XXX What about other special files? (sockets, devices...)
-            if stat.S_ISFIFO(st.st_mode):
-                raise SpecialFileError("`%s` is a named pipe" % fn)
+            if stat.S_ISFIFO(fstat.st_mode):
+                raise SpecialFileError("`%s` is a named pipe" % path)
     try:
         fsrc = open(src, 'rb')
         fdst = open(dst, 'wb')
@@ -87,11 +88,11 @@ def copyfile(src, dst):
 
 def copystat(src, dst):
     """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
-    st = os.lstat(src)
-    mode = stat.S_IMODE(st.st_mode)
+    fstat = os.lstat(src)
+    mode = stat.S_IMODE(fstat.st_mode)
     if hasattr(os, 'utime'):
         try:
-            os.utime(dst, (st.st_atime, st.st_mtime))
+            os.utime(dst, (fstat.st_atime, fstat.st_mtime))
         except Exception:
             pass
     if hasattr(os, 'chmod'):
@@ -99,9 +100,9 @@ def copystat(src, dst):
             os.chmod(dst, mode)
         except Exception:
             pass
-    if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
+    if hasattr(os, 'chflags') and hasattr(fstat, 'st_flags'):
         try:
-            os.chflags(dst, st.st_flags)
+            os.chflags(dst, fstat.st_flags)  # pylint: disable=no-member
         except Exception:
             pass
 
@@ -143,7 +144,8 @@ def get_safe_path(dst):
     return test_dst
 
 
-def copytree(src, dst, symlinks=False, ignore=None, overwrite=False):
+def copytree(src, dst,  # pylint: disable=too-many-locals,too-many-branches
+             symlinks=False, ignore=None, overwrite=False):
     """Recursively copy a directory tree using copy2().
 
     The destination directory must not already exist.
@@ -196,18 +198,18 @@ def copytree(src, dst, symlinks=False, ignore=None, overwrite=False):
                 os.symlink(linkto, dstname)
                 copystat(srcname, dstname)
             elif os.path.isdir(srcname):
-                d = 0
-                for d in copytree(srcname, dstname, symlinks,
+                n = 0
+                for n in copytree(srcname, dstname, symlinks,
                                   ignore, overwrite):
-                    yield done + d
-                done += d
+                    yield done + n
+                done += n
             else:
                 # Will raise a SpecialFileError for unsupported file types
-                d = 0
-                for d in copy2(srcname, dstname,
+                n = 0
+                for n in copy2(srcname, dstname,
                                overwrite=overwrite, symlinks=symlinks):
-                    yield done + d
-                done += d
+                    yield done + n
+                done += n
         # catch the Error from the recursive copytree so that we can
         # continue with other files
         except Error as err:
@@ -238,11 +240,11 @@ def rmtree(path, ignore_errors=False, onerror=None):
 
     """
     if ignore_errors:
-        def onerror(*args):
+        def onerror(*_):  # pylint: disable=function-redefined
             pass
     elif onerror is None:
-        def onerror(*args):
-            raise
+        def onerror(*_):  # pylint: disable=function-redefined
+            raise  # pylint: disable=misplaced-bare-raise
     try:
         if os.path.islink(path):
             # symlinks to directories are forbidden, see bug #1669
@@ -254,7 +256,7 @@ def rmtree(path, ignore_errors=False, onerror=None):
     names = []
     try:
         names = os.listdir(path)
-    except os.error as err:
+    except os.error:
         onerror(os.listdir, path, sys.exc_info())
     for name in names:
         fullname = os.path.join(path, name)
@@ -267,7 +269,7 @@ def rmtree(path, ignore_errors=False, onerror=None):
         else:
             try:
                 os.remove(fullname)
-            except os.error as err:
+            except os.error:
                 onerror(os.remove, fullname, sys.exc_info())
     try:
         os.rmdir(path)
diff --git a/ranger/ext/signals.py b/ranger/ext/signals.py
index e35eab57..3800a6ea 100644
--- a/ranger/ext/signals.py
+++ b/ranger/ext/signals.py
@@ -82,7 +82,7 @@ class Signal(dict):
         self.stopped = True
 
 
-class SignalHandler:
+class SignalHandler:  # pylint: disable=too-few-public-methods
     """Signal Handlers contain information about a signal binding.
 
     They are returned by signal_bind() and have to be passed to signal_unbind()
@@ -110,7 +110,7 @@ class SignalDispatcher(object):
         """Remove all signals."""
         for handler_list in self._signals.values():
             for handler in handler_list:
-                handler._function = None
+                handler._function = None  # pylint: disable=protected-access
         self._signals = dict()
 
     def signal_bind(self, signal_name, function, priority=0.5, weak=False, autosort=True):
@@ -149,7 +149,8 @@ class SignalDispatcher(object):
         handler = SignalHandler(signal_name, function, priority, nargs > 0)
         handlers.append(handler)
         if autosort:
-            handlers.sort(key=lambda handler: -handler._priority)
+            handlers.sort(
+                key=lambda handler: -handler._priority)  # pylint: disable=protected-access
         return handler
 
     def signal_force_sort(self, signal_name=None):
@@ -160,9 +161,11 @@ class SignalDispatcher(object):
         """
         if signal_name is None:
             for handlers in self._signals.values():
-                handlers.sort(key=lambda handler: -handler._priority)
+                handlers.sort(
+                    key=lambda handler: -handler._priority)  # pylint: disable=protected-access
         elif signal_name in self._signals:
-            self._signals[signal_name].sort(key=lambda handler: -handler._priority)
+            self._signals[signal_name].sort(
+                key=lambda handler: -handler._priority)  # pylint: disable=protected-access
         else:
             return False
 
@@ -173,12 +176,13 @@ class SignalDispatcher(object):
         signal_bind().
         """
         try:
-            handlers = self._signals[signal_handler._signal_name]
+            handlers = self._signals[
+                signal_handler._signal_name]  # pylint: disable=protected-access
         except Exception:
             pass
         else:
             try:
-                signal_handler._function = None
+                signal_handler._function = None  # pylint: disable=protected-access
                 handlers.remove(signal_handler)
             except Exception:
                 pass
@@ -220,14 +224,16 @@ class SignalDispatcher(object):
             while i:
                 i -= 1
                 handler = handler_list[i]
+                # pylint: disable=protected-access
                 try:
                     if isinstance(handler._function, tuple):
-                        handler._function[1].__class__
+                        handler._function[1].__class__  # pylint: disable=pointless-statement
                     else:
-                        handler._function.__class__
+                        handler._function.__class__  # pylint: disable=pointless-statement
                 except ReferenceError:
                     handler._function = None
                     del handler_list[i]
+                # pylint: enable=protected-access
 
     def signal_emit(self, signal_name, **kw):
         """Emits a signal and call every function that was bound to that signal.
@@ -251,6 +257,7 @@ class SignalDispatcher(object):
         # propagate
         for handler in tuple(handlers):
             if handler.active:
+                # pylint: disable=protected-access
                 try:
                     if isinstance(handler._function, tuple):
                         fnc = MethodType(*handler._function)
@@ -263,6 +270,7 @@ class SignalDispatcher(object):
                 except ReferenceError:
                     handler._function = None
                     handlers.remove(handler)
+                # pylint: enable=protected-access
                 if signal.stopped:
                     return False
         return True
diff --git a/ranger/ext/widestring.py b/ranger/ext/widestring.py
index 9d7b9d03..603439cb 100644
--- a/ranger/ext/widestring.py
+++ b/ranger/ext/widestring.py
@@ -32,9 +32,9 @@ def string_to_charlist(string):
         return list(string)
     result = []
     if PY3:
-        for c in string:
-            result.append(c)
-            if east_asian_width(c) in WIDE_SYMBOLS:
+        for char in string:
+            result.append(char)
+            if east_asian_width(char) in WIDE_SYMBOLS:
                 result.append('')
     else:
         try:
@@ -45,14 +45,14 @@ def string_to_charlist(string):
             string = string.decode('utf-8', 'ignore')
         except UnicodeEncodeError:
             return []
-        for c in string:
-            result.append(c.encode('utf-8'))
-            if east_asian_width(c) in WIDE_SYMBOLS:
+        for char in string:
+            result.append(char.encode('utf-8'))
+            if east_asian_width(char) in WIDE_SYMBOLS:
                 result.append('')
     return result
 
 
-class WideString(object):
+class WideString(object):  # pylint: disable=too-few-public-methods
 
     def __init__(self, string, chars=None):
         try:
@@ -101,7 +101,7 @@ class WideString(object):
     def __repr__(self):
         return '<' + self.__class__.__name__ + " '" + self.string + "'>"
 
-    def __getslice__(self, a, z):
+    def __getslice__(self, start, stop):
         """
         >>> WideString("asdf")[1:3]
         <WideString 'sd'>
@@ -124,21 +124,21 @@ class WideString(object):
         >>> WideString("aモ")[0:1]
         <WideString 'a'>
         """
-        if z is None or z > len(self.chars):
-            z = len(self.chars)
-        if z < 0:
-            z = len(self.chars) + z
-        if z < 0:
+        if stop is None or stop > len(self.chars):
+            stop = len(self.chars)
+        if stop < 0:
+            stop = len(self.chars) + stop
+        if stop < 0:
             return WideString("")
-        if a is None or a < 0:
-            a = 0
-        if z < len(self.chars) and self.chars[z] == '':
-            if self.chars[a] == '':
-                return WideString(' ' + ''.join(self.chars[a:z - 1]) + ' ')
-            return WideString(''.join(self.chars[a:z - 1]) + ' ')
-        if self.chars[a] == '':
-            return WideString(' ' + ''.join(self.chars[a:z - 1]))
-        return WideString(''.join(self.chars[a:z]))
+        if start is None or start < 0:
+            start = 0
+        if stop < len(self.chars) and self.chars[stop] == '':
+            if self.chars[start] == '':
+                return WideString(' ' + ''.join(self.chars[start:stop - 1]) + ' ')
+            return WideString(''.join(self.chars[start:stop - 1]) + ' ')
+        if self.chars[start] == '':
+            return WideString(' ' + ''.join(self.chars[start:stop - 1]))
+        return WideString(''.join(self.chars[start:stop]))
 
     def __getitem__(self, i):
         """
diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py
index 24e378e2..184c04fb 100644
--- a/ranger/gui/ansi.py
+++ b/ranger/gui/ansi.py
@@ -4,12 +4,16 @@
 
 """A library to help to convert ANSI codes to curses instructions."""
 
-from ranger.gui import color
 import re
 
+from ranger.gui import color
+
+
+# pylint: disable=invalid-name
 ansi_re = re.compile('(\x1b' + r'\[\d*(?:;\d+)*?[a-zA-Z])')
 codesplit_re = re.compile(r'38;5;(\d+);|48;5;(\d+);|(\d*);')
 reset = '\x1b[0m'
+# pylint: enable=invalid-name
 
 
 def split_ansi_from_text(ansi_text):
@@ -19,7 +23,7 @@ def split_ansi_from_text(ansi_text):
 # githttp://en.wikipedia.org/wiki/ANSI_escape_code
 
 
-def text_with_fg_bg_attr(ansi_text):
+def text_with_fg_bg_attr(ansi_text):  # pylint: disable=too-many-branches,too-many-statements
     fg, bg, attr = -1, -1, 0
     for chunk in split_ansi_from_text(ansi_text):
         if chunk and chunk[0] == '\x1b':
@@ -164,6 +168,7 @@ def char_slice(ansi_text, start, length):
             break
     return ''.join(chunks)
 
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod()
diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py
index bac8adb5..91fdff24 100644
--- a/ranger/gui/bar.py
+++ b/ranger/gui/bar.py
@@ -1,8 +1,11 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
-from ranger.ext.widestring import WideString, utf_char_width
 import sys
+
+from ranger.ext.widestring import WideString, utf_char_width
+
+
 PY3 = sys.version_info[0] >= 3
 
 
@@ -88,13 +91,13 @@ class Bar(object):
 
 class BarSide(list):
 
-    def __init__(self, base_color_tag):
+    def __init__(self, base_color_tag):  # pylint: disable=super-init-not-called
         self.base_color_tag = base_color_tag
 
     def add(self, string, *lst, **kw):
-        cs = ColoredString(string, self.base_color_tag, *lst)
-        cs.__dict__.update(kw)
-        self.append(cs)
+        colorstr = ColoredString(string, self.base_color_tag, *lst)
+        colorstr.__dict__.update(kw)
+        self.append(colorstr)
 
     def add_space(self, n=1):
         self.add(' ' * n, 'space')
diff --git a/ranger/gui/color.py b/ranger/gui/color.py
index 62870ace..786299a3 100644
--- a/ranger/gui/color.py
+++ b/ranger/gui/color.py
@@ -46,6 +46,8 @@ def get_color(fg, bg):
 
     return COLOR_PAIRS[key]
 
+
+# pylint: disable=invalid-name
 black = curses.COLOR_BLACK
 blue = curses.COLOR_BLUE
 cyan = curses.COLOR_CYAN
@@ -64,3 +66,4 @@ underline = curses.A_UNDERLINE
 invisible = curses.A_INVIS
 
 default_colors = (default, default, normal)
+# pylint: enable=invalid-name
diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py
index 02333828..363d4da3 100644
--- a/ranger/gui/colorscheme.py
+++ b/ranger/gui/colorscheme.py
@@ -66,7 +66,8 @@ class ColorScheme(object):
         fg, bg, attr = self.get(*flatten(keys))
         return attr | color_pair(get_color(fg, bg))
 
-    def use(self, context):
+    @staticmethod
+    def use(_):
         """Use the colorscheme to determine the (fg, bg, attr) tuple.
 
         Override this method in your own colorscheme.
@@ -74,7 +75,7 @@ class ColorScheme(object):
         return (-1, -1, 0)
 
 
-def _colorscheme_name_to_class(signal):
+def _colorscheme_name_to_class(signal):  # pylint: disable=too-many-branches
     # Find the colorscheme.  First look in ~/.config/ranger/colorschemes,
     # then at RANGERDIR/colorschemes.  If the file contains a class
     # named Scheme, it is used.  Otherwise, an arbitrary other class
@@ -91,9 +92,9 @@ def _colorscheme_name_to_class(signal):
     def exists(colorscheme):
         return os.path.exists(colorscheme + '.py') or os.path.exists(colorscheme + '.pyc')
 
-    def is_scheme(x):
+    def is_scheme(cls):
         try:
-            return issubclass(x, ColorScheme)
+            return issubclass(cls, ColorScheme)
         except Exception:
             return False
 
@@ -117,7 +118,7 @@ def _colorscheme_name_to_class(signal):
         if signal.previous and isinstance(signal.previous, ColorScheme):
             signal.value = signal.previous
         else:
-            signal.value = ColorScheme()
+            signal.value = ColorScheme()  # pylint: disable=redefined-variable-type
         raise Exception("Cannot locate colorscheme `%s'" % scheme_name)
     else:
         if usecustom:
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index 13598b16..be118655 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -23,14 +23,19 @@ CONTEXT_KEYS = ['reset', 'error', 'badinfo',
                 'vcsstaged', 'vcssync', 'vcsnone', 'vcsbehind', 'vcsahead', 'vcsdiverged']
 
 
-class Context(object):
+class Context(object):  # pylint: disable=too-few-public-methods
 
     def __init__(self, keys):
         # set all given keys to True
-        d = self.__dict__
+        dictionary = self.__dict__
         for key in keys:
-            d[key] = True
+            dictionary[key] = True
 
-# set all keys to False
-for key in CONTEXT_KEYS:
-    setattr(Context, key, False)
+
+def _context_init():
+    # set all keys to False
+    for key in CONTEXT_KEYS:
+        setattr(Context, key, False)
+
+
+_context_init()
diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py
index ed762c9e..3213d097 100644
--- a/ranger/gui/curses_shortcuts.py
+++ b/ranger/gui/curses_shortcuts.py
@@ -1,9 +1,9 @@
 # This file is part of ranger, the console file manager.
 # License: GNU GPL version 3, see the file "AUTHORS" for details.
 
+import sys
 import curses
 import _curses
-import sys
 
 from ranger.gui.color import get_color
 from ranger.core.shared import SettingsAware
diff --git a/ranger/gui/displayable.py b/ranger/gui/displayable.py
index 4c5133d4..1eef8f41 100644
--- a/ranger/gui/displayable.py
+++ b/ranger/gui/displayable.py
@@ -5,7 +5,8 @@ from ranger.core.shared import FileManagerAware
 from ranger.gui.curses_shortcuts import CursesShortcuts
 
 
-class Displayable(FileManagerAware, CursesShortcuts):
+class Displayable(  # pylint: disable=too-many-instance-attributes
+        FileManagerAware, CursesShortcuts):
     """Displayables are objects which are displayed on the screen.
 
     This is just the abstract class, defining basic operations
@@ -163,7 +164,7 @@ class Displayable(FileManagerAware, CursesShortcuts):
         window_is_cleared = False
 
         if hei != self.hei or wid != self.wid:
-            #log("resizing " + str(self))
+            # log("resizing " + str(self))
             self.win.erase()
             self.need_redraw = True
             window_is_cleared = True
@@ -177,7 +178,7 @@ class Displayable(FileManagerAware, CursesShortcuts):
                     self.win.resize(hei, wid)
                 except Exception:
                     pass
-                    #raise ValueError("Resizing Failed!")
+                    # raise ValueError("Resizing Failed!")
 
             self.hei, self.wid = self.win.getmaxyx()
 
@@ -185,7 +186,7 @@ class Displayable(FileManagerAware, CursesShortcuts):
             if not window_is_cleared:
                 self.win.erase()
                 self.need_redraw = True
-            #log("moving " + str(self))
+            # log("moving " + str(self))
             try:
                 self.win.mvderwin(y, x)
             except Exception:
@@ -311,7 +312,7 @@ class DisplayableContainer(Displayable):
             if displayable.focused:
                 return displayable
             try:
-                obj = displayable._get_focused_obj()
+                obj = displayable._get_focused_obj()  # pylint: disable=protected-access
             except AttributeError:
                 pass
             else:
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index cb8e5718..90dee4c9 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -3,16 +3,18 @@
 
 import os
 import sys
+import threading
 import curses
 import _curses
-import threading
 
-from .displayable import DisplayableContainer
-from .mouse_event import MouseEvent
 from ranger.ext.keybinding_parser import KeyBuffer, KeyMaps, ALT_KEY
 from ranger.ext.lazy_property import lazy_property
 from ranger.ext.signals import Signal
 
+from .displayable import DisplayableContainer
+from .mouse_event import MouseEvent
+
+
 MOUSEMASK = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION
 
 _ASCII = ''.join(chr(c) for c in range(32, 127))
@@ -38,7 +40,8 @@ def _setup_mouse(signal):
         curses.mousemask(0)
 
 
-class UI(DisplayableContainer):
+class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-methods
+        DisplayableContainer):
     ALLOWED_VIEWMODES = 'miller', 'multipane'
 
     is_set_up = False
@@ -46,7 +49,7 @@ class UI(DisplayableContainer):
     is_on = False
     termsize = None
 
-    def __init__(self, env=None, fm=None):
+    def __init__(self, env=None, fm=None):  # pylint: disable=super-init-not-called
         self.keybuffer = KeyBuffer()
         self.keymaps = KeyMaps(self.keybuffer)
         self.redrawlock = threading.Event()
@@ -59,8 +62,8 @@ class UI(DisplayableContainer):
         os.environ['ESCDELAY'] = '25'   # don't know a cleaner way
         try:
             self.win = curses.initscr()
-        except _curses.error as e:
-            if e.args[0] == "setupterm: could not find terminal":
+        except _curses.error as ex:
+            if ex.args[0] == "setupterm: could not find terminal":
                 os.environ['TERM'] = 'linux'
                 self.win = curses.initscr()
         self.keymaps.use_keymap('browser')
@@ -198,7 +201,7 @@ class UI(DisplayableContainer):
             keys = [key]
             previous_load_mode = self.load_mode
             self.set_load_mode(True)
-            for n in range(4):
+            for _ in range(4):
                 getkey = self.win.getch()
                 if getkey is not -1:
                     keys.append(getkey)
@@ -231,7 +234,6 @@ class UI(DisplayableContainer):
 
     def setup(self):
         """Build up the UI by initializing widgets."""
-        from ranger.gui.widgets.view_miller import ViewMiller
         from ranger.gui.widgets.titlebar import TitleBar
         from ranger.gui.widgets.console import Console
         from ranger.gui.widgets.statusbar import StatusBar
@@ -455,7 +457,8 @@ class UI(DisplayableContainer):
 
     viewmode = property(_get_viewmode, _set_viewmode)
 
-    def _viewmode_to_class(self, viewmode):
+    @staticmethod
+    def _viewmode_to_class(viewmode):
         if viewmode == 'miller':
             from ranger.gui.widgets.view_miller import ViewMiller
             return ViewMiller
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 3fde7f03..28216dd5 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -3,21 +3,18 @@
 
 """The BrowserColumn widget displays the contents of a directory or file."""
 
-import curses
 import stat
 from time import time
 from os.path import splitext
 
-from . import Widget
-from .pager import Pager
 from ranger.ext.widestring import WideString
-
 from ranger.core import linemode
 
-from ranger.gui.color import *
+from . import Widget
+from .pager import Pager
 
 
-class BrowserColumn(Pager):
+class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
     main_column = False
     display_infostring = False
     display_vcsstate = True
@@ -39,7 +36,7 @@ class BrowserColumn(Pager):
         level <0 => parent directories
         """
         Pager.__init__(self, win)
-        Widget.__init__(self, win)
+        Widget.__init__(self, win)  # pylint: disable=non-parent-init-called
         self.level = level
         self.tab = tab
         self.original_level = level
@@ -182,14 +179,14 @@ class BrowserColumn(Pager):
             Pager.close(self)
             return
 
-        f = self.target.get_preview_source(self.wid, self.hei)
-        if f is None:
+        path = self.target.get_preview_source(self.wid, self.hei)
+        if path is None:
             Pager.close(self)
         else:
             if self.target.is_image_preview():
-                self.set_image(f)
+                self.set_image(path)
             else:
-                self.set_source(f)
+                self.set_source(path)
             Pager.draw(self)
 
     def _format_line_number(self, linum_format, i, selected_i):
@@ -201,7 +198,8 @@ class BrowserColumn(Pager):
 
         return linum_format.format(line_number)
 
-    def _draw_directory(self):
+    def _draw_directory(  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
+            self):
         """Draw the contents of a directory"""
         if self.image:
             self.image = None
@@ -273,7 +271,8 @@ class BrowserColumn(Pager):
 
             # Extract linemode-related information from the drawn object
             metadata = None
-            current_linemode = drawn.linemode_dict[drawn._linemode]
+            current_linemode = \
+                drawn.linemode_dict[drawn._linemode]  # pylint: disable=protected-access
             if current_linemode.uses_metadata:
                 metadata = self.fm.metadata.get_metadata(drawn.path)
                 if not all(getattr(metadata, tag)
@@ -390,7 +389,8 @@ class BrowserColumn(Pager):
         else:
             return self.target.pointer
 
-    def _total_len(self, predisplay):
+    @staticmethod
+    def _total_len(predisplay):
         return sum([len(WideString(s)) for s, L in predisplay])
 
     def _draw_text_display(self, text, space):
@@ -479,7 +479,7 @@ class BrowserColumn(Pager):
 
         return this_color
 
-    def _get_scroll_begin(self):
+    def _get_scroll_begin(self):  # pylint: disable=too-many-return-statements
         """Determines scroll_begin (the position of the first displayed file)"""
         offset = self.settings.scroll_offset
         dirsize = len(self.target)
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 85fb92a3..ecd98d9b 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -14,7 +14,7 @@ from ranger.container.history import History, HistoryEmptyException
 import ranger
 
 
-class Console(Widget):
+class Console(Widget):  # pylint: disable=too-many-instance-attributes,too-many-public-methods
     visible = False
     last_cursor_mode = None
     history_search_pattern = None
@@ -38,13 +38,13 @@ class Console(Widget):
         if not ranger.arg.clean:
             self.historypath = self.fm.confpath('history')
             try:
-                f = open(self.historypath, 'r')
+                fobj = open(self.historypath, 'r')
             except Exception:
                 pass
             else:
-                for line in f:
+                for line in fobj:
                     self.history.add(line[:-1])
-                f.close()
+                fobj.close()
         self.line = ""
         self.history_backup = History(self.history)
 
@@ -67,16 +67,16 @@ class Console(Widget):
             return
         if self.historypath:
             try:
-                f = open(self.historypath, 'w')
+                fobj = open(self.historypath, 'w')
             except Exception:
                 pass
             else:
                 for entry in self.history_backup:
                     try:
-                        f.write(entry + '\n')
+                        fobj.write(entry + '\n')
                     except UnicodeEncodeError:
                         pass
-                f.close()
+                fobj.close()
         Widget.destroy(self)
 
     def draw(self):
@@ -178,7 +178,7 @@ class Console(Widget):
         if not self.question_queue:
             return False
         question = self.question_queue[0]
-        text, callback, answers = question
+        _, callback, answers = question
         if answer in answers:
             self.question_queue.pop(0)
             callback(answer)
@@ -275,17 +275,17 @@ class Console(Widget):
                     current=self.pos)
             else:
                 if self.fm.py3:
-                    uc = list(self.line)
+                    uchar = list(self.line)
                     upos = len(self.line[:self.pos])
                 else:
-                    uc = list(self.line.decode('utf-8', 'ignore'))
+                    uchar = list(self.line.decode('utf-8', 'ignore'))
                     upos = len(self.line[:self.pos].decode('utf-8', 'ignore'))
                 newupos = direction.move(
                     direction=direction.right(),
                     minimum=0,
-                    maximum=len(uc) + 1,
+                    maximum=len(uchar) + 1,
                     current=upos)
-                self.pos = len(''.join(uc[:newupos]).encode('utf-8', 'ignore'))
+                self.pos = len(''.join(uchar[:newupos]).encode('utf-8', 'ignore'))
 
     def move_word(self, **keywords):
         direction = Direction(keywords)
@@ -409,11 +409,11 @@ class Console(Widget):
             self.pos = len(left_part)
             self.line = left_part + self.line[self.pos + 1:]
         else:
-            uc = list(self.line.decode('utf-8', 'ignore'))
+            uchar = list(self.line.decode('utf-8', 'ignore'))
             upos = len(self.line[:self.pos].decode('utf-8', 'ignore')) + mod
-            left_part = ''.join(uc[:upos]).encode('utf-8', 'ignore')
+            left_part = ''.join(uchar[:upos]).encode('utf-8', 'ignore')
             self.pos = len(left_part)
-            self.line = left_part + ''.join(uc[upos + 1:]).encode('utf-8', 'ignore')
+            self.line = left_part + ''.join(uchar[upos + 1:]).encode('utf-8', 'ignore')
         self.on_line_change()
 
     def execute(self, cmd=None):
@@ -494,7 +494,7 @@ class Console(Widget):
                 cmd.quickly_executed = True
                 self.execute(cmd)
 
-    def ask(self, text, callback, choices=['y', 'n']):
+    def ask(self, text, callback, choices=None):
         """Open a question prompt with predefined choices
 
         The "text" is displayed as the question text and should include a list
@@ -507,7 +507,9 @@ class Console(Widget):
         The first choice is used when the user presses <Enter>, the second
         choice is used when the user presses <ESC>.
         """
-        self.question_queue.append((text, callback, choices))
+        self.question_queue.append(
+            (text, callback, choices if choices is not None else ['y', 'n']))
+
 
 if __name__ == '__main__':
     import doctest
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index 979c6d62..fabf6113 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -3,16 +3,16 @@
 
 """The pager displays text and allows you to scroll inside it."""
 
-from . import Widget
-from ranger.core.loader import CommandLoader
 from ranger.gui import ansi
 from ranger.ext.direction import Direction
 from ranger.ext.img_display import ImgDisplayUnsupportedException
 
+from . import Widget
+
 # TODO: Scrolling in embedded pager
 
 
-class Pager(Widget):
+class Pager(Widget):  # pylint: disable=too-many-instance-attributes
     source = None
     source_is_stream = False
 
@@ -97,8 +97,8 @@ class Pager(Widget):
                                              self.wid, self.hei)
             except ImgDisplayUnsupportedException:
                 self.fm.settings.preview_images = False
-            except Exception as e:
-                self.fm.notify(e, bad=True)
+            except Exception as ex:
+                self.fm.notify(ex, bad=True)
             else:
                 self.image_drawn = True
 
@@ -190,7 +190,7 @@ class Pager(Widget):
         self.markup = 'ansi'
 
         if not self.source_is_stream and strip:
-            self.lines = map(lambda x: x.strip(), self.lines)
+            self.lines = [line.strip() for line in self.lines]
 
         self.source = source
         return True
@@ -209,10 +209,10 @@ class Pager(Widget):
         except (KeyError, IndexError):
             if attempt_to_read and self.source_is_stream:
                 try:
-                    for l in self.source:
-                        if len(l) > self.max_width:
-                            self.max_width = len(l)
-                        self.lines.append(l)
+                    for line in self.source:
+                        if len(line) > self.max_width:
+                            self.max_width = len(line)
+                        self.lines.append(line)
                         if len(self.lines) > n:
                             break
                 except (UnicodeError, IOError):
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index 87cd7c53..ead79e5d 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -9,17 +9,18 @@ such as the space used by all the files in this directory.
 """
 
 import os
+from os import getuid, readlink
 from pwd import getpwuid
 from grp import getgrgid
-from os import getuid, readlink
 from time import time, strftime, localtime
 
 from ranger.ext.human_readable import human_readable
-from . import Widget
 from ranger.gui.bar import Bar
 
+from . import Widget
+
 
-class StatusBar(Widget):
+class StatusBar(Widget):  # pylint: disable=too-many-instance-attributes
     __doc__ = __doc__
     owners = {}
     groups = {}
@@ -133,7 +134,7 @@ class StatusBar(Widget):
             space_left -= len(string)
             starting_point += len(string)
 
-    def _get_left_part(self, bar):
+    def _get_left_part(self, bar):  # pylint: disable=too-many-branches,too-many-statements
         left = bar.left
 
         if self.column is not None and self.column.target is not None\
@@ -230,7 +231,7 @@ class StatusBar(Widget):
             except KeyError:
                 return str(gid)
 
-    def _get_right_part(self, bar):
+    def _get_right_part(self, bar):  # pylint: disable=too-many-branches
         right = bar.right
         if self.column is None:
             return
@@ -261,8 +262,11 @@ class StatusBar(Widget):
             if len(target.marked_items) == target.size:
                 right.add(human_readable(target.disk_usage, separator=''))
             else:
-                sumsize = sum(f.size for f in target.marked_items if not
-                              f.is_directory or f._cumulative_size_calculated)
+                sumsize = sum(
+                    f.size for f in target.marked_items
+                    if not f.is_directory
+                    or f._cumulative_size_calculated  # pylint: disable=protected-access
+                )
                 right.add(human_readable(sumsize, separator=''))
             right.add("/" + str(len(target.marked_items)))
         else:
@@ -319,7 +323,7 @@ def get_free_space(path):
     return stat.f_bavail * stat.f_frsize
 
 
-class Message(object):
+class Message(object):  # pylint: disable=too-few-public-methods
     elapse = None
     text = None
     bad = False
diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py
index f72de94a..803b4ae4 100644
--- a/ranger/gui/widgets/taskview.py
+++ b/ranger/gui/widgets/taskview.py
@@ -3,9 +3,10 @@
 
 """The TaskView allows you to modify what the loader is doing."""
 
-from . import Widget
 from ranger.ext.accumulator import Accumulator
 
+from . import Widget
+
 
 class TaskView(Widget, Accumulator):
     old_lst = None
@@ -79,7 +80,7 @@ class TaskView(Widget, Accumulator):
         if self.fm.loader.queue:
             self.fm.loader.remove(index=i)
 
-    def task_move(self, to, i=None):
+    def task_move(self, to, i=None):  # pylint: disable=invalid-name
         if i is None:
             i = self.pointer
 
diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py
index 283da3a8..3f96e094 100644
--- a/ranger/gui/widgets/titlebar.py
+++ b/ranger/gui/widgets/titlebar.py
@@ -8,9 +8,9 @@ It displays the current path among other things.
 
 from os.path import basename
 
+from ranger.gui.bar import Bar
 
 from . import Widget
-from ranger.gui.bar import Bar
 
 
 class TitleBar(Widget):
@@ -56,7 +56,7 @@ class TitleBar(Widget):
             return False
 
         pos = self.wid - 1
-        for tabname in reversed(self.fm._get_tab_list()):
+        for tabname in reversed(self.fm._get_tab_list()):  # pylint: disable=protected-access
             tabtext = self._get_tab_text(tabname)
             pos -= len(tabtext)
             if event.x > pos:
@@ -123,13 +123,13 @@ class TitleBar(Widget):
 
     def _get_right_part(self, bar):
         # TODO: fix that pressed keys are cut off when chaining CTRL keys
-        kb = str(self.fm.ui.keybuffer)
-        self.old_keybuffer = kb
-        bar.addright(kb, 'keybuffer', fixed=True)
+        kbuf = str(self.fm.ui.keybuffer)
+        self.old_keybuffer = kbuf
+        bar.addright(kbuf, 'keybuffer', fixed=True)
         bar.addright('  ', 'space', fixed=True)
         self.tab_width = 0
         if len(self.fm.tabs) > 1:
-            for tabname in self.fm._get_tab_list():
+            for tabname in self.fm._get_tab_list():  # pylint: disable=protected-access
                 tabtext = self._get_tab_text(tabname)
                 self.tab_width += len(tabtext)
                 clr = 'good' if tabname == self.fm.current_tab else 'bad'
diff --git a/ranger/gui/widgets/view_base.py b/ranger/gui/widgets/view_base.py
index bc9a8260..d6652ef5 100644
--- a/ranger/gui/widgets/view_base.py
+++ b/ranger/gui/widgets/view_base.py
@@ -4,19 +4,18 @@
 """The base GUI element for views on the directory"""
 
 import curses
-import _curses
 from ranger.ext.keybinding_parser import key_to_string
 from . import Widget
 from ..displayable import DisplayableContainer
 
 
-class ViewBase(Widget, DisplayableContainer):
+class ViewBase(Widget, DisplayableContainer):  # pylint: disable=too-many-instance-attributes
     draw_bookmarks = False
     need_clear = False
     draw_hints = False
     draw_info = False
 
-    def __init__(self, win):
+    def __init__(self, win):  # pylint: disable=super-init-not-called
         DisplayableContainer.__init__(self, win)
 
         self.fm.signal_bind('move', self.request_clear)
@@ -51,10 +50,10 @@ class ViewBase(Widget, DisplayableContainer):
                 pass
         else:
             try:
-                x = self.main_column.x
-                y = self.main_column.y + self.main_column.target.pointer\
+                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(y, x)
+                self.fm.ui.win.move(col_y, col_x)
             except Exception:
                 pass
 
@@ -101,15 +100,15 @@ class ViewBase(Widget, DisplayableContainer):
         self.columns[-1].clear_image(force=True)
         self.need_clear = True
         hints = []
-        for k, v in self.fm.ui.keybuffer.pointer.items():
-            k = key_to_string(k)
-            if isinstance(v, dict):
+        for key, value in self.fm.ui.keybuffer.pointer.items():
+            key = key_to_string(key)
+            if isinstance(value, dict):
                 text = '...'
             else:
-                text = v
+                text = value
             if text.startswith('hint') or text.startswith('chain hint'):
                 continue
-            hints.append((k, text))
+            hints.append((key, text))
         hints.sort(key=lambda t: t[1])
 
         hei = min(self.hei - 1, len(hints))
@@ -154,7 +153,7 @@ class ViewBase(Widget, DisplayableContainer):
             self.main_column.scroll(direction)
         return False
 
-    def resize(self, y, x, hei, wid):
+    def resize(self, y, x, hei=None, wid=None):
         DisplayableContainer.resize(self, y, x, hei, wid)
 
     def poke(self):
diff --git a/ranger/gui/widgets/view_miller.py b/ranger/gui/widgets/view_miller.py
index b4db5b08..1496e18c 100644
--- a/ranger/gui/widgets/view_miller.py
+++ b/ranger/gui/widgets/view_miller.py
@@ -6,14 +6,14 @@
 import curses
 import _curses
 from ranger.container import settings
-from ranger.ext.signals import Signal
+from ranger.gui.widgets.view_base import ViewBase
+
 from .browsercolumn import BrowserColumn
 from .pager import Pager
 from ..displayable import DisplayableContainer
-from ranger.gui.widgets.view_base import ViewBase
 
 
-class ViewMiller(ViewBase):
+class ViewMiller(ViewBase):  # pylint: disable=too-many-ancestors,too-many-instance-attributes
     ratios = None
     preview = True
     is_collapsed = False
@@ -68,9 +68,9 @@ class ViewMiller(ViewBase):
             offset += 1
 
         for level in range(len(ratios)):
-            fl = BrowserColumn(self.win, level + offset)
-            self.add_child(fl)
-            self.columns.append(fl)
+            column = BrowserColumn(self.win, level + offset)
+            self.add_child(column)
+            self.columns.append(column)
 
         try:
             self.main_column = self.columns[self.preview and -2 or -1]
@@ -130,10 +130,12 @@ class ViewMiller(ViewBase):
 
         # Draw horizontal lines and the leftmost vertical line
         try:
+            # pylint: disable=no-member
             win.hline(0, left_start, curses.ACS_HLINE, right_end - left_start)
             win.hline(self.hei - 1, left_start, curses.ACS_HLINE,
                       right_end - left_start)
             win.vline(1, left_start, curses.ACS_VLINE, self.hei - 2)
+            # pylint: enable=no-member
         except _curses.error:
             pass
 
@@ -148,23 +150,29 @@ class ViewMiller(ViewBase):
             x = child.x + child.wid
             y = self.hei - 1
             try:
+                # pylint: disable=no-member
                 win.vline(1, x, curses.ACS_VLINE, y - 1)
                 self.addch(0, x, curses.ACS_TTEE, 0)
                 self.addch(y, x, curses.ACS_BTEE, 0)
+                # pylint: enable=no-member
             except Exception:
                 # in case it's off the boundaries
                 pass
 
         # Draw the last vertical line
         try:
+            # pylint: disable=no-member
             win.vline(1, right_end, curses.ACS_VLINE, self.hei - 2)
+            # pylint: enable=no-member
         except _curses.error:
             pass
 
+        # pylint: disable=no-member
         self.addch(0, left_start, curses.ACS_ULCORNER)
         self.addch(self.hei - 1, left_start, curses.ACS_LLCORNER)
         self.addch(0, right_end, curses.ACS_URCORNER)
         self.addch(self.hei - 1, right_end, curses.ACS_LRCORNER)
+        # pylint: enable=no-member
 
     def _collapse(self):
         # Should the last column be cut off? (Because there is no preview)
@@ -184,7 +192,7 @@ class ViewMiller(ViewBase):
         self.old_collapse = result
         return result
 
-    def resize(self, y, x, hei, wid):
+    def resize(self, y, x, hei=None, wid=None):
         """Resize all the columns according to the given ratio"""
         ViewBase.resize(self, y, x, hei, wid)
 
diff --git a/ranger/gui/widgets/view_multipane.py b/ranger/gui/widgets/view_multipane.py
index abeb31fe..f99b133c 100644
--- a/ranger/gui/widgets/view_multipane.py
+++ b/ranger/gui/widgets/view_multipane.py
@@ -5,7 +5,7 @@ from ranger.gui.widgets.view_base import ViewBase
 from ranger.gui.widgets.browsercolumn import BrowserColumn
 
 
-class ViewMultipane(ViewBase):
+class ViewMultipane(ViewBase):  # pylint: disable=too-many-ancestors
 
     def __init__(self, win):
         ViewBase.__init__(self, win)
@@ -41,12 +41,12 @@ class ViewMultipane(ViewBase):
             self.add_child(column)
         self.resize(self.y, self.x, self.hei, self.wid)
 
-    def resize(self, y, x, hei, wid):
+    def resize(self, y, x, hei=None, wid=None):
         ViewBase.resize(self, y, x, hei, wid)
         column_width = int((float(wid) - len(self.columns) + 1) / len(self.columns))
         left = 0
         top = 0
-        for i, column in enumerate(self.columns):
+        for column in self.columns:
             column.resize(top, left, hei, max(1, column_width))
             left += column_width + 1
             column.need_redraw = True
diff --git a/setup.py b/setup.py
index ce0631f0..26bb58e2 100755
--- a/setup.py
+++ b/setup.py
@@ -4,6 +4,7 @@
 
 import distutils.core
 import os.path
+
 import ranger
 
 
@@ -11,6 +12,7 @@ def _findall(directory):
     return [os.path.join(directory, f) for f in os.listdir(directory)
             if os.path.isfile(os.path.join(directory, f))]
 
+
 if __name__ == '__main__':
     distutils.core.setup(
         name='ranger',
@@ -24,17 +26,17 @@ if __name__ == '__main__':
         scripts=['scripts/ranger', 'scripts/rifle'],
         data_files=[
             ('share/applications',
-                ['doc/ranger.desktop']),
+             ['doc/ranger.desktop']),
             ('share/man/man1',
-                ['doc/ranger.1',
-                 'doc/rifle.1']),
+             ['doc/ranger.1',
+              'doc/rifle.1']),
             ('share/doc/ranger',
-                ['README.md',
-                 'CHANGELOG.md',
-                 'HACKING.md',
-                 'doc/colorschemes.txt']),
+             ['README.md',
+              'CHANGELOG.md',
+              'HACKING.md',
+              'doc/colorschemes.txt']),
             ('share/doc/ranger/config/colorschemes',
-                _findall('doc/config/colorschemes')),
+             _findall('doc/config/colorschemes')),
             ('share/doc/ranger/config', _findall('doc/config')),
             ('share/doc/ranger/tools', _findall('doc/tools')),
             ('share/doc/ranger/examples', _findall('examples')),
diff --git a/tests/ranger/container/test_bookmarks.py b/tests/ranger/container/test_bookmarks.py
index a2cd446f..8107a71a 100644
--- a/tests/ranger/container/test_bookmarks.py
+++ b/tests/ranger/container/test_bookmarks.py
@@ -1,5 +1,6 @@
 import os
 import time
+
 import pytest
 
 from ranger.container.bookmarks import Bookmarks
diff --git a/tests/ranger/container/test_container.py b/tests/ranger/container/test_container.py
index 2b823912..7144236f 100644
--- a/tests/ranger/container/test_container.py
+++ b/tests/ranger/container/test_container.py
@@ -10,92 +10,92 @@ def testhistorybasic():
     # item added to it. It has a `current` index that serves as a cursor.
 
     # A history has a limited size, check that only `maxlen` items are stored
-    h = history.History(maxlen=10)
+    hist = history.History(maxlen=10)
     for entry in HISTORY_TEST_ENTRIES:
-        h.add(entry)
+        hist.add(entry)
 
     # 10 items are stored
-    assert len(h) == 10
-    assert h.current() == "19"
-    assert h.top() == "19"
-    assert h.bottom() == "10"
+    assert len(hist) == 10
+    assert hist.current() == "19"
+    assert hist.top() == "19"
+    assert hist.bottom() == "10"
 
     # going back in time affects only changes current item
-    h.back()
-    assert len(h) == 10
-    assert h.current() == "18"
-    assert h.top() == "19"
-    assert h.bottom() == "10"
+    hist.back()
+    assert len(hist) == 10
+    assert hist.current() == "18"
+    assert hist.top() == "19"
+    assert hist.bottom() == "10"
 
     # __iter__ is actually an interator and we can iterate through the list
-    it = iter(h)
-    assert iter(it) == it
-    assert list(it) == HISTORY_TEST_ENTRIES[10:]
+    iterator = iter(hist)
+    assert iter(iterator) == iterator
+    assert list(iterator) == HISTORY_TEST_ENTRIES[10:]
 
     # search allows to go back in time as long as a pattern matches and we don't
     # go over a step limit
-    assert h.search("45", -9) == "18"
-    assert h.search("1", -5) == "13"
+    assert hist.search("45", -9) == "18"
+    assert hist.search("1", -5) == "13"
 
     # fast forward selects the last item
-    h.fast_forward()
-    assert h.current() == "19"
+    hist.fast_forward()
+    assert hist.current() == "19"
 
     # back followed by forward is a noop
-    h.back()
-    h.forward()
-    assert h.current() == "19"
+    hist.back()
+    hist.forward()
+    assert hist.current() == "19"
 
     # move can be expressed as multiple calls to back and forward
-    h.move(-3)
-    h.forward()
-    h.forward()
-    h.forward()
-    assert h.current() == "19"
+    hist.move(-3)
+    hist.forward()
+    hist.forward()
+    hist.forward()
+    assert hist.current() == "19"
 
     # back, forward, move play well with boundaries
     for _ in range(30):
-        h.back()
+        hist.back()
 
     for _ in range(30):
-        h.forward()
+        hist.forward()
 
     for _ in range(30):
-        h.move(-2)
+        hist.move(-2)
 
     for _ in range(30):
-        h.move(2)
-    assert h.current() == "19"
+        hist.move(2)
+    assert hist.current() == "19"
 
     # we can create an history from another history
-    h = history.History(maxlen=10)
+    hist = history.History(maxlen=10)
     for entry in HISTORY_TEST_ENTRIES:
-        h.add(entry)
+        hist.add(entry)
     # XXX maxlen should not be used to refer to something that isn't a length
-    otherh = history.History(maxlen=h)
-    assert(list(h) == list(otherh))
+    otherh = history.History(maxlen=hist)
+    assert list(hist) == list(otherh)
 
     # Rebase replaces the past of the history with that of another
-    otherh = history.History(maxlen=h)
-    old_current_item = h.current()
+    otherh = history.History(maxlen=hist)
+    old_current_item = hist.current()
     for entry in OTHER_TEST_ENTRIES:
         otherh.add(entry)
     assert list(otherh)[-3:] == ["42", "43", "44"]
-    h.rebase(otherh)
-    assert h.current() == old_current_item
-    assert list(h)[-3:] == ['43', '44', old_current_item]
+    hist.rebase(otherh)
+    assert hist.current() == old_current_item
+    assert list(hist)[-3:] == ['43', '44', old_current_item]
 
     # modify, modifies the top of the stack
-    h.modify("23")
-    assert h.current() == "23"
+    hist.modify("23")
+    assert hist.current() == "23"
 
 
 def testhistoryunique():
     # Check that unique history refuses to store duplicated entries
-    h = history.History(maxlen=10, unique=True)
+    hist = history.History(maxlen=10, unique=True)
     for entry in HISTORY_TEST_ENTRIES:
-        h.add(entry)
-    assert h.current() == "19"
-    h.add("17")
-    assert list(h).count("17") == 1
-    assert h.current() == "17"
+        hist.add(entry)
+    assert hist.current() == "19"
+    hist.add("17")
+    assert list(hist).count("17") == 1
+    assert hist.current() == "17"
diff --git a/tests/ranger/container/test_fsobject.py b/tests/ranger/container/test_fsobject.py
index 73d2024a..49c15666 100644
--- a/tests/ranger/container/test_fsobject.py
+++ b/tests/ranger/container/test_fsobject.py
@@ -1,10 +1,9 @@
-import pytest
 import operator
 
 from ranger.container.fsobject import FileSystemObject
 
 
-class MockFM(object):
+class MockFM(object):  # pylint: disable=too-few-public-methods
     """Used to fulfill the dependency by FileSystemObject."""
 
     default_linemodes = []
@@ -22,13 +21,13 @@ def test_basename_natural1():
     """Test filenames without extensions."""
     fsos = [create_filesystem_object(path)
             for path in ("hello", "hello1", "hello2")]
-    assert(fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural")))
-    assert(fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural_lower")))
+    assert fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural"))
+    assert fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural_lower"))
 
 
 def test_basename_natural2():
     """Test filenames with extensions."""
     fsos = [create_filesystem_object(path)
             for path in ("hello", "hello.txt", "hello1.txt", "hello2.txt")]
-    assert(fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural")))
-    assert(fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural_lower")))
+    assert fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural"))
+    assert fsos == sorted(fsos[::-1], key=operator.attrgetter("basename_natural_lower"))
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..61d90815
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 99