Get mypy running on lint
diff --git a/tools/lint/lint.py b/tools/lint/lint.py
index 19d0c11..dd06e0a 100644
--- a/tools/lint/lint.py
+++ b/tools/lint/lint.py
@@ -4,6 +4,7 @@
 import argparse
 import ast
 import json
+import logging
 import os
 import re
 import subprocess
@@ -19,12 +20,15 @@
 from ..wpt import testfiles
 from ..manifest.vcs import walk
 
-from manifest.sourcefile import SourceFile, js_meta_re, python_meta_re, space_chars, get_any_variants, get_default_any_variants
-from six import binary_type, iteritems, itervalues
+from ..manifest.sourcefile import SourceFile, js_meta_re, python_meta_re, space_chars, get_any_variants, get_default_any_variants
+from six import binary_type, iteritems, itervalues, with_metaclass
 from six.moves import range
 from six.moves.urllib.parse import urlsplit, urljoin
 
-import logging
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import Type
 
 logger = None
 
@@ -330,7 +334,7 @@
     return [item for i, item in enumerate(errors) if not whitelisted[i]]
 
 
-regexps = [item() for item in
+regexps = [item() for item in  # type: ignore
            [rules.TrailingWhitespaceRegexp,
             rules.TabsRegexp,
             rules.CRRegexp,
@@ -503,9 +507,11 @@
 
     return errors
 
-class ASTCheck(object):
-    __metaclass__ = abc.ABCMeta
-    rule = None
+class ASTCheck(with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def rule(self):
+        # type: () -> Type[rules.Rule]
+        pass
 
     @abc.abstractmethod
     def check(self, root):
diff --git a/tools/lint/rules.py b/tools/lint/rules.py
index 78f9d07..76d5d3e 100644
--- a/tools/lint/rules.py
+++ b/tools/lint/rules.py
@@ -1,11 +1,29 @@
 from __future__ import unicode_literals
+
+import abc
 import os
 import re
 
-class Rule(object):
-    name = None
-    description = None
-    to_fix = None
+import six
+
+MYPY = False
+if MYPY:
+    # MYPY is set to True when run under Mypy.
+    from typing import List, Optional, Pattern, Text, Match
+
+
+class Rule(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def name(self):
+        # type: () -> Text
+        pass
+
+    @abc.abstractproperty
+    def description(self):
+        # type: () -> Text
+        pass
+
+    to_fix = None  # type: Optional[Text]
 
     @classmethod
     def error(cls, path, context=(), line_no=None):
@@ -232,19 +250,30 @@
     description = "Metadata comment is not formatted correctly"
 
 
-class Regexp(Rule):
-    pattern = None
-    file_extensions = None
-    _re = None
+class Regexp(six.with_metaclass(abc.ABCMeta)):
+    @abc.abstractproperty
+    def pattern(self):
+        # type: () -> bytes
+        pass
+
+    @abc.abstractproperty
+    def description(self):
+        # type: () -> Text
+        pass
+
+    file_extensions = None  # type: Optional[List[Text]]
 
     def __init__(self):
-        self._re = re.compile(self.pattern)
+        # type: () -> None
+        self._re = re.compile(self.pattern)  # type: Pattern
 
     def applies(self, path):
+        # type: (str) -> bool
         return (self.file_extensions is None or
                 os.path.splitext(path)[1] in self.file_extensions)
 
     def search(self, line):
+        # type: (bytes) -> Optional[Match[bytes]]
         return self._re.search(line)
 
 
diff --git a/tools/manifest/update.py b/tools/manifest/update.py
index f1a7093..013ec21 100755
--- a/tools/manifest/update.py
+++ b/tools/manifest/update.py
@@ -2,7 +2,7 @@
 import argparse
 import os
 
-import manifest
+from . import manifest
 from . import vcs
 from .log import get_logger
 from .download import download_from_github
diff --git a/tools/manifest/vcs.py b/tools/manifest/vcs.py
index 9a6726c..d900012 100644
--- a/tools/manifest/vcs.py
+++ b/tools/manifest/vcs.py
@@ -78,7 +78,7 @@
 
 class FileSystem(object):
     def __init__(self, root, url_base, cache_path, manifest_path=None, rebuild=False):
-        from gitignore import gitignore
+        from gitignore import gitignore  # type: ignore
         self.root = os.path.abspath(root)
         self.url_base = url_base
         self.ignore_cache = None
diff --git a/tools/mypy.ini b/tools/mypy.ini
index 6a0fdce..710df96 100644
--- a/tools/mypy.ini
+++ b/tools/mypy.ini
@@ -23,7 +23,3 @@
 
 [mypy-zstandard.*]
 ignore_missing_imports = True
-
-[mypy-.lint.*]
-# this is weird
-ignore_missing_imports = True
diff --git a/tools/tox.ini b/tools/tox.ini
index 5706440..d0b1864 100644
--- a/tools/tox.ini
+++ b/tools/tox.ini
@@ -24,6 +24,7 @@
 
 [testenv:py36-mypy]
 deps = -rrequirements_mypy.txt
+changedir = {toxinidir}/..
 commands =
-  mypy --config-file={toxinidir}/mypy.ini --no-incremental --py2 -p manifest
-  mypy --config-file={toxinidir}/mypy.ini --no-incremental -p manifest
+  mypy --config-file={toxinidir}/mypy.ini --no-incremental --py2 -p tools.manifest -p tools.lint -p tools.gitignore
+  mypy --config-file={toxinidir}/mypy.ini --no-incremental -p tools.manifest -p tools.lint -p tools.gitignore
diff --git a/tools/wpt/testfiles.py b/tools/wpt/testfiles.py
index 7f0f221..09dd45b 100644
--- a/tools/wpt/testfiles.py
+++ b/tools/wpt/testfiles.py
@@ -185,7 +185,7 @@
     # Delay import after localpaths sets up sys.path, because otherwise the
     # import path will be "..manifest" and Python will treat it as a different
     # manifest module.
-    from manifest import manifest
+    from manifest import manifest  # type: ignore
     if manifest_path is None:
         manifest_path = os.path.join(wpt_root, "MANIFEST.json")
     return manifest.load_and_update(wpt_root, manifest_path, "/",