Add flake8 checks in tox and Python 3.8 in supported versions
diff --git a/.travis.yml b/.travis.yml
index ba42c72..a282485 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,10 @@
       python: "3.7"
       env: TOXENV=py37
       dist: xenial
-      sudo: true
+    - os: linux
+      python: "3.8-dev"
+      env: TOXENV=py38
+      dist: xenial
     - os: linux
       python: "pypy"
       env: TOXENV=pypy
@@ -30,8 +33,8 @@
     - os: osx
       language: generic
       env:
-        - PYTHON_VERSION=3.6
-        - TOXENV=py36
+        - PYTHON_VERSION=3.7
+        - TOXENV=py37
 
 before_install:
   - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then chmod +x .travis/install.sh && .travis/install.sh; fi
@@ -44,4 +47,4 @@
   - tox
 
 notifications:
-    email: false
+  email: false
diff --git a/docs/source/conf.py b/docs/source/conf.py
index efe7ca2..101c7ab 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -17,9 +17,9 @@
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-TOP_DIR_PATH = os.path.abspath('../../')
-SRC_DIR_PATH = os.path.join(TOP_DIR_PATH, 'src')
-sys.path.insert(0, SRC_DIR_PATH)
+TOP_DIR_PATH = os.path.abspath('../../')  # noqa
+SRC_DIR_PATH = os.path.join(TOP_DIR_PATH, 'src')  # noqa
+sys.path.insert(0, SRC_DIR_PATH)  # noqa
 
 import watchdog.version
 
@@ -30,12 +30,15 @@
 
 # -- General configuration -----------------------------------------------------
 
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.todo',
+    'sphinx.ext.coverage',
+    'sphinx.ext.ifconfig',
+    'sphinx.ext.viewcode'
+]
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
@@ -43,9 +46,6 @@
 # The suffix of source filenames.
 source_suffix = '.rst'
 
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
 # The master toctree document.
 master_doc = 'index'
 
@@ -62,161 +62,33 @@
 # The full version, including alpha/beta/rc tags.
 release = version
 
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-#html_theme = 'default'
 html_theme = 'pyramid'
 
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
 # Output file base name for HTML help builder.
 htmlhelp_basename = '%sdoc' % PROJECT_NAME
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', '%s.tex' % PROJECT_NAME, '%s Documentation' % PROJECT_NAME,
-   AUTHOR_NAME, 'manual'),
+    ('index', '%s.tex' % PROJECT_NAME, '%s Documentation' % PROJECT_NAME,
+     AUTHOR_NAME, 'manual'),
 ]
 
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
 # -- Options for manual page output --------------------------------------------
 
 # One entry per manual page. List of tuples
@@ -234,34 +106,3 @@
 epub_author = AUTHOR_NAME
 epub_publisher = AUTHOR_NAME
 epub_copyright = COPYRIGHT
-
-# The language of the text. It defaults to the language option
-# or en if the language is not set.
-#epub_language = ''
-
-# The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#epub_identifier = ''
-
-# A unique identification for the text.
-#epub_uid = ''
-
-# HTML files that should be inserted before the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_pre_files = []
-
-# HTML files shat should be inserted after the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_post_files = []
-
-# A list of files that should not be packed into the epub file.
-#epub_exclude_files = []
-
-# The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
-
-# Allow duplicate toc entries.
-#epub_tocdup = True
diff --git a/docs/source/examples/logger.py b/docs/source/examples/logger.py
index 3337e93..577ab01 100644
--- a/docs/source/examples/logger.py
+++ b/docs/source/examples/logger.py
@@ -17,4 +17,3 @@
 except KeyboardInterrupt:
     observer.stop()
 observer.join()
-
diff --git a/docs/source/examples/patterns.py b/docs/source/examples/patterns.py
index b8c271a..bb750b5 100644
--- a/docs/source/examples/patterns.py
+++ b/docs/source/examples/patterns.py
@@ -10,13 +10,15 @@
 import logging
 logging.basicConfig(level=logging.DEBUG)
 
+
 class MyEventHandler(PatternMatchingEventHandler):
     def on_any_event(self, event):
         logging.debug(event)
 
+
 event_handler = MyEventHandler(patterns=['*.py', '*.pyc'],
-                                ignore_patterns=['version.py'],
-                                ignore_directories=True)
+                               ignore_patterns=['version.py'],
+                               ignore_directories=True)
 observer = Observer()
 observer.schedule(event_handler, sys.argv[1], recursive=True)
 observer.start()
@@ -26,4 +28,3 @@
 except KeyboardInterrupt:
     observer.stop()
 observer.join()
-
diff --git a/docs/source/examples/simple.py b/docs/source/examples/simple.py
index eb46959..fe60e19 100644
--- a/docs/source/examples/simple.py
+++ b/docs/source/examples/simple.py
@@ -10,6 +10,7 @@
 
 logging.basicConfig(level=logging.DEBUG)
 
+
 class MyEventHandler(FileSystemEventHandler):
     def catch_all_handler(self, event):
         logging.debug(event)
@@ -26,6 +27,7 @@
     def on_modified(self, event):
         self.catch_all_handler(event)
 
+
 path = sys.argv[1]
 
 event_handler = MyEventHandler()
diff --git a/setup.cfg b/setup.cfg
index 669bb6a..e3cfc2f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,6 +3,14 @@
 build-dir  = docs/build
 all_files  = 1
 
+[flake8]
+ignore =
+    # E203 whitespace before ':', but E203 is not PEP 8 compliant
+    E203
+    # W503 line break before binary operator, but W503 is not PEP 8 compliant
+    W503
+max-line-length = 120
+
 [upload_sphinx]
 # Requires sphinx-pypi-upload to work.
 upload-dir = docs/build/html
diff --git a/setup.py b/setup.py
index 81a746c..59f54f3 100644
--- a/setup.py
+++ b/setup.py
@@ -124,6 +124,7 @@
           'Programming Language :: Python :: 3.5',
           'Programming Language :: Python :: 3.6',
           'Programming Language :: Python :: 3.7',
+          'Programming Language :: Python :: 3.8',
           'Programming Language :: Python :: Implementation :: PyPy',
           'Programming Language :: C',
           'Topic :: Software Development :: Libraries',
diff --git a/src/watchdog/observers/__init__.py b/src/watchdog/observers/__init__.py
index b03ff68..7ba6d3d 100644
--- a/src/watchdog/observers/__init__.py
+++ b/src/watchdog/observers/__init__.py
@@ -28,7 +28,7 @@
    :members:
    :show-inheritance:
    :inherited-members:
-   
+
 Observer thread that schedules watching directories and dispatches
 calls to event handlers.
 
diff --git a/src/watchdog/observers/fsevents.py b/src/watchdog/observers/fsevents.py
index 42900ec..b7e4b0e 100644
--- a/src/watchdog/observers/fsevents.py
+++ b/src/watchdog/observers/fsevents.py
@@ -80,8 +80,8 @@
 
     def queue_events(self, timeout):
         with self._lock:
-            if not self.watch.is_recursive\
-                and self.watch.path not in self.pathnames:
+            if (not self.watch.is_recursive
+                    and self.watch.path not in self.pathnames):
                 return
             new_snapshot = DirectorySnapshot(self.watch.path,
                                              self.watch.is_recursive)
@@ -126,7 +126,7 @@
 
             # INFO: FSEvents reports directory notifications recursively
             # by default, so we do not need to add subdirectory paths.
-            #pathnames = set([self.watch.path])
+            # pathnames = set([self.watch.path])
             # if self.watch.is_recursive:
             #    for root, directory_names, _ in os.walk(self.watch.path):
             #        for directory_name in directory_names:
@@ -159,7 +159,7 @@
         # Fix for issue #26: Trace/BPT error when given a unicode path
         # string. https://github.com/gorakhargosh/watchdog/issues#issue/26
         if isinstance(path, str_class):
-            #path = unicode(path, 'utf-8')
+            # path = unicode(path, 'utf-8')
             path = unicodedata.normalize('NFC', path)
             # We only encode the path in Python 2 for backwards compatibility.
             # On Python 3 we want the path to stay as unicode if possible for
diff --git a/src/watchdog/observers/fsevents2.py b/src/watchdog/observers/fsevents2.py
index b42b5b6..9f48d7e 100644
--- a/src/watchdog/observers/fsevents2.py
+++ b/src/watchdog/observers/fsevents2.py
@@ -157,17 +157,23 @@
 
     @property
     def _event_type(self):
-        if self.is_created: return "Created"
-        if self.is_removed: return "Removed"
-        if self.is_renamed: return "Renamed"
-        if self.is_modified: return "Modified"
-        if self.is_inode_meta_mod: return "InodeMetaMod"
-        if self.is_xattr_mod: return "XattrMod"
+        if self.is_created:
+            return "Created"
+        if self.is_removed:
+            return "Removed"
+        if self.is_renamed:
+            return "Renamed"
+        if self.is_modified:
+            return "Modified"
+        if self.is_inode_meta_mod:
+            return "InodeMetaMod"
+        if self.is_xattr_mod:
+            return "XattrMod"
         return "Unknown"
 
     def __repr__(self):
-        s ="<%s: path=%s, type=%s, is_dir=%s, flags=%s, id=%s>"
-        return s % (type(self).__name__, repr(self.path),self._event_type,
+        s = "<%s: path=%s, type=%s, is_dir=%s, flags=%s, id=%s>"
+        return s % (type(self).__name__, repr(self.path), self._event_type,
                     self.is_directory, hex(self.flags), self.event_id)
 
 
@@ -201,13 +207,13 @@
                 # don't) making it possible to pair up the two events coming
                 # from a singe move operation. (None of this is documented!)
                 # Otherwise, guess whether file was moved in or out.
-                #TODO: handle id wrapping
-                if (i+1 < len(events) and events[i+1].is_renamed and
-                        events[i+1].event_id == event.event_id + 1):
+                # TODO: handle id wrapping
+                if (i + 1 < len(events) and events[i + 1].is_renamed
+                        and events[i + 1].event_id == event.event_id + 1):
                     cls = DirMovedEvent if event.is_directory else FileMovedEvent
-                    self.queue_event(cls(event.path, events[i+1].path))
+                    self.queue_event(cls(event.path, events[i + 1].path))
                     self.queue_event(DirModifiedEvent(os.path.dirname(event.path)))
-                    self.queue_event(DirModifiedEvent(os.path.dirname(events[i+1].path)))
+                    self.queue_event(DirModifiedEvent(os.path.dirname(events[i + 1].path)))
                     i += 1
                 elif os.path.exists(event.path):
                     cls = DirCreatedEvent if event.is_directory else FileCreatedEvent
@@ -217,7 +223,7 @@
                     cls = DirDeletedEvent if event.is_directory else FileDeletedEvent
                     self.queue_event(cls(event.path))
                     self.queue_event(DirModifiedEvent(os.path.dirname(event.path)))
-                #TODO: generate events for tree
+                # TODO: generate events for tree
 
             elif event.is_modified or event.is_inode_meta_mod or event.is_xattr_mod :
                 cls = DirModifiedEvent if event.is_directory else FileModifiedEvent
diff --git a/src/watchdog/observers/inotify.py b/src/watchdog/observers/inotify.py
index e26a6a6..6e56052 100644
--- a/src/watchdog/observers/inotify.py
+++ b/src/watchdog/observers/inotify.py
@@ -200,10 +200,11 @@
     """
     def __init__(self, event_queue, watch, timeout=DEFAULT_EMITTER_TIMEOUT):
         InotifyEmitter.__init__(self, event_queue, watch, timeout)
-        
+
     def queue_events(self, timeout, events=True):
         InotifyEmitter.queue_events(self, timeout, full_events=events)
 
+
 class InotifyObserver(BaseObserver):
     """
     Observer thread that schedules watching directories and dispatches
@@ -215,4 +216,4 @@
             BaseObserver.__init__(self, emitter_class=InotifyFullEmitter, timeout=timeout)
         else:
             BaseObserver.__init__(self, emitter_class=InotifyEmitter,
-                              timeout=timeout)
+                                  timeout=timeout)
diff --git a/src/watchdog/observers/inotify_buffer.py b/src/watchdog/observers/inotify_buffer.py
index a8d2ba0..de0802f 100644
--- a/src/watchdog/observers/inotify_buffer.py
+++ b/src/watchdog/observers/inotify_buffer.py
@@ -55,6 +55,7 @@
         grouped = []
         for inotify_event in event_list:
             logger.debug("in-event %s", inotify_event)
+
             def matching_from_event(event):
                 return (not isinstance(event, tuple) and event.is_moved_from
                         and event.cookie == inotify_event.cookie)
diff --git a/src/watchdog/observers/inotify_c.py b/src/watchdog/observers/inotify_c.py
index d6db4f4..c9c1b5c 100644
--- a/src/watchdog/observers/inotify_c.py
+++ b/src/watchdog/observers/inotify_c.py
@@ -320,7 +320,7 @@
                 if wd == -1:
                     continue
                 wd_path = self._path_for_wd[wd]
-                src_path = os.path.join(wd_path, name) if name else wd_path #avoid trailing slash
+                src_path = os.path.join(wd_path, name) if name else wd_path  # avoid trailing slash
                 inotify_event = InotifyEvent(wd, mask, cookie, name, src_path)
 
                 if inotify_event.is_moved_from:
@@ -344,8 +344,8 @@
 
                 event_list.append(inotify_event)
 
-                if (self.is_recursive and inotify_event.is_directory and
-                        inotify_event.is_create):
+                if (self.is_recursive and inotify_event.is_directory
+                        and inotify_event.is_create):
 
                     # TODO: When a directory from another part of the
                     # filesystem is moved into a watched directory, this
@@ -543,8 +543,8 @@
         # It looks like the kernel does not provide this information for
         # IN_DELETE_SELF and IN_MOVE_SELF. In this case, assume it's a dir.
         # See also: https://github.com/seb-m/pyinotify/blob/2c7e8f8/python2/pyinotify.py#L897
-        return (self.is_delete_self or self.is_move_self or
-                self._mask & InotifyConstants.IN_ISDIR > 0)
+        return (self.is_delete_self or self.is_move_self
+                or self._mask & InotifyConstants.IN_ISDIR > 0)
 
     @property
     def key(self):
diff --git a/src/watchdog/observers/kqueue.py b/src/watchdog/observers/kqueue.py
index 458dbd0..490d64f 100644
--- a/src/watchdog/observers/kqueue.py
+++ b/src/watchdog/observers/kqueue.py
@@ -62,7 +62,8 @@
    :members:
    :show-inheritance:
 
-.. _Mac OS X File System Performance Guidelines: http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/FileSystem/Articles/TrackingChanges.html#//apple_ref/doc/uid/20001993-CJBJFIDD
+.. _Mac OS X File System Performance Guidelines:
+    http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/FileSystem/Articles/TrackingChanges.html#//apple_ref/doc/uid/20001993-CJBJFIDD
 
 """
 
@@ -114,13 +115,13 @@
 WATCHDOG_KQ_FILTER = select.KQ_FILTER_VNODE
 WATCHDOG_KQ_EV_FLAGS = select.KQ_EV_ADD | select.KQ_EV_ENABLE | select.KQ_EV_CLEAR
 WATCHDOG_KQ_FFLAGS = (
-    select.KQ_NOTE_DELETE |
-    select.KQ_NOTE_WRITE |
-    select.KQ_NOTE_EXTEND |
-    select.KQ_NOTE_ATTRIB |
-    select.KQ_NOTE_LINK |
-    select.KQ_NOTE_RENAME |
-    select.KQ_NOTE_REVOKE
+    select.KQ_NOTE_DELETE
+    | select.KQ_NOTE_WRITE
+    | select.KQ_NOTE_EXTEND
+    | select.KQ_NOTE_ATTRIB
+    | select.KQ_NOTE_LINK
+    | select.KQ_NOTE_RENAME
+    | select.KQ_NOTE_REVOKE
 )
 
 # Flag tests.
@@ -460,7 +461,7 @@
                 # and then quickly deleted before we could open
                 # a descriptor for it. Therefore, simply queue a sequence
                 # of created and deleted events for the path.
-                #path = absolute_path(path)
+                # path = absolute_path(path)
                 # if is_directory:
                 #    self.queue_event(DirCreatedEvent(path))
                 #    self.queue_event(DirDeletedEvent(path))
diff --git a/src/watchdog/observers/winapi.py b/src/watchdog/observers/winapi.py
index 6eaa7f6..8a17370 100644
--- a/src/watchdog/observers/winapi.py
+++ b/src/watchdog/observers/winapi.py
@@ -223,9 +223,10 @@
     _fields_ = [("NextEntryOffset", ctypes.wintypes.DWORD),
                 ("Action", ctypes.wintypes.DWORD),
                 ("FileNameLength", ctypes.wintypes.DWORD),
-                #("FileName", (ctypes.wintypes.WCHAR * 1))]
+                # ("FileName", (ctypes.wintypes.WCHAR * 1))]
                 ("FileName", (ctypes.c_char * 1))]
 
+
 LPFNI = ctypes.POINTER(FILE_NOTIFY_INFORMATION)
 
 
@@ -258,7 +259,7 @@
     while nBytes > 0:
         fni = ctypes.cast(readBuffer, LPFNI)[0]
         ptr = ctypes.addressof(fni) + FILE_NOTIFY_INFORMATION.FileName.offset
-        #filename = ctypes.wstring_at(ptr, fni.FileNameLength)
+        # filename = ctypes.wstring_at(ptr, fni.FileNameLength)
         filename = ctypes.string_at(ptr, fni.FileNameLength)
         results.append((fni.Action, filename.decode('utf-16')))
         numToSkip = fni.NextEntryOffset
diff --git a/src/watchdog/utils/bricks.py b/src/watchdog/utils/bricks.py
index 843f15c..8d68745 100644
--- a/src/watchdog/utils/bricks.py
+++ b/src/watchdog/utils/bricks.py
@@ -38,6 +38,7 @@
 
 from .compat import queue
 
+
 class SkipRepeatsQueue(queue.Queue):
 
     """Thread-safe implementation of an special queue where a
diff --git a/src/watchdog/utils/dirsnapshot.py b/src/watchdog/utils/dirsnapshot.py
index ee04438..abf0604 100644
--- a/src/watchdog/utils/dirsnapshot.py
+++ b/src/watchdog/utils/dirsnapshot.py
@@ -69,17 +69,17 @@
     :type snapshot:
         :class:`DirectorySnapshot`
     """
-    
+
     def __init__(self, ref, snapshot):
         created = snapshot.paths - ref.paths
         deleted = ref.paths - snapshot.paths
-        
+
         # check that all unchanged paths have the same inode
         for path in ref.paths & snapshot.paths:
             if ref.inode(path) != snapshot.inode(path):
                 created.add(path)
                 deleted.add(path)
-        
+
         # find moved paths
         moved = set()
         for path in set(deleted):
@@ -89,14 +89,14 @@
                 # file is not deleted but moved
                 deleted.remove(path)
                 moved.add((path, new_path))
-        
+
         for path in set(created):
             inode = snapshot.inode(path)
             old_path = ref.path(inode)
             if old_path:
                 created.remove(path)
                 moved.add((old_path, path))
-        
+
         # find modified paths
         # first check paths that have not moved
         modified = set()
@@ -104,21 +104,21 @@
             if ref.inode(path) == snapshot.inode(path):
                 if ref.mtime(path) != snapshot.mtime(path) or ref.size(path) != snapshot.size(path):
                     modified.add(path)
-        
+
         for (old_path, new_path) in moved:
             if ref.mtime(old_path) != snapshot.mtime(new_path) or ref.size(old_path) != snapshot.size(new_path):
                 modified.add(old_path)
-        
+
         self._dirs_created = [path for path in created if snapshot.isdir(path)]
         self._dirs_deleted = [path for path in deleted if ref.isdir(path)]
         self._dirs_modified = [path for path in modified if ref.isdir(path)]
         self._dirs_moved = [(frm, to) for (frm, to) in moved if ref.isdir(frm)]
-        
+
         self._files_created = list(created - set(self._dirs_created))
         self._files_deleted = list(deleted - set(self._dirs_deleted))
         self._files_modified = list(modified - set(self._dirs_modified))
         self._files_moved = list(moved - set(self._dirs_moved))
-    
+
     @property
     def files_created(self):
         """List of files that were created."""
@@ -175,6 +175,7 @@
         """
         return self._dirs_created
 
+
 class DirectorySnapshot(object):
     """
     A snapshot of stat information of files in a directory.
@@ -193,20 +194,20 @@
     :param stat:
         Use custom stat function that returns a stat structure for path.
         Currently only st_dev, st_ino, st_mode and st_mtime are needed.
-        
+
         A function with the signature ``walker_callback(path, stat_info)``
         which will be called for every entry in the directory tree.
     :param listdir:
         Use custom listdir function. For details see ``os.scandir`` if available, else ``os.listdir``.
     """
-    
+
     def __init__(self, path, recursive=True,
                  walker_callback=(lambda p, s: None),
                  stat=default_stat,
                  listdir=scandir):
         self._stat_info = {}
         self._inode_to_path = {}
-        
+
         st = stat(path)
         self._stat_info[path] = st
         self._inode_to_path[(st.st_ino, st.st_dev)] = path
@@ -250,27 +251,27 @@
         Set of file/directory paths in the snapshot.
         """
         return set(self._stat_info.keys())
-    
+
     def path(self, id):
         """
         Returns path for id. None if id is unknown to this snapshot.
         """
         return self._inode_to_path.get(id)
-    
+
     def inode(self, path):
         """ Returns an id for path. """
         st = self._stat_info[path]
         return (st.st_ino, st.st_dev)
-    
+
     def isdir(self, path):
         return S_ISDIR(self._stat_info[path].st_mode)
-    
+
     def mtime(self, path):
         return self._stat_info[path].st_mtime
 
     def size(self, path):
         return self._stat_info[path].st_size
-    
+
     def stat_info(self, path):
         """
         Returns a stat information object for the specified path from
@@ -294,9 +295,9 @@
             A :class:`DirectorySnapshotDiff` object.
         """
         return DirectorySnapshotDiff(previous_dirsnap, self)
-    
+
     def __str__(self):
         return self.__repr__()
-    
+
     def __repr__(self):
         return str(self._stat_info)
diff --git a/src/watchdog/utils/echo.py b/src/watchdog/utils/echo.py
index 12803e0..28b6038 100644
--- a/src/watchdog/utils/echo.py
+++ b/src/watchdog/utils/echo.py
@@ -45,6 +45,7 @@
     " Determine if an instancemethod is a classmethod. "
     return inspect.ismethod(instancemethod) and instancemethod.__self__ is klass
 
+
 def is_static_method(method, klass):
     """Returns True if method is an instance method of klass."""
     for c in klass.mro():
@@ -53,6 +54,7 @@
     else:
         return False
 
+
 def is_class_private_name(name):
     " Determine if a name is a class private name. "
     # Exclude system defined names such as __init__, __add__ etc
@@ -127,20 +129,22 @@
     else:
         setattr(klass, mname, echo(method, write))
 
+
 def echo_class(klass, write=sys.stdout.write):
     """ Echo calls to class methods and static functions
     """
     for _, method in inspect.getmembers(klass, inspect.ismethod):
-        #In python 3 only class methods are returned here, but in python2 instance methods are too.
+        # In python 3 only class methods are returned here, but in python2 instance methods are too.
         echo_instancemethod(klass, method, write)
     for _, fn in inspect.getmembers(klass, inspect.isfunction):
         if is_static_method(fn, klass):
             setattr(klass, name(fn), staticmethod(echo(fn, write)))
         else:
-            #It's not a class or a static method, so it must be an instance method.
-            #This should only be called in python 3, because in python 3 instance methods are considered functions.
+            # It's not a class or a static method, so it must be an instance method.
+            # This should only be called in python 3, because in python 3 instance methods are considered functions.
             echo_instancemethod(klass, fn, write)
 
+
 def echo_module(mod, write=sys.stdout.write):
     """ Echo calls to functions and methods in a module.
     """
@@ -149,6 +153,7 @@
     for _, klass in inspect.getmembers(mod, inspect.isclass):
         echo_class(klass, write)
 
+
 if __name__ == "__main__":
     import doctest
 
diff --git a/src/watchdog/utils/platform.py b/src/watchdog/utils/platform.py
index 84a3efa..55bda3e 100644
--- a/src/watchdog/utils/platform.py
+++ b/src/watchdog/utils/platform.py
@@ -38,6 +38,7 @@
     else:
         return PLATFORM_UNKNOWN
 
+
 __platform__ = get_platform_name()
 
 
diff --git a/src/watchdog/utils/win32stat.py b/src/watchdog/utils/win32stat.py
index 4f6bb84..41da772 100644
--- a/src/watchdog/utils/win32stat.py
+++ b/src/watchdog/utils/win32stat.py
@@ -87,6 +87,7 @@
 
 StatResult = namedtuple('StatResult', 'st_dev st_ino st_mode st_mtime st_size')
 
+
 def _to_mode(attr):
     m = 0
     if (attr & FILE_ATTRIBUTE_DIRECTORY):
@@ -99,18 +100,22 @@
         m |= 0o666
     return m
 
+
 def _to_unix_time(ft):
     t = (ft.dwHighDateTime) << 32 | ft.dwLowDateTime
     return (t / 10000000) - 11644473600
 
+
 def stat(path):
     hfile = CreateFile(path,
-            FILE_READ_ATTRIBUTES,
-            0,
-            None,
-            OPEN_EXISTING,
-            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
-            None)
+                       FILE_READ_ATTRIBUTES,
+                       0,
+                       None,
+                       OPEN_EXISTING,
+                       FILE_ATTRIBUTE_NORMAL
+                       | FILE_FLAG_BACKUP_SEMANTICS
+                       | FILE_FLAG_OPEN_REPARSE_POINT,
+                       None)
     if hfile == INVALID_HANDLE_VALUE:
         raise ctypes.WinError()
     info = BY_HANDLE_FILE_INFORMATION()
diff --git a/src/watchdog/watchmedo.py b/src/watchdog/watchmedo.py
index 4f76914..da893d9 100755
--- a/src/watchdog/watchmedo.py
+++ b/src/watchdog/watchmedo.py
@@ -343,8 +343,8 @@
     elif args.debug_force_fsevents:
         from watchdog.observers.fsevents import FSEventsObserver as Observer
     else:
-    # Automatically picks the most appropriate observer for the platform
-    # on which it is running.
+        # Automatically picks the most appropriate observer for the platform
+        # on which it is running.
         from watchdog.observers import Observer
     observer = Observer(timeout=args.timeout)
     observe_with(observer, handler, args.directories, args.recursive)
@@ -412,7 +412,7 @@
      dest='drop_during_process',
      action='store_true',
      default=False,
-     help="Ignore events that occur while command is still being executed " \
+     help="Ignore events that occur while command is still being executed "
           "to avoid multiple simultaneous instances")
 @arg('--debug-force-polling',
      default=False,
diff --git a/tests/test_delayed_queue.py b/tests/test_delayed_queue.py
index 387e170..a62b3bd 100644
--- a/tests/test_delayed_queue.py
+++ b/tests/test_delayed_queue.py
@@ -27,6 +27,7 @@
     # 2.10 instead of 2.05 for slow macOS slaves on Travis
     assert 2.10 > elapsed > 1.99
 
+
 def test_nondelayed_get():
     q = DelayedQueue(2)
     q.put("", False)
diff --git a/tests/test_fsevents.py b/tests/test_fsevents.py
index fd044c2..27fa6dd 100644
--- a/tests/test_fsevents.py
+++ b/tests/test_fsevents.py
@@ -3,7 +3,7 @@
 import pytest
 from watchdog.utils import platform
 
-if not platform.is_darwin():
+if not platform.is_darwin():  # noqa
     pytest.skip("macOS only.", allow_module_level=True)
 
 import logging
diff --git a/tests/test_inotify_buffer.py b/tests/test_inotify_buffer.py
index 0c69f38..24d467e 100644
--- a/tests/test_inotify_buffer.py
+++ b/tests/test_inotify_buffer.py
@@ -19,8 +19,8 @@
 import pytest
 from watchdog.utils import platform
 
-if not platform.is_linux():
-    pytest.skip("GNU/Linux only.", allow_module_level=True)
+if not platform.is_linux():  # noqa
+    pytest.skip("GNU/Linux only.", allow_module_level=True)  # noqa
 
 import os
 import random
diff --git a/tests/test_inotify_c.py b/tests/test_inotify_c.py
index 912195a..3de1853 100644
--- a/tests/test_inotify_c.py
+++ b/tests/test_inotify_c.py
@@ -3,7 +3,7 @@
 import pytest
 from watchdog.utils import platform
 
-if not platform.is_linux():
+if not platform.is_linux():  # noqa
     pytest.skip("GNU/Linux only.", allow_module_level=True)
 
 import contextlib
@@ -54,27 +54,27 @@
 
 
 def test_late_double_deletion(monkeypatch):
-    inotify_fd = type(str("FD"), (object,), {})() # Empty object
+    inotify_fd = type(str("FD"), (object,), {})()  # Empty object
     inotify_fd.last = 0
     inotify_fd.wds = []
 
     # CREATE DELETE CREATE DELETE DELETE_SELF IGNORE DELETE_SELF IGNORE
     inotify_fd.buf = (
-      # IN_CREATE|IS_DIR (wd = 1, path = subdir1)
-      b"\x01\x00\x00\x00\x00\x01\x00\x40\x00\x00\x00\x00\x10\x00\x00\x00"
-      b"\x73\x75\x62\x64\x69\x72\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-      # IN_DELETE|IS_DIR (wd = 1, path = subdir1)
-      b"\x01\x00\x00\x00\x00\x02\x00\x40\x00\x00\x00\x00\x10\x00\x00\x00"
-      b"\x73\x75\x62\x64\x69\x72\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        # IN_CREATE|IS_DIR (wd = 1, path = subdir1)
+        b"\x01\x00\x00\x00\x00\x01\x00\x40\x00\x00\x00\x00\x10\x00\x00\x00"
+        b"\x73\x75\x62\x64\x69\x72\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        # IN_DELETE|IS_DIR (wd = 1, path = subdir1)
+        b"\x01\x00\x00\x00\x00\x02\x00\x40\x00\x00\x00\x00\x10\x00\x00\x00"
+        b"\x73\x75\x62\x64\x69\x72\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00"
     ) * 2 + (
-      # IN_DELETE_SELF (wd = 2)
-      b"\x02\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-      # IN_IGNORE (wd = 2)
-      b"\x02\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-      # IN_DELETE_SELF (wd = 3)
-      b"\x03\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-      # IN_IGNORE (wd = 3)
-      b"\x03\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        # IN_DELETE_SELF (wd = 2)
+        b"\x02\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        # IN_IGNORE (wd = 2)
+        b"\x02\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        # IN_DELETE_SELF (wd = 3)
+        b"\x03\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        # IN_IGNORE (wd = 3)
+        b"\x03\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
     )
 
     os_read_bkp = os.read
diff --git a/tests/test_observer.py b/tests/test_observer.py
index f40570d..4d3e967 100644
--- a/tests/test_observer.py
+++ b/tests/test_observer.py
@@ -32,6 +32,7 @@
     except RuntimeError:
         pass
 
+
 @pytest.fixture
 def observer2():
     obs = BaseObserver(EventEmitter)
diff --git a/tests/test_observers_winapi.py b/tests/test_observers_winapi.py
index a08e10d..5bc4977 100644
--- a/tests/test_observers_winapi.py
+++ b/tests/test_observers_winapi.py
@@ -19,7 +19,7 @@
 import pytest
 from watchdog.utils import platform
 
-if not platform.is_windows():
+if not platform.is_windows():  # noqa
     pytest.skip("Windows only.", allow_module_level=True)
 
 import os.path
diff --git a/tests/test_snapshot_diff.py b/tests/test_snapshot_diff.py
index 00e08f8..1f8dbe5 100644
--- a/tests/test_snapshot_diff.py
+++ b/tests/test_snapshot_diff.py
@@ -78,6 +78,7 @@
     assert diff.files_deleted == [p('dir2', 'b')]
     assert diff.files_created == []
 
+
 def test_dir_modify_on_create(p):
     ref = DirectorySnapshot(p(''))
     wait()
diff --git a/tests/test_watchmedo.py b/tests/test_watchmedo.py
index a4ecd7d..e865ea8 100644
--- a/tests/test_watchmedo.py
+++ b/tests/test_watchmedo.py
@@ -2,10 +2,11 @@
 from __future__ import unicode_literals
 
 import pytest
+
 # Skip if import PyYAML failed. PyYAML missing possible because
-# watchdog installed without watchmedo. See Installation section 
+# watchdog installed without watchmedo. See Installation section
 # in README.rst
-yaml = pytest.importorskip('yaml')
+yaml = pytest.importorskip('yaml')  # noqa
 
 import os
 
diff --git a/tools/dump_fsevents_constants.py b/tools/dump_fsevents_constants.py
index 3d27459..a8c4ebf 100755
--- a/tools/dump_fsevents_constants.py
+++ b/tools/dump_fsevents_constants.py
@@ -39,6 +39,7 @@
     with open(filename, 'wb') as f:
         f.write(content)
 
+
 if __name__ == "__main__":
     if len(sys.argv) > 1:
         output_file = sys.argv[1]
diff --git a/tox.ini b/tox.ini
index 5c85d45..4abe32d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,10 +1,13 @@
 [tox]
-envlist = py{37,36,35,27,py3,py}
+envlist = py{38,37,36,35,27,py3,py}
+skip_missing_interpreters = True
 
 [testenv]
 deps =
-    py{37,36,35,27,py3,py}: pytest-cov
-    py{37,36,35,27,py3,py}: pytest-timeout
+    flake8
+    pytest-cov
+    pytest-timeout
 extras = watchmedo
 commands =
+    python -m flake8 docs tools src tests
     python -bb -m pytest {posargs}