Repository: netromdk/vermin

Issue:
Handling Backported Packages
**Is your feature request related to a problem? Please describe.**
When using the [backported `typing` module](https://pypi.org/project/typing/) vermin indicates the files using it are not compatible with any version of py2. This is not correct, as this module is compatible with 2.7+ and 3.4+. It is flagging this usage of `typing` because it was not included in the standard library until version [3.5](https://docs.python.org/3/library/typing.html?highlight=typing#module-typing).

**Describe the solution you'd like**
One solution we were discussing was giving `vermin` the capability to parse a projects `requirements.txt` and give the user the ability to disable rules based on conflicts in the `requirements.txt`

However after discussing this with some of my co-workers, (I knew I was missing something obvious), the package name specified to PyPI doesn't necessarily have anything to do with the imported module name. Simply parsing the `requirements.txt` isn't a robust solution.

**Describe alternatives you've considered**
I'm at a loss here for good ideas. I would really like to support something as official as a backport of a standard library. Perhaps `vermin` could classify certain errors as warning instead, and caution the user that they need to have `typing` installed to work with py2. Have you considered anything along those lines? A lot of other static analysis tools make a clear distinction between different types of issues. (Critical, Error, Warning, Info, etc.)

**Additional context**
This feature is mostly stemming from my usage of `vermin`, which is primarily to analyze applications and collect statistics about their py2/3 compatibility.


Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 11170e3..762fe5a 100644
--- a/README.rst
+++ b/README.rst
@@ -33,7 +33,9 @@ function, function and variable annotations, ``continue`` in ``finally`` block,
 user-defined functions, classes, arguments, and variables with names that clash with library-defined
 symbols.
 
-The project is fairly well-tested with **1321** unit and integration tests.
+Backports of the standard library, like ``typing``, can be enabled for better results.
+
+The project is fairly well-tested with **1327** unit and integration tests.
 
 Usage
 =====
@@ -109,6 +111,10 @@ Examples
     --versions
           In the end, print all unique versions required by the analysed code.
 
+    --no-tips
+          Don't show any helpful tips at the end, like those relating to backports or
+          lax mode.
+
     [--exclude <name>] ...
           Exclude full names, like 'email.parser.FeedParser', from analysis. Useful to
           ignore conditional logic that can trigger incompatible results. It's more fine
@@ -124,6 +130,18 @@ Examples
           Exclude full names like --exclude but from a specified file instead. Each line
           constitutes an exclusion with the same format as with --exclude.
 
+    [--backport <name>] ...
+          Some features are sometimes backported into packages, in repositories such as
+          PyPi, that are widely used but aren't in the standard language. If such a
+          backport is specified as being used, the results will reflect that instead.
+
+          Supported backports:
+            argparse     - https://pypi.org/project/argparse/
+            configparser - https://pypi.org/project/configparser/
+            enum         - https://pypi.org/project/enum34/
+            faulthandler - https://pypi.org/project/faulthandler/
+            typing       - https://pypi.org/project/typing/
+
   Results interpretation:
     ~2       No known reason it won't work with py2.
     !2       It is known that it won't work with py2.
diff --git a/count.py b/count.py
index 5933729..8176798 100755
--- a/count.py
+++ b/count.py
@@ -2,7 +2,7 @@
 from vermin import MOD_REQS, MOD_MEM_REQS, KWARGS_REQS, STRFTIME_REQS, ARRAY_TYPECODE_REQS,\
   CODECS_ERROR_HANDLERS, CODECS_ENCODINGS
 (mods, mems, kwargs, dirs, typecodes, codecshandlers, codecsencs) =\
-  (len(MOD_REQS), len(MOD_MEM_REQS), len(KWARGS_REQS), len(STRFTIME_REQS),
+  (len(MOD_REQS()), len(MOD_MEM_REQS()), len(KWARGS_REQS), len(STRFTIME_REQS),
    len(ARRAY_TYPECODE_REQS), len(CODECS_ERROR_HANDLERS), len(CODECS_ENCODINGS))
 print("Rules:\n{:>4} modules\n{:>4} members\n{:>4} kwargs\n{:>4} strftime directives\n{:>4} array"
       " typecodes\n{:>4} codecs error handlers\n{:>4} codecs encodings\n{:>4} total".
diff --git a/vermin/__init__.py b/vermin/__init__.py
index da981bd..430b2fc 100644
--- a/vermin/__init__.py
+++ b/vermin/__init__.py
@@ -8,10 +8,12 @@ from .rules import MOD_REQS, MOD_MEM_REQS, KWARGS_REQS, STRFTIME_REQS, ARRAY_TYP
 from .arguments import Arguments
 from .config import Config
 from .utility import reverse_range, dotted_name, combine_versions, InvalidVersionException
+from .backports import Backports
 
 __all__ = [
   "ARRAY_TYPECODE_REQS",
   "Arguments",
+  "Backports",
   "CODECS_ENCODINGS",
   "CODECS_ERROR_HANDLERS",
   "Config",
diff --git a/vermin/arguments.py b/vermin/arguments.py
index e27df40..9ec7151 100644
--- a/vermin/arguments.py
+++ b/vermin/arguments.py
@@ -4,6 +4,7 @@ from multiprocessing import cpu_count
 
 from .constants import VERSION
 from .config import Config
+from .backports import Backports
 
 TARGETS_SPLIT = re.compile("[\\.,]")
 
@@ -50,6 +51,9 @@ class Arguments:
           "        not specified directly).")
     print("\n  --versions\n"
           "        In the end, print all unique versions required by the analysed code.")
+    print("\n  --no-tips\n"
+          "        Don't show any helpful tips at the end, like those relating to backports or\n"
+          "        lax mode.")
     # TODO: Argument for specifying files and folders to ignore from detection/parsing.
     print("\n  [--exclude <name>] ...\n"
           "        Exclude full names, like 'email.parser.FeedParser', from analysis. Useful to\n"
@@ -63,6 +67,11 @@ class Arguments:
     print("\n  [--exclude-file <file name>] ...\n"
           "        Exclude full names like --exclude but from a specified file instead. Each line\n"
           "        constitutes an exclusion with the same format as with --exclude.")
+    print("\n  [--backport <name>] ...\n"
+          "        Some features are sometimes backported into packages, in repositories such as\n"
+          "        PyPi, that are widely used but aren't in the standard language. If such a\n"
+          "        backport is specified as being used, the results will reflect that instead.\n\n"
+          "        Supported backports:\n{}".format(Backports.str(10)))
     print("\nResults interpretation:")
     print("  ~2       No known reason it won't work with py2.")
     print("  !2       It is known that it won't work with py2.")
@@ -79,6 +88,7 @@ class Arguments:
     targets = []
     hidden = False
     versions = False
+    no_tips = False
     for i in range(len(self.__args)):
       arg = self.__args[i].lower()
       if arg == "-h" or arg == "--help":
@@ -152,6 +162,9 @@ class Arguments:
       elif arg == "--versions":
         versions = True
         path_pos += 1
+      elif arg == "--no-tips":
+        no_tips = True
+        path_pos += 1
       elif arg == "--exclude":
         if (i + 1) >= len(self.__args):
           print("Exclusion requires a name! Example: --exclude email.parser.FeedParser")
@@ -164,6 +177,15 @@ class Arguments:
           return {"code": 1}
         config.add_exclusion_file(self.__args[i + 1])
         path_pos += 2
+      elif arg == "--backport":
+        if (i + 1) >= len(self.__args):
+          print("Requires a backport name! Example: --backport typing")
+          return {"code": 1}
+        name = self.__args[i + 1]
+        if not config.add_backport(name):
+          print("Unknown backport: {}".format(name))
+          return {"code": 1}
+        path_pos += 2
 
     if config.quiet() and config.verbose() > 0:
       print("Cannot use quiet and verbose modes together!")
@@ -182,4 +204,5 @@ class Arguments:
             "processes": processes,
             "targets": targets,
             "hidden": hidden,
-            "versions": versions}
+            "versions": versions,
+            "no-tips": no_tips}
diff --git a/vermin/backports.py b/vermin/backports.py
new file mode 100644
index 0000000..0512e30
--- /dev/null
+++ b/vermin/backports.py
@@ -0,0 +1,24 @@
+BACKPORTS = (
+  ("argparse", "https://pypi.org/project/argparse/"),
+  ("configparser", "https://pypi.org/project/configparser/"),
+  ("enum", "https://pypi.org/project/enum34/"),
+  ("faulthandler", "https://pypi.org/project/faulthandler/"),
+  ("typing", "https://pypi.org/project/typing/"),
+)
+
+class Backports:
+  @staticmethod
+  def str(indent=0):
+    res = []
+    longest = len(max({"typing", "configparser", "enum", "faulthandler"}, key=lambda x: len(x)))
+    for (mod, link) in BACKPORTS:
+      res.append("{}{:{fill}} - {}".format(" " * indent, mod, link, fill=longest))
+    return "\n".join(res)
+
+  @staticmethod
+  def modules():
+    return {mod for (mod, link) in BACKPORTS}
+
+  @staticmethod
+  def is_backport(module):
+    return module in Backports.modules()
diff --git a/vermin/config.py b/vermin/config.py
index eed2d8f..36c18f4 100644
--- a/vermin/config.py
+++ b/vermin/config.py
@@ -1,3 +1,5 @@
+from .backports import Backports
+
 class Config:
   __instance = None
 
@@ -19,6 +21,7 @@ class Config:
     self.__ignore_incomp = False
     self.__lax_mode = False
     self.__exclusions = set()
+    self.__backports = set()
 
   def quiet(self):
     return self.__quiet
@@ -77,3 +80,12 @@ class Config:
 
   def is_excluded_codecs_encoding(self, name):
     return "ce={}".format(name) in self.__exclusions
+
+  def add_backport(self, name):
+    if not Backports.is_backport(name):
+      return False
+    self.__backports.add(name)
+    return True
+
+  def backports(self):
+    return self.__backports
diff --git a/vermin/main.py b/vermin/main.py
index 7477440..6fda1e3 100644
--- a/vermin/main.py
+++ b/vermin/main.py
@@ -21,6 +21,7 @@ def main():
 
   processes = args["processes"]
   targets = args["targets"]
+  no_tips = args["no-tips"]
 
   # Detect paths, remove duplicates, and sort for deterministic results.
   vprint("Detecting python files..")
@@ -40,7 +41,7 @@ def main():
 
   try:
     processor = Processor()
-    (mins, incomp, unique_versions) = processor.process(paths, processes)
+    (mins, incomp, unique_versions, backports) = processor.process(paths, processes)
   except KeyboardInterrupt:
     print("Aborting..")
     sys.exit(1)
@@ -60,9 +61,15 @@ def main():
   if len(reqs) == 0 and len(incomps) == 0:
     print("No known reason found that it will not work with 2+ and 3+.")
     print("Please report if it does not: https://github.com/netromdk/vermin/issues/")
-    if config.lax_mode():
+    if config.lax_mode() and not no_tips:
       print("Tip: Try without using lax mode for more thorough analysis.")
 
+  unique_bps = backports - config.backports()
+  if len(unique_bps) > 0 and not no_tips:
+    print("Tip: You're using potentially backported modules: {}".format(", ".join(unique_bps)))
+    print("If so, try using the following for better results: {}\n".
+          format("".join([" --backport {}".format(n) for n in unique_bps]).strip()))
+
   if len(reqs) > 0:
     print("Minimum required versions: {}".format(version_strings(reqs)))
 
diff --git a/vermin/processor.py b/vermin/processor.py
index c1d1b34..0a7128b 100644
--- a/vermin/processor.py
+++ b/vermin/processor.py
@@ -5,6 +5,7 @@ from .config import Config
 from .utility import combine_versions, InvalidVersionException, version_strings
 from .parser import Parser
 from .source_visitor import SourceVisitor
+from .backports import Backports
 
 # NOTE: This function isn't in the Processor class because Python 2's multiprocessing.pool doesn't
 # like it being an instance method:
@@ -22,27 +23,28 @@ def process_individual(args):
   mins = None
   text = ""
   novermin = set()
+  bps = set()
   with open(path, mode="rb") as fp:
     try:
       parser = Parser(fp.read(), path)
       (node, mins, novermin) = parser.detect()
     except KeyboardInterrupt:
-      return (path, mins, text)
+      return (path, mins, text, bps)
 
     # When input isn't python code, ignore it.
     except ValueError:
       # source code string cannot contain null bytes
-      return (None, None, None)
+      return (None, None, None, set())
     except TypeError:
       # compile() expected string without null bytes
-      return (None, None, None)
+      return (None, None, None, set())
 
     except Exception as ex:
       text = "{}: {}, {}".format(path, type(ex), ex)
       mins = [(0, 0), (0, 0)]
 
   if node is None:
-    return (path, mins, text)
+    return (path, mins, text, bps)
 
   visitor = SourceVisitor(config)
   visitor.set_no_lines(novermin)
@@ -50,16 +52,19 @@ def process_individual(args):
   try:
     visitor.visit(node)
   except KeyboardInterrupt:
-    return (path, mins, text)
+    return (path, mins, text, bps)
 
   try:
     mins = visitor.minimum_versions()
     text = visitor.output_text()
+    for m in visitor.modules():
+      if Backports.is_backport(m):
+        bps.add(m)
   except InvalidVersionException as ex:
     mins = None
     text = str(ex)
 
-  return (path, mins, text)
+  return (path, mins, text, bps)
 
 class Processor:
   def process(self, paths, processes=cpu_count()):
@@ -73,7 +78,8 @@ class Processor:
         nprint("File with incompatible versions: {}".format(path))
 
     unique_versions = set()
-    for (path, min_versions, text) in \
+    all_backports = set()
+    for (path, min_versions, text, bps) in \
           pool.imap(process_individual, ((path, config) for path in paths)):
       # Ignore paths that didn't contain python code.
       if path is None and min_versions is None and text is None:
@@ -84,6 +90,8 @@ class Processor:
         print_incomp(path)
         continue
 
+      all_backports = all_backports | bps
+
       for ver in min_versions:
         if ver is not None and ver > (0, 0):
           unique_versions.add(ver)
@@ -109,4 +117,4 @@ class Processor:
 
     unique_versions = list(unique_versions)
     unique_versions.sort()
-    return (mins, incomp, unique_versions)
+    return (mins, incomp, unique_versions, all_backports)
diff --git a/vermin/rules.py b/vermin/rules.py
index d186592..9ffccc6 100644
--- a/vermin/rules.py
+++ b/vermin/rules.py
@@ -1,1013 +1,1021 @@
+from .config import Config
+
+def bp(name):
+  """Checking if backport is used."""
+  return name in Config.get().backports()
+
 # Module requirements: name -> min version per major or None if N.A.
-MOD_REQS = {
-  "ConfigParser": ((2, 0), None),
-  "DocXMLRPCServer": ((2, 3), None),
-  "HTMLParser": ((2, 2), None),
-  "Queue": ((2, 0), None),
-  "SimpleXMLRPCServer": ((2, 2), None),
-  "SocketServer": ((2, 0), None),
-  "Tkinter": ((2, 0), None),
-  "__builtin__": ((2, 0), None),
-  "__future__": ((2, 1), (3, 0)),
-  "_dummy_thread": (None, (3, 0)),
-  "_markupbase": (None, (3, 0)),
-  "_winreg": ((2, 0), None),
-  "abc": ((2, 6), (3, 0)),
-  "argparse": ((2, 7), (3, 2)),
-  "ast": ((2, 6), (3, 0)),
-  "asyncio": (None, (3, 4)),
-  "atexit": ((2, 0), (3, 0)),
-  "builtins": (None, (3, 0)),
-  "bz2": ((2, 3), (3, 0)),
-  "cProfile": ((2, 5), (3, 0)),
-  "cgitb": ((2, 2), (3, 0)),
-  "collections": ((2, 4), (3, 0)),
-  "collections.abc": (None, (3, 3)),
-  "concurrent.futures": (None, (3, 2)),
-  "configparser": (None, (3, 0)),
-  "contextlib": ((2, 5), (3, 0)),
-  "contextvars": (None, (3, 7)),
-  "cookielib": ((2, 4), None),
-  "copy_reg": ((2, 0), None),
-  "copyreg": (None, (3, 0)),
-  "csv": ((2, 3), (3, 0)),
-  "ctypes": ((2, 5), (3, 0)),
-  "dataclasses": (None, (3, 7)),
-  "datetime": ((2, 3), (3, 0)),
-  "dbm.dumb": (None, (3, 0)),
-  "dbm.gnu": (None, (3, 0)),
-  "dbm.io": (None, (3, 0)),
-  "dbm.ndbm": (None, (3, 0)),
-  "dbm.os": (None, (3, 0)),
-  "dbm.struct": (None, (3, 0)),
-  "dbm.sys": (None, (3, 0)),
-  "dbm.whichdb": (None, (3, 0)),
-  "decimal": ((2, 4), (3, 0)),
-  "difflib": ((2, 1), (3, 0)),
-  "dummy_thread": ((2, 3), None),
-  "dummy_threading": ((2, 3), None),
-  "email": ((2, 2), (3, 0)),
-  "email.charset": ((2, 2), (3, 0)),
-  "email.contentmanager": (None, (3, 6)),
-  "email.header": ((2, 2), (3, 0)),
-  "email.headerregistry": (None, (3, 6)),
-  "email.policy": (None, (3, 3)),
-  "ensurepip": ((2, 7), (3, 4)),
-  "enum": (None, (3, 4)),
-  "faulthandler": (None, (3, 3)),
-  "fractions": ((2, 6), (3, 0)),
-  "functools": ((2, 5), (3, 0)),
-  "future_builtins": ((2, 6), None),
-  "hashlib": ((2, 5), (3, 0)),
-  "heapq": ((2, 3), (3, 0)),
-  "hmac": ((2, 2), (3, 0)),
-  "hotshot": ((2, 2), None),
-  "html": (None, (3, 0)),
-  "htmlentitydefs": ((2, 0), None),
-  "http": (None, (3, 0)),
-  "http.cookiejar": (None, (3, 0)),
-  "importlib": ((2, 7), (3, 1)),
-  "importlib.metadata": (None, (3, 8)),
-  "importlib.resources": (None, (3, 7)),
-  "inspect": ((2, 1), (3, 0)),
-  "io": ((2, 6), (3, 0)),
-  "ipaddress": (None, (3, 3)),
-  "itertools": ((2, 3), (3, 0)),
-  "json": ((2, 6), (3, 0)),
-  "logging": ((2, 3), (3, 0)),
-  "lzma": (None, (3, 3)),
-  "markupbase": ((2, 0), None),
-  "md5": ((2, 0), None),
-  "modulefinder": ((2, 3), (3, 0)),
-  "msilib": ((2, 5), (3, 0)),
-  "multiprocessing": ((2, 6), (3, 0)),
-  "multiprocessing.shared_memory": (None, (3, 8)),
-  "new": ((2, 0), None),
-  "numbers": ((2, 6), (3, 0)),
-  "optparse": ((2, 3), (3, 0)),
-  "ossaudiodev": ((2, 3), (3, 0)),
-  "pathlib": (None, (3, 4)),
-  "pickletools": ((2, 3), (3, 0)),
-  "pkgutil": ((2, 3), (3, 0)),
-  "platform": ((2, 3), (3, 0)),
-  "pydoc": ((2, 1), (3, 0)),
-  "queue": (None, (3, 0)),
-  "repr": ((2, 0), None),
-  "reprlib": (None, (3, 0)),
-  "runpy": ((2, 5), (3, 0)),
-  "secrets": (None, (3, 6)),
-  "selectors": (None, (3, 4)),
-  "sets": ((2, 3), None),
-  "shlex": ((2, 0), (3, 0)),
-  "socketserver": (None, (3, 0)),
-  "spwd": ((2, 5), (3, 0)),
-  "sqlite3": ((2, 5), (3, 0)),
-  "ssl": ((2, 6), (3, 0)),
-  "statistics": (None, (3, 4)),
-  "string.letters": ((2, 0), None),
-  "string.lowercase": ((2, 0), None),
-  "string.uppercase": ((2, 0), None),
-  "stringprep": ((2, 3), (3, 0)),
-  "subprocess": ((2, 4), (3, 0)),
-  "sysconfig": ((2, 7), (3, 2)),
-  "tarfile": ((2, 3), (3, 0)),
-  "textwrap": ((2, 3), (3, 0)),
-  "timeit": ((2, 3), (3, 0)),
-  "tkinter": (None, (3, 0)),
-  "tracemalloc": (None, (3, 4)),
-  "typing": (None, (3, 5)),
-  "unittest": ((2, 1), (3, 0)),
-  "unittest.mock": (None, (3, 3)),
-  "urllib2": ((2, 0), None),
-  "urlparse": ((2, 0), None),
-  "uuid": ((2, 5), (3, 0)),
-  "venv": (None, (3, 3)),
-  "warnings": ((2, 1), (3, 0)),
-  "weakref": ((2, 1), (3, 0)),
-  "winreg": (None, (3, 0)),
-  "wsgiref": ((2, 5), (3, 0)),
-  "xmlrpc": (None, (3, 0)),
-  "xmlrpc.client": (None, (3, 0)),
-  "xmlrpc.server": (None, (3, 0)),
-  "xmlrpclib": ((2, 2), None),
-  "zipapp": (None, (3, 5)),
-  "zipimport": ((2, 3), (3, 0)),
-}
+def MOD_REQS():
+  return {
+    "ConfigParser": ((2, 0), None),
+    "DocXMLRPCServer": ((2, 3), None),
+    "HTMLParser": ((2, 2), None),
+    "Queue": ((2, 0), None),
+    "SimpleXMLRPCServer": ((2, 2), None),
+    "SocketServer": ((2, 0), None),
+    "Tkinter": ((2, 0), None),
+    "__builtin__": ((2, 0), None),
+    "__future__": ((2, 1), (3, 0)),
+    "_dummy_thread": (None, (3, 0)),
+    "_markupbase": (None, (3, 0)),
+    "_winreg": ((2, 0), None),
+    "abc": ((2, 6), (3, 0)),
+    "argparse": ((2, 7), (3, 2)) if not bp("argparse") else ((2, 3), (3, 1)),
+    "ast": ((2, 6), (3, 0)),
+    "asyncio": (None, (3, 4)),
+    "atexit": ((2, 0), (3, 0)),
+    "builtins": (None, (3, 0)),
+    "bz2": ((2, 3), (3, 0)),
+    "cProfile": ((2, 5), (3, 0)),
+    "cgitb": ((2, 2), (3, 0)),
+    "collections": ((2, 4), (3, 0)),
+    "collections.abc": (None, (3, 3)),
+    "concurrent.futures": (None, (3, 2)),
+    "configparser": (None if not bp("configparser") else (2, 6), (3, 0)),
+    "contextlib": ((2, 5), (3, 0)),
+    "contextvars": (None, (3, 7)),
+    "cookielib": ((2, 4), None),
+    "copy_reg": ((2, 0), None),
+    "copyreg": (None, (3, 0)),
+    "csv": ((2, 3), (3, 0)),
+    "ctypes": ((2, 5), (3, 0)),
+    "dataclasses": (None, (3, 7)),
+    "datetime": ((2, 3), (3, 0)),
+    "dbm.dumb": (None, (3, 0)),
+    "dbm.gnu": (None, (3, 0)),
+    "dbm.io": (None, (3, 0)),
+    "dbm.ndbm": (None, (3, 0)),
+    "dbm.os": (None, (3, 0)),
+    "dbm.struct": (None, (3, 0)),
+    "dbm.sys": (None, (3, 0)),
+    "dbm.whichdb": (None, (3, 0)),
+    "decimal": ((2, 4), (3, 0)),
+    "difflib": ((2, 1), (3, 0)),
+    "dummy_thread": ((2, 3), None),
+    "dummy_threading": ((2, 3), None),
+    "email": ((2, 2), (3, 0)),
+    "email.charset": ((2, 2), (3, 0)),
+    "email.contentmanager": (None, (3, 6)),
+    "email.header": ((2, 2), (3, 0)),
+    "email.headerregistry": (None, (3, 6)),
+    "email.policy": (None, (3, 3)),
+    "ensurepip": ((2, 7), (3, 4)),
+    "enum": (None, (3, 4)) if not bp("enum") else ((2, 4), (3, 3)),
+    "faulthandler": (None, (3, 3)) if not bp("faulthandler") else ((2, 6), (3, 0)),
+    "fractions": ((2, 6), (3, 0)),
+    "functools": ((2, 5), (3, 0)),
+    "future_builtins": ((2, 6), None),
+    "hashlib": ((2, 5), (3, 0)),
+    "heapq": ((2, 3), (3, 0)),
+    "hmac": ((2, 2), (3, 0)),
+    "hotshot": ((2, 2), None),
+    "html": (None, (3, 0)),
+    "htmlentitydefs": ((2, 0), None),
+    "http": (None, (3, 0)),
+    "http.cookiejar": (None, (3, 0)),
+    "importlib": ((2, 7), (3, 1)),
+    "importlib.metadata": (None, (3, 8)),
+    "importlib.resources": (None, (3, 7)),
+    "inspect": ((2, 1), (3, 0)),
+    "io": ((2, 6), (3, 0)),
+    "ipaddress": (None, (3, 3)),
+    "itertools": ((2, 3), (3, 0)),
+    "json": ((2, 6), (3, 0)),
+    "logging": ((2, 3), (3, 0)),
+    "lzma": (None, (3, 3)),
+    "markupbase": ((2, 0), None),
+    "md5": ((2, 0), None),
+    "modulefinder": ((2, 3), (3, 0)),
+    "msilib": ((2, 5), (3, 0)),
+    "multiprocessing": ((2, 6), (3, 0)),
+    "multiprocessing.shared_memory": (None, (3, 8)),
+    "new": ((2, 0), None),
+    "numbers": ((2, 6), (3, 0)),
+    "optparse": ((2, 3), (3, 0)),
+    "ossaudiodev": ((2, 3), (3, 0)),
+    "pathlib": (None, (3, 4)),
+    "pickletools": ((2, 3), (3, 0)),
+    "pkgutil": ((2, 3), (3, 0)),
+    "platform": ((2, 3), (3, 0)),
+    "pydoc": ((2, 1), (3, 0)),
+    "queue": (None, (3, 0)),
+    "repr": ((2, 0), None),
+    "reprlib": (None, (3, 0)),
+    "runpy": ((2, 5), (3, 0)),
+    "secrets": (None, (3, 6)),
+    "selectors": (None, (3, 4)),
+    "sets": ((2, 3), None),
+    "shlex": ((2, 0), (3, 0)),
+    "socketserver": (None, (3, 0)),
+    "spwd": ((2, 5), (3, 0)),
+    "sqlite3": ((2, 5), (3, 0)),
+    "ssl": ((2, 6), (3, 0)),
+    "statistics": (None, (3, 4)),
+    "string.letters": ((2, 0), None),
+    "string.lowercase": ((2, 0), None),
+    "string.uppercase": ((2, 0), None),
+    "stringprep": ((2, 3), (3, 0)),
+    "subprocess": ((2, 4), (3, 0)),
+    "sysconfig": ((2, 7), (3, 2)),
+    "tarfile": ((2, 3), (3, 0)),
+    "textwrap": ((2, 3), (3, 0)),
+    "timeit": ((2, 3), (3, 0)),
+    "tkinter": (None, (3, 0)),
+    "tracemalloc": (None, (3, 4)),
+    "typing": (None, (3, 5)) if not bp("typing") else ((2, 7), (3, 4)),
+    "unittest": ((2, 1), (3, 0)),
+    "unittest.mock": (None, (3, 3)),
+    "urllib2": ((2, 0), None),
+    "urlparse": ((2, 0), None),
+    "uuid": ((2, 5), (3, 0)),
+    "venv": (None, (3, 3)),
+    "warnings": ((2, 1), (3, 0)),
+    "weakref": ((2, 1), (3, 0)),
+    "winreg": (None, (3, 0)),
+    "wsgiref": ((2, 5), (3, 0)),
+    "xmlrpc": (None, (3, 0)),
+    "xmlrpc.client": (None, (3, 0)),
+    "xmlrpc.server": (None, (3, 0)),
+    "xmlrpclib": ((2, 2), None),
+    "zipapp": (None, (3, 5)),
+    "zipimport": ((2, 3), (3, 0)),
+   }
 
 # Module member requirements: member -> (module, requirements)
-MOD_MEM_REQS = {
-  # Builtin classes
-  "bool": ((2, 2), (3, 0)),
-  "bytearray": ((2, 6), (3, 0)),
-  "frozenset": ((2, 4), (3, 0)),
-  "memoryview": ((2, 7), (3, 0)),
-  "object": ((2, 2), (3, 0)),
-  "set": ((2, 4), (3, 0)),
-  "type": ((2, 2), (3, 0)),
+def MOD_MEM_REQS():
+  return {
+    # Builtin classes
+    "bool": ((2, 2), (3, 0)),
+    "bytearray": ((2, 6), (3, 0)),
+    "frozenset": ((2, 4), (3, 0)),
+    "memoryview": ((2, 7), (3, 0)),
+    "object": ((2, 2), (3, 0)),
+    "set": ((2, 4), (3, 0)),
+    "type": ((2, 2), (3, 0)),
 
-  # Classes
-  "SimpleXMLRPCServer.CGIXMLRPCRequestHandler": ((2, 3), None),
-  "abc.ABC": (None, (3, 4)),
-  "collections.ChainMap": (None, (3, 3)),
-  "collections.Counter": ((2, 7), (3, 1)),
-  "collections.OrderedDict": ((2, 7), (3, 1)),
-  "collections.UserDict": (None, (3, 0)),
-  "collections.UserList": (None, (3, 0)),
-  "collections.UserString": (None, (3, 0)),
-  "collections.abc.AsyncGenerator": (None, (3, 6)),
-  "collections.abc.AsyncIterable": (None, (3, 5)),
-  "collections.abc.AsyncIterator": (None, (3, 5)),
-  "collections.abc.Awaitable": (None, (3, 5)),
-  "collections.abc.Collection": (None, (3, 6)),
-  "collections.abc.Coroutine": (None, (3, 5)),
-  "collections.abc.Generator": (None, (3, 5)),
-  "collections.abc.Reversible": (None, (3, 6)),
-  "collections.defaultdict": ((2, 5), (3, 0)),
-  "collections.deque": ((2, 4), (3, 0)),
-  "collections.namedtuple": ((2, 6), (3, 0)),
-  "contextlib.AbstractAsyncContextManager": (None, (3, 7)),
-  "contextlib.AbstractContextManager": (None, (3, 6)),
-  "contextlib.AsyncExitStack": (None, (3, 7)),
-  "contextlib.ContextDecorator": (None, (3, 2)),
-  "contextlib.ExitStack": (None, (3, 3)),
-  "csv.unix_dialect": (None, (3, 2)),
-  "ctypes.c_bool": ((2, 6), (3, 0)),
-  "ctypes.c_longdouble": ((2, 6), (3, 0)),
-  "ctypes.c_ssize_t": ((2, 7), (3, 2)),
-  "datetime.timezone": (None, (3, 2)),
-  "difflib.HtmlDiff": ((2, 4), (3, 0)),
-  "dis.Bytecode": (None, (3, 4)),
-  "email.generator.BytesGenerator": (None, (3, 2)),
-  "email.generator.DecodedGenerator": ((2, 2), (3, 0)),
-  "email.mime.application.MIMEApplication": ((2, 5), (3, 0)),
-  "email.mime.multipart.MIMEMultipart": ((2, 2), (3, 0)),
-  "email.mime.nonmultipart.MIMENonMultipart": ((2, 2), (3, 0)),
-  "email.parser.BytesFeedParser": (None, (3, 2)),
-  "email.parser.BytesHeaderParser": (None, (3, 3)),
-  "email.parser.BytesParser": (None, (3, 2)),
-  "email.parser.FeedParser": ((2, 4), (3, 0)),
-  "email.policy.EmailPolicy": (None, (3, 6)),
-  "importlib.machinery.ExtensionFileLoader": (None, (3, 3)),
-  "importlib.machinery.FileFinder": (None, (3, 3)),
-  "importlib.machinery.ModuleSpec": (None, (3, 4)),
-  "importlib.machinery.SourceFileLoader": (None, (3, 3)),
-  "importlib.machinery.SourcelessFileLoader": (None, (3, 3)),
-  "importlib.machinery.WindowsRegistryFinder": (None, (3, 3)),
-  "importlib.util.LazyLoader": (None, (3, 5)),
-  "inspect.BoundArguments": (None, (3, 3)),
-  "inspect.Parameter": (None, (3, 3)),
-  "inspect.Signature": (None, (3, 3)),
-  "json.JSONDecodeError": (None, (3, 5)),
-  "logging.LoggerAdapter": ((2, 6), (3, 0)),
-  "multiprocessing.Barrier": (None, (3, 3)),
-  "os.PathLike": (None, (3, 6)),
-  "os.terminal_size": (None, (3, 3)),
-  "pkgutil.ModuleInfo": (None, (3, 6)),
-  "plistlib.UID": (None, (3, 8)),
-  "ssl.SSLContext": ((2, 7), (3, 2)),
-  "ssl.SSLMemoryBIO": (None, (3, 6)),
-  "ssl.SSLObject": (None, (3, 6)),
-  "ssl.SSLSession": (None, (3, 6)),
-  "statistics.NormalDist": (None, (3, 8)),
-  "subprocess.CompletedProcess": (None, (3, 5)),
-  "tracemalloc.DomainFilter": (None, (3, 6)),
-  "typing.AsyncContextManager": (None, (3, 6)),
-  "typing.AsyncGenerator": (None, (3, 6)),
-  "typing.AsyncIterable": (None, (3, 5)),
-  "typing.AsyncIterator": (None, (3, 5)),
-  "typing.Awaitable": (None, (3, 5)),
-  "typing.ChainMap": (None, (3, 6)),
-  "typing.ClassVar": (None, (3, 5)),
-  "typing.Collection": (None, (3, 6)),
-  "typing.ContextManager": (None, (3, 6)),
-  "typing.Counter": (None, (3, 6)),
-  "typing.Deque": (None, (3, 6)),
-  "typing.Protocol": (None, (3, 8)),
-  "typing.SupportsIndex": (None, (3, 8)),
-  "typing.Text": (None, (3, 6)),
-  "typing.Type": (None, (3, 5)),
-  "unittest.TextTestResult": ((2, 7), (3, 0)),
-  "warnings.catch_warnings": ((2, 6), (3, 0)),
-  "weakref.WeakMethod": (None, (3, 4)),
-  "weakref.WeakSet": ((2, 7), (3, 0)),
-  "weakref.finalize": (None, (3, 4)),
-  "wsgiref.handlers.IISCGIHandler": (None, (3, 2)),
-  "xml.etree.ElementTree": ((2, 5), (3, 0)),
-  "xml.etree.ElementTree.C14NWriterTarget": (None, (3, 8)),
-  "xml.etree.ElementTree.XMLPullParser": (None, (3, 4)),
-  "xmlrpclib.MultiCall": ((2, 4), None),
+    # Classes
+    "SimpleXMLRPCServer.CGIXMLRPCRequestHandler": ((2, 3), None),
+    "abc.ABC": (None, (3, 4)),
+    "collections.ChainMap": (None, (3, 3)),
+    "collections.Counter": ((2, 7), (3, 1)),
+    "collections.OrderedDict": ((2, 7), (3, 1)),
+    "collections.UserDict": (None, (3, 0)),
+    "collections.UserList": (None, (3, 0)),
+    "collections.UserString": (None, (3, 0)),
+    "collections.abc.AsyncGenerator": (None, (3, 6)),
+    "collections.abc.AsyncIterable": (None, (3, 5)),
+    "collections.abc.AsyncIterator": (None, (3, 5)),
+    "collections.abc.Awaitable": (None, (3, 5)),
+    "collections.abc.Collection": (None, (3, 6)),
+    "collections.abc.Coroutine": (None, (3, 5)),
+    "collections.abc.Generator": (None, (3, 5)),
+    "collections.abc.Reversible": (None, (3, 6)),
+    "collections.defaultdict": ((2, 5), (3, 0)),
+    "collections.deque": ((2, 4), (3, 0)),
+    "collections.namedtuple": ((2, 6), (3, 0)),
+    "contextlib.AbstractAsyncContextManager": (None, (3, 7)),
+    "contextlib.AbstractContextManager": (None, (3, 6)),
+    "contextlib.AsyncExitStack": (None, (3, 7)),
+    "contextlib.ContextDecorator": (None, (3, 2)),
+    "contextlib.ExitStack": (None, (3, 3)),
+    "csv.unix_dialect": (None, (3, 2)),
+    "ctypes.c_bool": ((2, 6), (3, 0)),
+    "ctypes.c_longdouble": ((2, 6), (3, 0)),
+    "ctypes.c_ssize_t": ((2, 7), (3, 2)),
+    "datetime.timezone": (None, (3, 2)),
+    "difflib.HtmlDiff": ((2, 4), (3, 0)),
+    "dis.Bytecode": (None, (3, 4)),
+    "email.generator.BytesGenerator": (None, (3, 2)),
+    "email.generator.DecodedGenerator": ((2, 2), (3, 0)),
+    "email.mime.application.MIMEApplication": ((2, 5), (3, 0)),
+    "email.mime.multipart.MIMEMultipart": ((2, 2), (3, 0)),
+    "email.mime.nonmultipart.MIMENonMultipart": ((2, 2), (3, 0)),
+    "email.parser.BytesFeedParser": (None, (3, 2)),
+    "email.parser.BytesHeaderParser": (None, (3, 3)),
+    "email.parser.BytesParser": (None, (3, 2)),
+    "email.parser.FeedParser": ((2, 4), (3, 0)),
+    "email.policy.EmailPolicy": (None, (3, 6)),
+    "importlib.machinery.ExtensionFileLoader": (None, (3, 3)),
+    "importlib.machinery.FileFinder": (None, (3, 3)),
+    "importlib.machinery.ModuleSpec": (None, (3, 4)),
+    "importlib.machinery.SourceFileLoader": (None, (3, 3)),
+    "importlib.machinery.SourcelessFileLoader": (None, (3, 3)),
+    "importlib.machinery.WindowsRegistryFinder": (None, (3, 3)),
+    "importlib.util.LazyLoader": (None, (3, 5)),
+    "inspect.BoundArguments": (None, (3, 3)),
+    "inspect.Parameter": (None, (3, 3)),
+    "inspect.Signature": (None, (3, 3)),
+    "json.JSONDecodeError": (None, (3, 5)),
+    "logging.LoggerAdapter": ((2, 6), (3, 0)),
+    "multiprocessing.Barrier": (None, (3, 3)),
+    "os.PathLike": (None, (3, 6)),
+    "os.terminal_size": (None, (3, 3)),
+    "pkgutil.ModuleInfo": (None, (3, 6)),
+    "plistlib.UID": (None, (3, 8)),
+    "ssl.SSLContext": ((2, 7), (3, 2)),
+    "ssl.SSLMemoryBIO": (None, (3, 6)),
+    "ssl.SSLObject": (None, (3, 6)),
+    "ssl.SSLSession": (None, (3, 6)),
+    "statistics.NormalDist": (None, (3, 8)),
+    "subprocess.CompletedProcess": (None, (3, 5)),
+    "tracemalloc.DomainFilter": (None, (3, 6)),
+    "typing.AsyncContextManager": (None, (3, 6)),
+    "typing.AsyncGenerator": (None, (3, 6)),
+    "typing.AsyncIterable": (None, (3, 5)),
+    "typing.AsyncIterator": (None, (3, 5)),
+    "typing.Awaitable": (None, (3, 5)),
+    "typing.ChainMap": (None, (3, 6)),
+    "typing.ClassVar": (None if not bp("typing") else (2, 7), (3, 5)),
+    "typing.Collection": (None, (3, 6)),
+    "typing.ContextManager": (None if not bp("typing") else (2, 7), (3, 6)),
+    "typing.Counter": (None if not bp("typing") else (2, 7), (3, 6)),
+    "typing.Deque": (None if not bp("typing") else (2, 7), (3, 6)),
+    "typing.Protocol": (None, (3, 8)),
+    "typing.SupportsIndex": (None, (3, 8)),
+    "typing.Text": (None if not bp("typing") else (2, 7), (3, 6)),
+    "typing.Type": (None if not bp("typing") else (2, 7), (3, 5)),
+    "unittest.TextTestResult": ((2, 7), (3, 0)),
+    "warnings.catch_warnings": ((2, 6), (3, 0)),
+    "weakref.WeakMethod": (None, (3, 4)),
+    "weakref.WeakSet": ((2, 7), (3, 0)),
+    "weakref.finalize": (None, (3, 4)),
+    "wsgiref.handlers.IISCGIHandler": (None, (3, 2)),
+    "xml.etree.ElementTree": ((2, 5), (3, 0)),
+    "xml.etree.ElementTree.C14NWriterTarget": (None, (3, 8)),
+    "xml.etree.ElementTree.XMLPullParser": (None, (3, 4)),
+    "xmlrpclib.MultiCall": ((2, 4), None),
 
-  # Exceptions
-  "codecs.namereplace_errors": (None, (3, 5)),
-  "concurrent.futures.BrokenExecutor": (None, (3, 7)),
-  "concurrent.futures.process.BrokenProcessPool": (None, (3, 3)),
-  "concurrent.futures.thread.BrokenThreadPool": (None, (3, 7)),
-  "email.errors.CloseBoundaryNotFoundDefect": (None, (3, 3)),
-  "email.errors.FirstHeaderLineIsContinuationDefect": ((2, 4), (3, 0)),
-  "email.errors.MalformedHeaderDefect": ((2, 4), (3, 0)),
-  "email.errors.MisplacedEnvelopeHeaderDefect": ((2, 4), (3, 0)),
-  "email.errors.MissingHeaderBodySeparatorDefect": (None, (3, 3)),
-  "email.errors.MultipartInvariantViolationDefect": ((2, 4), (3, 0)),
-  "email.errors.NoBoundaryInMultipartDefect": ((2, 4), (3, 0)),
-  "email.errors.StartBoundaryNotFoundDefect": ((2, 4), (3, 0)),
-  "gzip.BadGzipFile": (None, (3, 8)),
-  "ssl.SSLEOFError": ((2, 7), (3, 3)),
-  "ssl.SSLSyscallError": ((2, 7), (3, 3)),
-  "ssl.SSLWantReadError": ((2, 7), (3, 3)),
-  "ssl.SSLWantWriteError": ((2, 7), (3, 3)),
-  "ssl.SSLZeroReturnError": ((2, 7), (3, 3)),
-  "subprocess.SubprocessError": (None, (3, 3)),
-  "subprocess.TimeoutExpired": (None, (3, 3)),
-  "tarfile.HeaderError": ((2, 6), (3, 0)),
-  "zipfile.BadZipFile": (None, (3, 2)),
+    # Exceptions
+    "codecs.namereplace_errors": (None, (3, 5)),
+    "concurrent.futures.BrokenExecutor": (None, (3, 7)),
+    "concurrent.futures.process.BrokenProcessPool": (None, (3, 3)),
+    "concurrent.futures.thread.BrokenThreadPool": (None, (3, 7)),
+    "email.errors.CloseBoundaryNotFoundDefect": (None, (3, 3)),
+    "email.errors.FirstHeaderLineIsContinuationDefect": ((2, 4), (3, 0)),
+    "email.errors.MalformedHeaderDefect": ((2, 4), (3, 0)),
+    "email.errors.MisplacedEnvelopeHeaderDefect": ((2, 4), (3, 0)),
+    "email.errors.MissingHeaderBodySeparatorDefect": (None, (3, 3)),
+    "email.errors.MultipartInvariantViolationDefect": ((2, 4), (3, 0)),
+    "email.errors.NoBoundaryInMultipartDefect": ((2, 4), (3, 0)),
+    "email.errors.StartBoundaryNotFoundDefect": ((2, 4), (3, 0)),
+    "gzip.BadGzipFile": (None, (3, 8)),
+    "ssl.SSLEOFError": ((2, 7), (3, 3)),
+    "ssl.SSLSyscallError": ((2, 7), (3, 3)),
+    "ssl.SSLWantReadError": ((2, 7), (3, 3)),
+    "ssl.SSLWantWriteError": ((2, 7), (3, 3)),
+    "ssl.SSLZeroReturnError": ((2, 7), (3, 3)),
+    "subprocess.SubprocessError": (None, (3, 3)),
+    "subprocess.TimeoutExpired": (None, (3, 3)),
+    "tarfile.HeaderError": ((2, 6), (3, 0)),
+    "zipfile.BadZipFile": (None, (3, 2)),
 
-  # Builtin functions
-  "all": ((2, 5), (3, 0)),
-  "any": ((2, 5), (3, 0)),
-  "basestring": ((2, 3), (3, 0)),
-  "bin": ((2, 6), (3, 0)),
-  "breakpoint": (None, (3, 7)),
-  "bytearray.hex": (None, (3, 5)),
-  "bytes.hex": (None, (3, 5)),
-  "classmethod": ((2, 2), (3, 0)),
-  "dict.fromkeys": ((2, 3), (3, 0)),
-  "dict.has_key": ((2, 2), None),
-  "dict.iteritems": ((2, 2), None),
-  "dict.iterkeys": ((2, 2), None),
-  "dict.itervalues": ((2, 2), None),
-  "dict.pop": ((2, 3), (3, 0)),
-  "dict.viewitems": ((2, 7), None),
-  "dict.viewkeys": ((2, 7), None),
-  "dict.viewvalues": ((2, 7), None),
-  "enumerate": ((2, 3), (3, 0)),
-  "file": ((2, 2), (3, 0)),
-  "float.as_integer_ratio": ((2, 6), (3, 0)),
-  "float.fromhex": ((2, 6), (3, 0)),
-  "float.hex": ((2, 6), (3, 0)),
-  "float.is_integer": ((2, 6), (3, 0)),
-  "format": ((2, 6), (3, 0)),
-  "frozenset.isdisjoint": ((2, 6), (3, 0)),
-  "help": ((2, 2), (3, 0)),
-  "int.bit_length": ((2, 7), (3, 1)),
-  "int.from_bytes": (None, (3, 2)),
-  "int.to_bytes": (None, (3, 2)),
-  "iter": ((2, 2), (3, 0)),
-  "list.clear": (None, (3, 0)),
-  "list.copy": (None, (3, 0)),
-  "long.bit_length": ((2, 7), None),
-  "memoryview.cast": (None, (3, 3)),
-  "memoryview.hex": (None, (3, 5)),
-  "memoryview.release": (None, (3, 2)),
-  "next": ((2, 6), (3, 0)),
-  "reversed": ((2, 4), (3, 0)),
-  "set.isdisjoint": ((2, 6), (3, 0)),
-  "sorted": ((2, 4), (3, 0)),
-  "staticmethod": ((2, 2), (3, 0)),
-  "str.casefold": (None, (3, 3)),
-  "str.decode": ((2, 2), None),
-  "str.format": ((2, 6), (3, 0)),
-  "str.format_map": (None, (3, 2)),
-  "str.isascii": (None, (3, 7)),
-  "str.isdecimal": (None, (3, 0)),
-  "str.isidentifier": (None, (3, 0)),
-  "str.isnumeric": (None, (3, 0)),
-  "str.isprintable": (None, (3, 0)),
-  "str.maketrans": (None, (3, 0)),
-  "str.partition": ((2, 5), (3, 0)),
-  "str.rpartition": ((2, 5), (3, 0)),
-  "str.rsplit": ((2, 4), (3, 0)),
-  "str.zfill": ((2, 2), (3, 0)),
-  "sum": ((2, 3), (3, 0)),
-  "super": ((2, 2), (3, 0)),
-  "unichr": ((2, 0), (3, 0)),
-  "unicode": ((2, 0), (3, 0)),
-  "unicode.isdecimal": ((2, 0), None),
-  "unicode.isnumeric": ((2, 0), None),
-  "zip": ((2, 0), (3, 0)),
+    # Builtin functions
+    "all": ((2, 5), (3, 0)),
+    "any": ((2, 5), (3, 0)),
+    "basestring": ((2, 3), (3, 0)),
+    "bin": ((2, 6), (3, 0)),
+    "breakpoint": (None, (3, 7)),
+    "bytearray.hex": (None, (3, 5)),
+    "bytes.hex": (None, (3, 5)),
+    "classmethod": ((2, 2), (3, 0)),
+    "dict.fromkeys": ((2, 3), (3, 0)),
+    "dict.has_key": ((2, 2), None),
+    "dict.iteritems": ((2, 2), None),
+    "dict.iterkeys": ((2, 2), None),
+    "dict.itervalues": ((2, 2), None),
+    "dict.pop": ((2, 3), (3, 0)),
+    "dict.viewitems": ((2, 7), None),
+    "dict.viewkeys": ((2, 7), None),
+    "dict.viewvalues": ((2, 7), None),
+    "enumerate": ((2, 3), (3, 0)),
+    "file": ((2, 2), (3, 0)),
+    "float.as_integer_ratio": ((2, 6), (3, 0)),
+    "float.fromhex": ((2, 6), (3, 0)),
+    "float.hex": ((2, 6), (3, 0)),
+    "float.is_integer": ((2, 6), (3, 0)),
+    "format": ((2, 6), (3, 0)),
+    "frozenset.isdisjoint": ((2, 6), (3, 0)),
+    "help": ((2, 2), (3, 0)),
+    "int.bit_length": ((2, 7), (3, 1)),
+    "int.from_bytes": (None, (3, 2)),
+    "int.to_bytes": (None, (3, 2)),
+    "iter": ((2, 2), (3, 0)),
+    "list.clear": (None, (3, 0)),
+    "list.copy": (None, (3, 0)),
+    "long.bit_length": ((2, 7), None),
+    "memoryview.cast": (None, (3, 3)),
+    "memoryview.hex": (None, (3, 5)),
+    "memoryview.release": (None, (3, 2)),
+    "next": ((2, 6), (3, 0)),
+    "reversed": ((2, 4), (3, 0)),
+    "set.isdisjoint": ((2, 6), (3, 0)),
+    "sorted": ((2, 4), (3, 0)),
+    "staticmethod": ((2, 2), (3, 0)),
+    "str.casefold": (None, (3, 3)),
+    "str.decode": ((2, 2), None),
+    "str.format": ((2, 6), (3, 0)),
+    "str.format_map": (None, (3, 2)),
+    "str.isascii": (None, (3, 7)),
+    "str.isdecimal": (None, (3, 0)),
+    "str.isidentifier": (None, (3, 0)),
+    "str.isnumeric": (None, (3, 0)),
+    "str.isprintable": (None, (3, 0)),
+    "str.maketrans": (None, (3, 0)),
+    "str.partition": ((2, 5), (3, 0)),
+    "str.rpartition": ((2, 5), (3, 0)),
+    "str.rsplit": ((2, 4), (3, 0)),
+    "str.zfill": ((2, 2), (3, 0)),
+    "sum": ((2, 3), (3, 0)),
+    "super": ((2, 2), (3, 0)),
+    "unichr": ((2, 0), (3, 0)),
+    "unicode": ((2, 0), (3, 0)),
+    "unicode.isdecimal": ((2, 0), None),
+    "unicode.isnumeric": ((2, 0), None),
+    "zip": ((2, 0), (3, 0)),
 
-  # Builtin constants
-  "memoryview.c_contiguous": (None, (3, 3)),
-  "memoryview.contiguous": (None, (3, 3)),
-  "memoryview.f_contiguous": (None, (3, 3)),
-  "memoryview.nbytes": (None, (3, 3)),
-  "memoryview.obj": (None, (3, 3)),
+    # Builtin constants
+    "memoryview.c_contiguous": (None, (3, 3)),
+    "memoryview.contiguous": (None, (3, 3)),
+    "memoryview.f_contiguous": (None, (3, 3)),
+    "memoryview.nbytes": (None, (3, 3)),
+    "memoryview.obj": (None, (3, 3)),
 
-  # Functions
-  "SimpleXMLRPCServer.SimpleXMLRPCServer.register_introspection_functions": ((2, 3), None),
-  "Tkinter.Tcl": ((2, 4), None),
-  "argparse.ArgumentParser.parse_intermixed_args": (None, (3, 7)),
-  "argparse.ArgumentParser.parse_known_intermixed_args": (None, (3, 7)),
-  "asyncio.all_tasks": (None, (3, 7)),
-  "asyncio.create_task": (None, (3, 7)),
-  "asyncio.current_task": (None, (3, 7)),
-  "asyncio.run": (None, (3, 7)),
-  "asyncio.run_coroutine_threadsafe": (None, (3, 5)),
-  "audioop.byteswap": (None, (3, 4)),
-  "base64.a85decode": (None, (3, 4)),
-  "base64.a85encode": (None, (3, 4)),
-  "base64.decodebytes": (None, (3, 1)),
-  "base64.decodestring": ((2, 4), 3),
-  "base64.encodebytes": (None, (3, 1)),
-  "base64.encodestring": ((2, 4), (3, 0)),
-  "bdb.Bdb.get_bpbynumber": (None, (3, 2)),
-  "bdb.Breakpoint.bpformat": (None, (3, 2)),
-  "bz2.BZ2File.fileno": (None, (3, 3)),
-  "bz2.BZ2File.peek": (None, (3, 3)),
-  "bz2.BZ2File.read1": (None, (3, 3)),
-  "bz2.BZ2File.readable": (None, (3, 3)),
-  "bz2.BZ2File.readinto": (None, (3, 3)),
-  "bz2.BZ2File.seekable": (None, (3, 3)),
-  "bz2.BZ2File.writable": (None, (3, 3)),
-  "bz2.open": (None, (3, 3)),
-  "calendar.Calendar.itermonthdays3": (None, (3, 7)),
-  "calendar.Calendar.itermonthdays4": (None, (3, 7)),
-  "cmath.isclose": (None, (3, 5)),
-  "cmath.isfinite": (None, (3, 2)),
-  "collections.Counter.subtract": (None, (3, 2)),
-  "collections.OrderedDict.move_to_end": (None, (3, 2)),
-  "collections.deque.copy": (None, (3, 5)),
-  "collections.deque.count": ((2, 7), (3, 2)),
-  "collections.deque.index": (None, (3, 5)),
-  "collections.deque.insert": (None, (3, 5)),
-  "collections.deque.remove": ((2, 5), (3, 0)),
-  "collections.deque.reverse": ((2, 7), (3, 2)),
-  "compileall.compile_file": ((2, 7), (3, 2)),
-  "contextlib.nullcontext": (None, (3, 7)),
-  "contextlib.redirect_stderr": (None, (3, 5)),
-  "contextlib.redirect_stderr": (None, (3, 5)),
-  "contextlib.redirect_stdout": (None, (3, 4)),
-  "contextlib.redirect_stdout": (None, (3, 4)),
-  "contextlib.suppress": (None, (3, 4)),
-  "contextlib.suppress": (None, (3, 4)),
-  "crypt.mksalt": (None, (3, 3)),
-  "csv.DictWriter.writeheader": (None, (3, 2)),
-  "csv.field_size_limit": ((2, 5), (3, 0)),
-  "ctypes._CData.from_buffer": ((2, 6), (3, 0)),
-  "ctypes._CData.from_buffer_copy": ((2, 6), (3, 0)),
-  "ctypes.get_errno": ((2, 6), (3, 0)),
-  "ctypes.get_last_error": ((2, 6), (3, 0)),
-  "ctypes.set_errno": ((2, 6), (3, 0)),
-  "ctypes.set_last_error": ((2, 6), (3, 0)),
-  "ctypes.util.find_msvcrt": ((2, 6), (3, 0)),
-  "curses.unget_wch": (None, (3, 3)),
-  "curses.update_lines_cols": (None, (3, 5)),
-  "datetime.date.fromisocalendar": (None, (3, 8)),
-  "datetime.date.fromisoformat": (None, (3, 7)),
-  "datetime.datetime.fromisocalendar": (None, (3, 8)),
-  "datetime.datetime.fromisoformat": (None, (3, 7)),
-  "datetime.datetime.timestamp": (None, (3, 3)),
-  "datetime.time.fromisoformat": (None, (3, 7)),
-  "datetime.timedelta.total_seconds": (None, (3, 2)),
-  "decimal.Context.clear_traps": (None, (3, 3)),
-  "decimal.Context.create_decimal_from_float": ((2, 7), (3, 1)),
-  "decimal.Decimal.as_integer_ratio": (None, (3, 6)),
-  "decimal.Decimal.canonical": ((2, 6), (3, 0)),
-  "decimal.Decimal.compare_signal": ((2, 6), (3, 0)),
-  "decimal.Decimal.compare_total": ((2, 6), (3, 0)),
-  "decimal.Decimal.compare_total_mag": ((2, 6), (3, 0)),
-  "decimal.Decimal.conjugate": ((2, 6), (3, 0)),
-  "decimal.Decimal.copy_abs": ((2, 6), (3, 0)),
-  "decimal.Decimal.copy_negate": ((2, 6), (3, 0)),
-  "decimal.Decimal.copy_sign": ((2, 6), (3, 0)),
-  "decimal.Decimal.exp": ((2, 6), (3, 0)),
-  "decimal.Decimal.fma": ((2, 6), (3, 0)),
-  "decimal.Decimal.from_float": ((2, 7), (3, 1)),
-  "decimal.Decimal.is_canonical": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_finite": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_infinite": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_nan": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_normal": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_qnan": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_signed": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_snan": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_subnormal": ((2, 6), (3, 0)),
-  "decimal.Decimal.is_zero": ((2, 6), (3, 0)),
-  "decimal.Decimal.ln": ((2, 6), (3, 0)),
-  "decimal.Decimal.log10": ((2, 6), (3, 0)),
-  "decimal.Decimal.logb": ((2, 6), (3, 0)),
-  "decimal.Decimal.logical_and": ((2, 6), (3, 0)),
-  "decimal.Decimal.logical_invert": ((2, 6), (3, 0)),
-  "decimal.Decimal.logical_or": ((2, 6), (3, 0)),
-  "decimal.Decimal.logical_xor": ((2, 6), (3, 0)),
-  "decimal.Decimal.max_mag": ((2, 6), (3, 0)),
-  "decimal.Decimal.min_mag": ((2, 6), (3, 0)),
-  "decimal.Decimal.next_minus": ((2, 6), (3, 0)),
-  "decimal.Decimal.next_plus": ((2, 6), (3, 0)),
-  "decimal.Decimal.next_toward": ((2, 6), (3, 0)),
-  "decimal.Decimal.number_class": ((2, 6), (3, 0)),
-  "decimal.Decimal.radix": ((2, 6), (3, 0)),
-  "decimal.Decimal.rotate": ((2, 6), (3, 0)),
-  "decimal.Decimal.scaleb": ((2, 6), (3, 0)),
-  "decimal.Decimal.shift": ((2, 6), (3, 0)),
-  "decimal.Decimal.to_integral_exact": ((2, 6), (3, 0)),
-  "decimal.Decimal.to_integral_value": ((2, 6), (3, 0)),
-  "decimal.localcontext": ((2, 5), (3, 0)),
-  "difflib.SequenceMatcher.get_grouped_opcodes": ((2, 3), (3, 0)),
-  "difflib.context_diff": ((2, 3), (3, 0)),
-  "difflib.diff_bytes": (None, (3, 5)),
-  "difflib.unified_diff": ((2, 3), (3, 0)),
-  "dis.code_info": (None, (3, 2)),
-  "dis.get_instructions": (None, (3, 4)),
-  "dis.show_code": (None, (3, 2)),
-  "dis.stack_effect": (None, (3, 4)),
-  "email.utils.format_datetime": (None, (3, 3)),
-  "email.utils.formatdate": ((2, 4), (3, 0)),
-  "email.utils.localtime": (None, (3, 3)),
-  "email.utils.parsedate_to_datetime": (None, (3, 3)),
-  "fractions.Fraction.as_integer_ratio": (None, (3, 8)),
-  "functools.cmp_to_key": ((2, 7), (3, 2)),
-  "functools.lru_cache": (None, (3, 2)),
-  "functools.partial_method": (None, (3, 4)),
-  "functools.reduce": ((2, 6), (3, 0)),
-  "functools.total_ordering": ((2, 7), (3, 2)),
-  "gettext.dnpgettext": (None, (3, 8)),
-  "gettext.dpgettext": (None, (3, 8)),
-  "gettext.npgettext": (None, (3, 8)),
-  "gettext.pgettext": (None, (3, 8)),
-  "gzip.GzipFile.peek": (None, (3, 2)),
-  "gzip.compress": (None, (3, 2)),
-  "gzip.decompress": (None, (3, 2)),
-  "hashlib.pbkdf2_hmac": ((2, 7), (3, 4)),
-  "hashlib.scrypt": (None, (3, 6)),
-  "heapq.heappushpop": ((2, 6), (3, 0)),
-  "heapq.merge": ((2, 6), (3, 0)),
-  "heapq.nlargest": ((2, 4), (3, 0)),
-  "heapq.nsmallest": ((2, 4), (3, 0)),
-  "hmac.compare_digest": ((2, 7), (3, 3)),
-  "hmac.digest": (None, (3, 7)),
-  "importlib.machinery.ExtensionFileLoader.create_module": (None, (3, 5)),
-  "importlib.machinery.ExtensionFileLoader.exec_module": (None, (3, 5)),
-  "importlib.machinery.ExtensionFileLoader.get_filename": (None, (3, 4)),
-  "importlib.machinery.FileFinder.find_spec": (None, (3, 4)),
-  "importlib.machinery.PathFinder.find_spec": (None, (3, 4)),
-  "importlib.machinery.all_suffixes": (None, (3, 3)),
-  "importlib.util.cache_from_source": (None, (3, 4)),
-  "importlib.util.decode_source": (None, (3, 4)),
-  "importlib.util.find_spec": (None, (3, 4)),
-  "importlib.util.module_from_spec": (None, (3, 5)),
-  "importlib.util.resolve_name": (None, (3, 3)),
-  "importlib.util.source_from_cache": (None, (3, 4)),
-  "importlib.util.source_hash": (None, (3, 7)),
-  "importlib.util.spec_from_file_location": (None, (3, 4)),
-  "importlib.util.spec_from_loader": (None, (3, 4)),
-  "inspect.BoundArguments.apply_defaults": (None, (3, 6)),
-  "inspect.cleandoc": ((2, 6), (3, 0)),
-  "inspect.getattr_static": (None, (3, 2)),
-  "inspect.getcallargs": ((2, 7), (3, 2)),
-  "inspect.getclosurevars": (None, (3, 3)),
-  "inspect.getcoroutinelocals": (None, (3, 5)),
-  "inspect.getcoroutinestate": (None, (3, 5)),
-  "inspect.getgeneratorlocals": (None, (3, 2)),
-  "inspect.getgeneratorstate": (None, (3, 2)),
-  "inspect.isabstract": ((2, 6), (3, 0)),
-  "inspect.isasyncgen": (None, (3, 6)),
-  "inspect.isasyncgenfunction": (None, (3, 6)),
-  "inspect.isawaitable": (None, (3, 5)),
-  "inspect.iscoroutine": (None, (3, 5)),
-  "inspect.iscoroutinefunction": (None, (3, 5)),
-  "inspect.isdatadescriptor": ((2, 3), (3, 0)),
-  "inspect.isgenerator": ((2, 6), (3, 0)),
-  "inspect.isgeneratorfunction": ((2, 6), (3, 0)),
-  "inspect.isgetsetdescriptor": ((2, 5), (3, 0)),
-  "inspect.ismemberdescriptor": ((2, 5), (3, 0)),
-  "inspect.signature": (None, (3, 3)),
-  "inspect.unwrap": (None, (3, 4)),
-  "io.BufferedIOBase.detach": ((2, 7), (3, 1)),
-  "io.BufferedIOBase.readinto1": (None, (3, 5)),
-  "io.BytesIO.getbuffer": (None, (3, 2)),
-  "io.BytesIO.readinto1": (None, (3, 5)),
-  "io.TextIOBase.detach": ((2, 7), (3, 1)),
-  "itertools.accumulate": (None, (3, 2)),
-  "itertools.combinations": ((2, 6), (3, 0)),
-  "itertools.combinations_with_replacement": ((2, 7), (3, 1)),
-  "itertools.compress": ((2, 7), (3, 1)),
-  "itertools.groupby": ((2, 4), (3, 0)),
-  "itertools.izip_longest": ((2, 6), (3, 0)),
-  "itertools.permutations": ((2, 6), (3, 0)),
-  "itertools.product": ((2, 6), (3, 0)),
-  "itertools.tee": ((2, 4), (3, 0)),
-  "logging.Logger.getChild": ((2, 7), (3, 2)),
-  "logging.Logger.hasHandlers": (None, (3, 2)),
-  "logging.getLogRecordFactory": (None, (3, 2)),
-  "logging.setLogRecordFactory": (None, (3, 2)),
-  "math.acosh": ((2, 6), (3, 0)),
-  "math.asinh": ((2, 6), (3, 0)),
-  "math.atanh": ((2, 6), (3, 0)),
-  "math.comb": (None, (3, 8)),
-  "math.copysign": ((2, 6), (3, 0)),
-  "math.dist": (None, (3, 8)),
-  "math.erf": ((2, 7), (3, 2)),
-  "math.erfc": ((2, 7), (3, 2)),
-  "math.expm1": ((2, 7), (3, 2)),
-  "math.fsum": ((2, 6), (3, 0)),
-  "math.gamma": ((2, 7), (3, 2)),
-  "math.gcd": (None, (3, 5)),
-  "math.isclose": (None, (3, 5)),
-  "math.isfinite": (None, (3, 2)),
-  "math.isinf": ((2, 6), (3, 0)),
-  "math.isnan": ((2, 6), (3, 0)),
-  "math.isqrt": (None, (3, 8)),
-  "math.lgamma": ((2, 7), (3, 2)),
-  "math.log1p": ((2, 6), (3, 0)),
-  "math.log2": (None, (3, 3)),
-  "math.perm": (None, (3, 8)),
-  "math.prod": (None, (3, 8)),
-  "math.remainder": (None, (3, 7)),
-  "math.trunc": ((2, 6), (3, 0)),
-  "mmap.mmap.madvise": (None, (3, 8)),
-  "multiprocessing.Pool.starmap": (None, (3, 3)),
-  "multiprocessing.Pool.starmap_async": (None, (3, 3)),
-  "multiprocessing.connection.wait": (None, (3, 3)),
-  "multiprocessing.get_all_start_methods": (None, (3, 4)),
-  "multiprocessing.get_context": (None, (3, 4)),
-  "multiprocessing.get_start_method": (None, (3, 4)),
-  "multiprocessing.set_start_method": (None, (3, 4)),
-  "os.add_dll_directory": (None, (3, 8)),
-  "os.fsdecode": (None, (3, 2)),
-  "os.fsencode": (None, (3, 2)),
-  "os.fspath": (None, (3, 6)),
-  "os.get_blocking": (None, (3, 5)),
-  "os.get_exec_path": (None, (3, 2)),
-  "os.get_handle_inheritable": (None, (3, 4)),
-  "os.get_inheritable": (None, (3, 4)),
-  "os.get_terminal_size": (None, (3, 3)),
-  "os.getenvb": (None, (3, 2)),
-  "os.getgrouplist": (None, (3, 3)),
-  "os.getpgid": ((2, 3), (3, 0)),
-  "os.getpriority": (None, (3, 3)),
-  "os.getresgid": ((2, 7), (3, 0)),
-  "os.getresuid": ((2, 7), (3, 0)),
-  "os.getsid": ((2, 4), (3, 0)),
-  "os.getxattr": (None, (3, 3)),
-  "os.initgroups": ((2, 7), (3, 2)),
-  "os.listxattr": (None, (3, 3)),
-  "os.lockf": (None, (3, 3)),
-  "os.memfd_create": (None, (3, 8)),
-  "os.path.commonpath": (None, (3, 5)),
-  "os.path.getctime": ((2, 3), (3, 0)),
-  "os.path.ismount": (None, (3, 4)),
-  "os.path.lexists": ((2, 4), (3, 0)),
-  "os.path.realpath": ((2, 2), (3, 0)),
-  "os.path.relpath": ((2, 6), (3, 0)),
-  "os.pipe2": (None, (3, 3)),
-  "os.posix_fadvise": (None, (3, 3)),
-  "os.posix_fallocate": (None, (3, 3)),
-  "os.pread": (None, (3, 3)),
-  "os.pwrite": (None, (3, 3)),
-  "os.readv": (None, (3, 3)),
-  "os.removexattr": (None, (3, 3)),
-  "os.sendfile": (None, (3, 3)),
-  "os.set_blocking": (None, (3, 5)),
-  "os.set_handle_inheritable": (None, (3, 4)),
-  "os.set_inheritable": (None, (3, 4)),
-  "os.setgroups": ((2, 2), (3, 0)),
-  "os.setpriority": (None, (3, 3)),
-  "os.setresgid": ((2, 7), (3, 2)),
-  "os.setresuid": ((2, 7), (3, 2)),
-  "os.setxattr": (None, (3, 3)),
-  "os.writev": (None, (3, 3)),
-  "pathlib.Path.expanduser": (None, (3, 5)),
-  "pathlib.Path.home": (None, (3, 5)),
-  "pathlib.Path.is_mount": (None, (3, 7)),
-  "pathlib.Path.link_to": (None, (3, 8)),
-  "pathlib.Path.read_bytes": (None, (3, 5)),
-  "pathlib.Path.read_text": (None, (3, 5)),
-  "pathlib.Path.samefile": (None, (3, 5)),
-  "pathlib.Path.write_bytes": (None, (3, 5)),
-  "pathlib.Path.write_text": (None, (3, 5)),
-  "pickle.Pickler.reducer_override": (None, (3, 8)),
-  "pickletools.optimize": ((2, 6), (3, 0)),
-  "pkgutil.get_data": ((2, 6), (3, 0)),
-  "platform.linux_distribution": ((2, 6), (3, 0)),
-  "platform.python_branch": ((2, 6), (3, 0)),
-  "platform.python_implementation": ((2, 6), (3, 0)),
-  "platform.python_revision": ((2, 6), (3, 0)),
-  "plistlib.dump": (None, (3, 4)),
-  "plistlib.dumps": (None, (3, 4)),
-  "plistlib.load": (None, (3, 4)),
-  "plistlib.loads": (None, (3, 4)),
-  "runpy.run_path": ((2, 7), (3, 2)),
-  "shlex.join": (None, (3, 8)),
-  "shlex.pop_source": ((2, 1), (3, 0)),
-  "shlex.push_source": ((2, 1), (3, 0)),
-  "shlex.quote": (None, (3, 3)),
-  "shlex.split": ((2, 3), (3, 0)),
-  "socket.CMSG_LEN": (None, (3, 3)),
-  "socket.CMSG_SPACE": (None, (3, 3)),
-  "socket.close": (None, (3, 7)),
-  "socket.create_server": (None, (3, 8)),
-  "socket.detach": (None, (3, 2)),
-  "socket.fromshare": (None, (3, 3)),
-  "socket.get_inheritable": (None, (3, 4)),
-  "socket.getblocking": (None, (3, 7)),
-  "socket.has_dualstack_ipv6": (None, (3, 8)),
-  "socket.if_indextoname": (None, (3, 3)),
-  "socket.if_nameindex": (None, (3, 3)),
-  "socket.if_nametoindex": (None, (3, 3)),
-  "socket.recvmsg": (None, (3, 3)),
-  "socket.recvmsg_into": (None, (3, 3)),
-  "socket.sendfile": (None, (3, 5)),
-  "socket.sendmsg": (None, (3, 3)),
-  "socket.sendmsg_afalg": (None, (3, 6)),
-  "socket.set_inheritable": (None, (3, 4)),
-  "socket.sethostname": (None, (3, 3)),
-  "socket.share": (None, (3, 3)),
-  "sqlite3.Connection.enable_load_extension": ((2, 7), (3, 2)),
-  "sqlite3.Connection.iter_dump": ((2, 6), (3, 0)),
-  "sqlite3.Connection.load_extension": ((2, 7), (3, 2)),
-  "sqlite3.Connection.set_progress_handler": ((2, 6), (3, 0)),
-  "sqlite3.Connection.set_trace_callback": (None, (3, 3)),
-  "sqlite3.Row.keys": ((2, 6), (3, 0)),
-  "ssl.RAND_bytes": (None, (3, 3)),
-  "ssl.RAND_pseudo_bytes": (None, (3, 3)),
-  "ssl.SSLContext.cert_store_stats": (None, (3, 4)),
-  "ssl.SSLContext.get_ca_certs": (None, (3, 4)),
-  "ssl.SSLContext.get_ciphers": (None, (3, 6)),
-  "ssl.SSLContext.load_default_certs": (None, (3, 4)),
-  "ssl.SSLContext.load_dh_params": (None, (3, 3)),
-  "ssl.SSLContext.set_alpn_protocols": ((2, 7), (3, 5)),
-  "ssl.SSLContext.set_ecdh_curve": (None, (3, 3)),
-  "ssl.SSLContext.set_npn_protocols": ((2, 7), (3, 3)),
-  "ssl.SSLContext.set_servername_callback": (None, (3, 4)),
-  "ssl.SSLSocket.verify_client_post_handshake": (None, (3, 8)),
-  "ssl._https_verify_certificates": ((2, 7), None),
-  "ssl.compression": ((2, 7), (3, 3)),
-  "ssl.create_default_context": ((2, 7), (3, 4)),
-  "ssl.enum_certificates": ((2, 7), (3, 4)),
-  "ssl.enum_crls": ((2, 7), (3, 4)),
-  "ssl.get_channel_binding": ((2, 7), (3, 3)),
-  "ssl.get_default_verify_paths": ((2, 7), (3, 4)),
-  "ssl.match_hostname": ((2, 7), (3, 2)),
-  "ssl.selected_alpn_protocol": ((2, 7), (3, 5)),
-  "ssl.selected_npn_protocol": ((2, 7), (3, 3)),
-  "ssl.shared_ciphers": (None, (3, 5)),
-  "ssl.version": ((2, 7), (3, 5)),
-  "statistics.fmean": (None, (3, 8)),
-  "statistics.geometric_mean": (None, (3, 8)),
-  "statistics.harmonic_mean": (None, (3, 6)),
-  "statistics.multimode": (None, (3, 8)),
-  "statistics.quantiles": (None, (3, 8)),
-  "subprocess.CompletedProcess.check_returncode": (None, (3, 5)),
-  "subprocess.Popen.kill": ((2, 6), (3, 0)),
-  "subprocess.Popen.send_signal": ((2, 6), (3, 0)),
-  "subprocess.Popen.terminate": ((2, 6), (3, 0)),
-  "subprocess.check_call": ((2, 5), (3, 0)),
-  "subprocess.check_output": ((2, 7), (3, 1)),
-  "subprocess.run": (None, (3, 5)),
-  "sys.exc_clear": ((2, 3), None),
-  "sys.getcheckinterval": ((2, 3), (3, 0)),
-  "sys.getdefaultencoding": ((2, 0), (3, 0)),
-  "sys.getdlopenflags": ((2, 2), (3, 0)),
-  "sys.getfilesystemencoding": ((2, 3), (3, 0)),
-  "sys.getprofile": ((2, 6), (3, 0)),
-  "sys.getsizeof": ((2, 6), (3, 0)),
-  "sys.gettrace": ((2, 6), (3, 0)),
-  "sys.getwindowsversion": ((2, 3), (3, 0)),
-  "sys.unraisablehook": (None, (3, 8)),
-  "tarfile.TarFile.extractall": ((2, 5), (3, 0)),
-  "tarfile.TarInfo.fromtarfile": ((2, 6), (3, 0)),
-  "textwrap.indent": (None, (3, 3)),
-  "textwrap.shorten": (None, (3, 4)),
-  "threading.excepthook": (None, (3, 8)),
-  "threading.get_ident": (None, (3, 3)),
-  "threading.get_native_id": (None, (3, 8)),
-  "threading.main_thread": (None, (3, 4)),
-  "time.clock_getres": (None, (3, 3)),
-  "time.clock_gettime": (None, (3, 3)),
-  "time.clock_gettime_ns": (None, (3, 7)),
-  "time.clock_settime": (None, (3, 3)),
-  "time.clock_settime_ns": (None, (3, 7)),
-  "time.get_clock_info": (None, (3, 3)),
-  "time.monotonic": (None, (3, 3)),
-  "time.monotonic_ns": (None, (3, 7)),
-  "time.perf_counter": (None, (3, 3)),
-  "time.perf_counter_ns": (None, (3, 7)),
-  "time.process_time": (None, (3, 3)),
-  "time.process_time_ns": (None, (3, 7)),
-  "time.pthread_getcpuclockid": (None, (3, 7)),
-  "time.thread_time": (None, (3, 7)),
-  "time.thread_time_ns": (None, (3, 7)),
-  "time.time_ns": (None, (3, 7)),
-  "timeit.repeat": ((2, 6), (3, 0)),
-  "timeit.timeit": ((2, 6), (3, 0)),
-  "tokenize.generate_tokens": ((2, 2), (3, 0)),
-  "tokenize.open": (None, (3, 2)),
-  "tokenize.untokenize": ((2, 5), (3, 0)),
-  "typing.NewType": (None, (3, 5)),
-  "typing.get_args": (None, (3, 8)),
-  "typing.get_origin": (None, (3, 8)),
-  "unicodedata.is_normalized": (None, (3, 8)),
-  "unittest.TestCase.addCleanup": ((2, 7), (3, 1)),
-  "unittest.TestCase.addTypeEqualityFunc": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertDictContainsSubset": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertDictEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertGreater": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertGreaterEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertIn": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertIs": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertIsInstance": ((2, 7), (3, 2)),
-  "unittest.TestCase.assertIsNone": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertIsNot": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertIsNotNone": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertItemsEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertLess": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertLessEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertListEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertMultilineEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertNotIn": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertNotIsInstance": ((2, 7), (3, 2)),
-  "unittest.TestCase.assertNotRegex": (None, (3, 1)),
-  "unittest.TestCase.assertNotRegexpMatches": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertRaisesRegexp": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertRegex": (None, (3, 1)),
-  "unittest.TestCase.assertRegexpMatches": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertSequenceEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertSetEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.assertTupleEqual": ((2, 7), (3, 1)),
-  "unittest.TestCase.doCleanups": ((2, 7), (3, 1)),
-  "unittest.TestCase.longMessage": ((2, 7), (3, 1)),
-  "unittest.TestCase.maxDiff": ((2, 7), (3, 2)),
-  "unittest.TestLoader.discover": ((2, 7), (3, 2)),
-  "unittest.TestResult.startTestRun": ((2, 7), (3, 1)),
-  "unittest.TestResult.stopTestRun": ((2, 7), (3, 1)),
-  "unittest.addModuleCleanup": (None, (3, 8)),
-  "unittest.doModuleCleanups": (None, (3, 8)),
-  "warnings.warnpy3k": ((2, 6), (3, 0)),
-  "weakref.WeakKeyDictionary.iterkeyrefs": ((2, 5), (3, 0)),
-  "weakref.WeakKeyDictionary.keyrefs": ((2, 5), (3, 0)),
-  "weakref.WeakValueDictionary.itervaluerefs": ((2, 5), (3, 0)),
-  "weakref.WeakValueDictionary.valuerefs": ((2, 5), (3, 0)),
-  "wsgiref.handlers.read_environ": (None, (3, 2)),
-  "xml.etree.ElementTree.Element.extend": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.Element.iter": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.Element.iterfind": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.Element.itertext": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.ElementTree.iterfind": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.TreeBuilder.comment": (None, (3, 8)),
-  "xml.etree.ElementTree.TreeBuilder.doctype": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.TreeBuilder.end_ns": (None, (3, 8)),
-  "xml.etree.ElementTree.TreeBuilder.start_ns": (None, (3, 8)),
-  "xml.etree.ElementTree.canonicalize": (None, (3, 8)),
-  "xml.etree.ElementTree.fromstringlist": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.register_namespace": ((2, 7), (3, 2)),
-  "xml.etree.ElementTree.tostringlist": ((2, 7), (3, 2)),
-  "xmlrpc.server.SimpleXMLRPCServer.register_introspection_functions": (None, (3, 0)),
-  "zipfile.ZipFile.extract": ((2, 6), (3, 0)),
-  "zipfile.ZipFile.extractall": ((2, 6), (3, 0)),
-  "zipfile.ZipFile.open": ((2, 6), (3, 0)),
-  "zipfile.ZipFile.setpassword": ((2, 6), (3, 0)),
-  "zipfile.ZipInfo.from_file": (None, (3, 6)),
-  "zipfile.ZipInfo.is_dir": (None, (3, 6)),
-  "zipimport.zipimporter.get_filename": ((2, 7), (3, 1)),
+    # Functions
+    "SimpleXMLRPCServer.SimpleXMLRPCServer.register_introspection_functions": ((2, 3), None),
+    "Tkinter.Tcl": ((2, 4), None),
+    "argparse.ArgumentParser.parse_intermixed_args": (None, (3, 7)),
+    "argparse.ArgumentParser.parse_known_intermixed_args": (None, (3, 7)),
+    "asyncio.all_tasks": (None, (3, 7)),
+    "asyncio.create_task": (None, (3, 7)),
+    "asyncio.current_task": (None, (3, 7)),
+    "asyncio.run": (None, (3, 7)),
+    "asyncio.run_coroutine_threadsafe": (None, (3, 5)),
+    "audioop.byteswap": (None, (3, 4)),
+    "base64.a85decode": (None, (3, 4)),
+    "base64.a85encode": (None, (3, 4)),
+    "base64.decodebytes": (None, (3, 1)),
+    "base64.decodestring": ((2, 4), 3),
+    "base64.encodebytes": (None, (3, 1)),
+    "base64.encodestring": ((2, 4), (3, 0)),
+    "bdb.Bdb.get_bpbynumber": (None, (3, 2)),
+    "bdb.Breakpoint.bpformat": (None, (3, 2)),
+    "bz2.BZ2File.fileno": (None, (3, 3)),
+    "bz2.BZ2File.peek": (None, (3, 3)),
+    "bz2.BZ2File.read1": (None, (3, 3)),
+    "bz2.BZ2File.readable": (None, (3, 3)),
+    "bz2.BZ2File.readinto": (None, (3, 3)),
+    "bz2.BZ2File.seekable": (None, (3, 3)),
+    "bz2.BZ2File.writable": (None, (3, 3)),
+    "bz2.open": (None, (3, 3)),
+    "calendar.Calendar.itermonthdays3": (None, (3, 7)),
+    "calendar.Calendar.itermonthdays4": (None, (3, 7)),
+    "cmath.isclose": (None, (3, 5)),
+    "cmath.isfinite": (None, (3, 2)),
+    "collections.Counter.subtract": (None, (3, 2)),
+    "collections.OrderedDict.move_to_end": (None, (3, 2)),
+    "collections.deque.copy": (None, (3, 5)),
+    "collections.deque.count": ((2, 7), (3, 2)),
+    "collections.deque.index": (None, (3, 5)),
+    "collections.deque.insert": (None, (3, 5)),
+    "collections.deque.remove": ((2, 5), (3, 0)),
+    "collections.deque.reverse": ((2, 7), (3, 2)),
+    "compileall.compile_file": ((2, 7), (3, 2)),
+    "contextlib.nullcontext": (None, (3, 7)),
+    "contextlib.redirect_stderr": (None, (3, 5)),
+    "contextlib.redirect_stderr": (None, (3, 5)),
+    "contextlib.redirect_stdout": (None, (3, 4)),
+    "contextlib.redirect_stdout": (None, (3, 4)),
+    "contextlib.suppress": (None, (3, 4)),
+    "contextlib.suppress": (None, (3, 4)),
+    "crypt.mksalt": (None, (3, 3)),
+    "csv.DictWriter.writeheader": (None, (3, 2)),
+    "csv.field_size_limit": ((2, 5), (3, 0)),
+    "ctypes._CData.from_buffer": ((2, 6), (3, 0)),
+    "ctypes._CData.from_buffer_copy": ((2, 6), (3, 0)),
+    "ctypes.get_errno": ((2, 6), (3, 0)),
+    "ctypes.get_last_error": ((2, 6), (3, 0)),
+    "ctypes.set_errno": ((2, 6), (3, 0)),
+    "ctypes.set_last_error": ((2, 6), (3, 0)),
+    "ctypes.util.find_msvcrt": ((2, 6), (3, 0)),
+    "curses.unget_wch": (None, (3, 3)),
+    "curses.update_lines_cols": (None, (3, 5)),
+    "datetime.date.fromisocalendar": (None, (3, 8)),
+    "datetime.date.fromisoformat": (None, (3, 7)),
+    "datetime.datetime.fromisocalendar": (None, (3, 8)),
+    "datetime.datetime.fromisoformat": (None, (3, 7)),
+    "datetime.datetime.timestamp": (None, (3, 3)),
+    "datetime.time.fromisoformat": (None, (3, 7)),
+    "datetime.timedelta.total_seconds": (None, (3, 2)),
+    "decimal.Context.clear_traps": (None, (3, 3)),
+    "decimal.Context.create_decimal_from_float": ((2, 7), (3, 1)),
+    "decimal.Decimal.as_integer_ratio": (None, (3, 6)),
+    "decimal.Decimal.canonical": ((2, 6), (3, 0)),
+    "decimal.Decimal.compare_signal": ((2, 6), (3, 0)),
+    "decimal.Decimal.compare_total": ((2, 6), (3, 0)),
+    "decimal.Decimal.compare_total_mag": ((2, 6), (3, 0)),
+    "decimal.Decimal.conjugate": ((2, 6), (3, 0)),
+    "decimal.Decimal.copy_abs": ((2, 6), (3, 0)),
+    "decimal.Decimal.copy_negate": ((2, 6), (3, 0)),
+    "decimal.Decimal.copy_sign": ((2, 6), (3, 0)),
+    "decimal.Decimal.exp": ((2, 6), (3, 0)),
+    "decimal.Decimal.fma": ((2, 6), (3, 0)),
+    "decimal.Decimal.from_float": ((2, 7), (3, 1)),
+    "decimal.Decimal.is_canonical": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_finite": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_infinite": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_nan": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_normal": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_qnan": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_signed": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_snan": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_subnormal": ((2, 6), (3, 0)),
+    "decimal.Decimal.is_zero": ((2, 6), (3, 0)),
+    "decimal.Decimal.ln": ((2, 6), (3, 0)),
+    "decimal.Decimal.log10": ((2, 6), (3, 0)),
+    "decimal.Decimal.logb": ((2, 6), (3, 0)),
+    "decimal.Decimal.logical_and": ((2, 6), (3, 0)),
+    "decimal.Decimal.logical_invert": ((2, 6), (3, 0)),
+    "decimal.Decimal.logical_or": ((2, 6), (3, 0)),
+    "decimal.Decimal.logical_xor": ((2, 6), (3, 0)),
+    "decimal.Decimal.max_mag": ((2, 6), (3, 0)),
+    "decimal.Decimal.min_mag": ((2, 6), (3, 0)),
+    "decimal.Decimal.next_minus": ((2, 6), (3, 0)),
+    "decimal.Decimal.next_plus": ((2, 6), (3, 0)),
+    "decimal.Decimal.next_toward": ((2, 6), (3, 0)),
+    "decimal.Decimal.number_class": ((2, 6), (3, 0)),
+    "decimal.Decimal.radix": ((2, 6), (3, 0)),
+    "decimal.Decimal.rotate": ((2, 6), (3, 0)),
+    "decimal.Decimal.scaleb": ((2, 6), (3, 0)),
+    "decimal.Decimal.shift": ((2, 6), (3, 0)),
+    "decimal.Decimal.to_integral_exact": ((2, 6), (3, 0)),
+    "decimal.Decimal.to_integral_value": ((2, 6), (3, 0)),
+    "decimal.localcontext": ((2, 5), (3, 0)),
+    "difflib.SequenceMatcher.get_grouped_opcodes": ((2, 3), (3, 0)),
+    "difflib.context_diff": ((2, 3), (3, 0)),
+    "difflib.diff_bytes": (None, (3, 5)),
+    "difflib.unified_diff": ((2, 3), (3, 0)),
+    "dis.code_info": (None, (3, 2)),
+    "dis.get_instructions": (None, (3, 4)),
+    "dis.show_code": (None, (3, 2)),
+    "dis.stack_effect": (None, (3, 4)),
+    "email.utils.format_datetime": (None, (3, 3)),
+    "email.utils.formatdate": ((2, 4), (3, 0)),
+    "email.utils.localtime": (None, (3, 3)),
+    "email.utils.parsedate_to_datetime": (None, (3, 3)),
+    "fractions.Fraction.as_integer_ratio": (None, (3, 8)),
+    "functools.cmp_to_key": ((2, 7), (3, 2)),
+    "functools.lru_cache": (None, (3, 2)),
+    "functools.partial_method": (None, (3, 4)),
+    "functools.reduce": ((2, 6), (3, 0)),
+    "functools.total_ordering": ((2, 7), (3, 2)),
+    "gettext.dnpgettext": (None, (3, 8)),
+    "gettext.dpgettext": (None, (3, 8)),
+    "gettext.npgettext": (None, (3, 8)),
+    "gettext.pgettext": (None, (3, 8)),
+    "gzip.GzipFile.peek": (None, (3, 2)),
+    "gzip.compress": (None, (3, 2)),
+    "gzip.decompress": (None, (3, 2)),
+    "hashlib.pbkdf2_hmac": ((2, 7), (3, 4)),
+    "hashlib.scrypt": (None, (3, 6)),
+    "heapq.heappushpop": ((2, 6), (3, 0)),
+    "heapq.merge": ((2, 6), (3, 0)),
+    "heapq.nlargest": ((2, 4), (3, 0)),
+    "heapq.nsmallest": ((2, 4), (3, 0)),
+    "hmac.compare_digest": ((2, 7), (3, 3)),
+    "hmac.digest": (None, (3, 7)),
+    "importlib.machinery.ExtensionFileLoader.create_module": (None, (3, 5)),
+    "importlib.machinery.ExtensionFileLoader.exec_module": (None, (3, 5)),
+    "importlib.machinery.ExtensionFileLoader.get_filename": (None, (3, 4)),
+    "importlib.machinery.FileFinder.find_spec": (None, (3, 4)),
+    "importlib.machinery.PathFinder.find_spec": (None, (3, 4)),
+    "importlib.machinery.all_suffixes": (None, (3, 3)),
+    "importlib.util.cache_from_source": (None, (3, 4)),
+    "importlib.util.decode_source": (None, (3, 4)),
+    "importlib.util.find_spec": (None, (3, 4)),
+    "importlib.util.module_from_spec": (None, (3, 5)),
+    "importlib.util.resolve_name": (None, (3, 3)),
+    "importlib.util.source_from_cache": (None, (3, 4)),
+    "importlib.util.source_hash": (None, (3, 7)),
+    "importlib.util.spec_from_file_location": (None, (3, 4)),
+    "importlib.util.spec_from_loader": (None, (3, 4)),
+    "inspect.BoundArguments.apply_defaults": (None, (3, 6)),
+    "inspect.cleandoc": ((2, 6), (3, 0)),
+    "inspect.getattr_static": (None, (3, 2)),
+    "inspect.getcallargs": ((2, 7), (3, 2)),
+    "inspect.getclosurevars": (None, (3, 3)),
+    "inspect.getcoroutinelocals": (None, (3, 5)),
+    "inspect.getcoroutinestate": (None, (3, 5)),
+    "inspect.getgeneratorlocals": (None, (3, 2)),
+    "inspect.getgeneratorstate": (None, (3, 2)),
+    "inspect.isabstract": ((2, 6), (3, 0)),
+    "inspect.isasyncgen": (None, (3, 6)),
+    "inspect.isasyncgenfunction": (None, (3, 6)),
+    "inspect.isawaitable": (None, (3, 5)),
+    "inspect.iscoroutine": (None, (3, 5)),
+    "inspect.iscoroutinefunction": (None, (3, 5)),
+    "inspect.isdatadescriptor": ((2, 3), (3, 0)),
+    "inspect.isgenerator": ((2, 6), (3, 0)),
+    "inspect.isgeneratorfunction": ((2, 6), (3, 0)),
+    "inspect.isgetsetdescriptor": ((2, 5), (3, 0)),
+    "inspect.ismemberdescriptor": ((2, 5), (3, 0)),
+    "inspect.signature": (None, (3, 3)),
+    "inspect.unwrap": (None, (3, 4)),
+    "io.BufferedIOBase.detach": ((2, 7), (3, 1)),
+    "io.BufferedIOBase.readinto1": (None, (3, 5)),
+    "io.BytesIO.getbuffer": (None, (3, 2)),
+    "io.BytesIO.readinto1": (None, (3, 5)),
+    "io.TextIOBase.detach": ((2, 7), (3, 1)),
+    "itertools.accumulate": (None, (3, 2)),
+    "itertools.combinations": ((2, 6), (3, 0)),
+    "itertools.combinations_with_replacement": ((2, 7), (3, 1)),
+    "itertools.compress": ((2, 7), (3, 1)),
+    "itertools.groupby": ((2, 4), (3, 0)),
+    "itertools.izip_longest": ((2, 6), (3, 0)),
+    "itertools.permutations": ((2, 6), (3, 0)),
+    "itertools.product": ((2, 6), (3, 0)),
+    "itertools.tee": ((2, 4), (3, 0)),
+    "logging.Logger.getChild": ((2, 7), (3, 2)),
+    "logging.Logger.hasHandlers": (None, (3, 2)),
+    "logging.getLogRecordFactory": (None, (3, 2)),
+    "logging.setLogRecordFactory": (None, (3, 2)),
+    "math.acosh": ((2, 6), (3, 0)),
+    "math.asinh": ((2, 6), (3, 0)),
+    "math.atanh": ((2, 6), (3, 0)),
+    "math.comb": (None, (3, 8)),
+    "math.copysign": ((2, 6), (3, 0)),
+    "math.dist": (None, (3, 8)),
+    "math.erf": ((2, 7), (3, 2)),
+    "math.erfc": ((2, 7), (3, 2)),
+    "math.expm1": ((2, 7), (3, 2)),
+    "math.fsum": ((2, 6), (3, 0)),
+    "math.gamma": ((2, 7), (3, 2)),
+    "math.gcd": (None, (3, 5)),
+    "math.isclose": (None, (3, 5)),
+    "math.isfinite": (None, (3, 2)),
+    "math.isinf": ((2, 6), (3, 0)),
+    "math.isnan": ((2, 6), (3, 0)),
+    "math.isqrt": (None, (3, 8)),
+    "math.lgamma": ((2, 7), (3, 2)),
+    "math.log1p": ((2, 6), (3, 0)),
+    "math.log2": (None, (3, 3)),
+    "math.perm": (None, (3, 8)),
+    "math.prod": (None, (3, 8)),
+    "math.remainder": (None, (3, 7)),
+    "math.trunc": ((2, 6), (3, 0)),
+    "mmap.mmap.madvise": (None, (3, 8)),
+    "multiprocessing.Pool.starmap": (None, (3, 3)),
+    "multiprocessing.Pool.starmap_async": (None, (3, 3)),
+    "multiprocessing.connection.wait": (None, (3, 3)),
+    "multiprocessing.get_all_start_methods": (None, (3, 4)),
+    "multiprocessing.get_context": (None, (3, 4)),
+    "multiprocessing.get_start_method": (None, (3, 4)),
+    "multiprocessing.set_start_method": (None, (3, 4)),
+    "os.add_dll_directory": (None, (3, 8)),
+    "os.fsdecode": (None, (3, 2)),
+    "os.fsencode": (None, (3, 2)),
+    "os.fspath": (None, (3, 6)),
+    "os.get_blocking": (None, (3, 5)),
+    "os.get_exec_path": (None, (3, 2)),
+    "os.get_handle_inheritable": (None, (3, 4)),
+    "os.get_inheritable": (None, (3, 4)),
+    "os.get_terminal_size": (None, (3, 3)),
+    "os.getenvb": (None, (3, 2)),
+    "os.getgrouplist": (None, (3, 3)),
+    "os.getpgid": ((2, 3), (3, 0)),
+    "os.getpriority": (None, (3, 3)),
+    "os.getresgid": ((2, 7), (3, 0)),
+    "os.getresuid": ((2, 7), (3, 0)),
+    "os.getsid": ((2, 4), (3, 0)),
+    "os.getxattr": (None, (3, 3)),
+    "os.initgroups": ((2, 7), (3, 2)),
+    "os.listxattr": (None, (3, 3)),
+    "os.lockf": (None, (3, 3)),
+    "os.memfd_create": (None, (3, 8)),
+    "os.path.commonpath": (None, (3, 5)),
+    "os.path.getctime": ((2, 3), (3, 0)),
+    "os.path.ismount": (None, (3, 4)),
+    "os.path.lexists": ((2, 4), (3, 0)),
+    "os.path.realpath": ((2, 2), (3, 0)),
+    "os.path.relpath": ((2, 6), (3, 0)),
+    "os.pipe2": (None, (3, 3)),
+    "os.posix_fadvise": (None, (3, 3)),
+    "os.posix_fallocate": (None, (3, 3)),
+    "os.pread": (None, (3, 3)),
+    "os.pwrite": (None, (3, 3)),
+    "os.readv": (None, (3, 3)),
+    "os.removexattr": (None, (3, 3)),
+    "os.sendfile": (None, (3, 3)),
+    "os.set_blocking": (None, (3, 5)),
+    "os.set_handle_inheritable": (None, (3, 4)),
+    "os.set_inheritable": (None, (3, 4)),
+    "os.setgroups": ((2, 2), (3, 0)),
+    "os.setpriority": (None, (3, 3)),
+    "os.setresgid": ((2, 7), (3, 2)),
+    "os.setresuid": ((2, 7), (3, 2)),
+    "os.setxattr": (None, (3, 3)),
+    "os.writev": (None, (3, 3)),
+    "pathlib.Path.expanduser": (None, (3, 5)),
+    "pathlib.Path.home": (None, (3, 5)),
+    "pathlib.Path.is_mount": (None, (3, 7)),
+    "pathlib.Path.link_to": (None, (3, 8)),
+    "pathlib.Path.read_bytes": (None, (3, 5)),
+    "pathlib.Path.read_text": (None, (3, 5)),
+    "pathlib.Path.samefile": (None, (3, 5)),
+    "pathlib.Path.write_bytes": (None, (3, 5)),
+    "pathlib.Path.write_text": (None, (3, 5)),
+    "pickle.Pickler.reducer_override": (None, (3, 8)),
+    "pickletools.optimize": ((2, 6), (3, 0)),
+    "pkgutil.get_data": ((2, 6), (3, 0)),
+    "platform.linux_distribution": ((2, 6), (3, 0)),
+    "platform.python_branch": ((2, 6), (3, 0)),
+    "platform.python_implementation": ((2, 6), (3, 0)),
+    "platform.python_revision": ((2, 6), (3, 0)),
+    "plistlib.dump": (None, (3, 4)),
+    "plistlib.dumps": (None, (3, 4)),
+    "plistlib.load": (None, (3, 4)),
+    "plistlib.loads": (None, (3, 4)),
+    "runpy.run_path": ((2, 7), (3, 2)),
+    "shlex.join": (None, (3, 8)),
+    "shlex.pop_source": ((2, 1), (3, 0)),
+    "shlex.push_source": ((2, 1), (3, 0)),
+    "shlex.quote": (None, (3, 3)),
+    "shlex.split": ((2, 3), (3, 0)),
+    "socket.CMSG_LEN": (None, (3, 3)),
+    "socket.CMSG_SPACE": (None, (3, 3)),
+    "socket.close": (None, (3, 7)),
+    "socket.create_server": (None, (3, 8)),
+    "socket.detach": (None, (3, 2)),
+    "socket.fromshare": (None, (3, 3)),
+    "socket.get_inheritable": (None, (3, 4)),
+    "socket.getblocking": (None, (3, 7)),
+    "socket.has_dualstack_ipv6": (None, (3, 8)),
+    "socket.if_indextoname": (None, (3, 3)),
+    "socket.if_nameindex": (None, (3, 3)),
+    "socket.if_nametoindex": (None, (3, 3)),
+    "socket.recvmsg": (None, (3, 3)),
+    "socket.recvmsg_into": (None, (3, 3)),
+    "socket.sendfile": (None, (3, 5)),
+    "socket.sendmsg": (None, (3, 3)),
+    "socket.sendmsg_afalg": (None, (3, 6)),
+    "socket.set_inheritable": (None, (3, 4)),
+    "socket.sethostname": (None, (3, 3)),
+    "socket.share": (None, (3, 3)),
+    "sqlite3.Connection.enable_load_extension": ((2, 7), (3, 2)),
+    "sqlite3.Connection.iter_dump": ((2, 6), (3, 0)),
+    "sqlite3.Connection.load_extension": ((2, 7), (3, 2)),
+    "sqlite3.Connection.set_progress_handler": ((2, 6), (3, 0)),
+    "sqlite3.Connection.set_trace_callback": (None, (3, 3)),
+    "sqlite3.Row.keys": ((2, 6), (3, 0)),
+    "ssl.RAND_bytes": (None, (3, 3)),
+    "ssl.RAND_pseudo_bytes": (None, (3, 3)),
+    "ssl.SSLContext.cert_store_stats": (None, (3, 4)),
+    "ssl.SSLContext.get_ca_certs": (None, (3, 4)),
+    "ssl.SSLContext.get_ciphers": (None, (3, 6)),
+    "ssl.SSLContext.load_default_certs": (None, (3, 4)),
+    "ssl.SSLContext.load_dh_params": (None, (3, 3)),
+    "ssl.SSLContext.set_alpn_protocols": ((2, 7), (3, 5)),
+    "ssl.SSLContext.set_ecdh_curve": (None, (3, 3)),
+    "ssl.SSLContext.set_npn_protocols": ((2, 7), (3, 3)),
+    "ssl.SSLContext.set_servername_callback": (None, (3, 4)),
+    "ssl.SSLSocket.verify_client_post_handshake": (None, (3, 8)),
+    "ssl._https_verify_certificates": ((2, 7), None),
+    "ssl.compression": ((2, 7), (3, 3)),
+    "ssl.create_default_context": ((2, 7), (3, 4)),
+    "ssl.enum_certificates": ((2, 7), (3, 4)),
+    "ssl.enum_crls": ((2, 7), (3, 4)),
+    "ssl.get_channel_binding": ((2, 7), (3, 3)),
+    "ssl.get_default_verify_paths": ((2, 7), (3, 4)),
+    "ssl.match_hostname": ((2, 7), (3, 2)),
+    "ssl.selected_alpn_protocol": ((2, 7), (3, 5)),
+    "ssl.selected_npn_protocol": ((2, 7), (3, 3)),
+    "ssl.shared_ciphers": (None, (3, 5)),
+    "ssl.version": ((2, 7), (3, 5)),
+    "statistics.fmean": (None, (3, 8)),
+    "statistics.geometric_mean": (None, (3, 8)),
+    "statistics.harmonic_mean": (None, (3, 6)),
+    "statistics.multimode": (None, (3, 8)),
+    "statistics.quantiles": (None, (3, 8)),
+    "subprocess.CompletedProcess.check_returncode": (None, (3, 5)),
+    "subprocess.Popen.kill": ((2, 6), (3, 0)),
+    "subprocess.Popen.send_signal": ((2, 6), (3, 0)),
+    "subprocess.Popen.terminate": ((2, 6), (3, 0)),
+    "subprocess.check_call": ((2, 5), (3, 0)),
+    "subprocess.check_output": ((2, 7), (3, 1)),
+    "subprocess.run": (None, (3, 5)),
+    "sys.exc_clear": ((2, 3), None),
+    "sys.getcheckinterval": ((2, 3), (3, 0)),
+    "sys.getdefaultencoding": ((2, 0), (3, 0)),
+    "sys.getdlopenflags": ((2, 2), (3, 0)),
+    "sys.getfilesystemencoding": ((2, 3), (3, 0)),
+    "sys.getprofile": ((2, 6), (3, 0)),
+    "sys.getsizeof": ((2, 6), (3, 0)),
+    "sys.gettrace": ((2, 6), (3, 0)),
+    "sys.getwindowsversion": ((2, 3), (3, 0)),
+    "sys.unraisablehook": (None, (3, 8)),
+    "tarfile.TarFile.extractall": ((2, 5), (3, 0)),
+    "tarfile.TarInfo.fromtarfile": ((2, 6), (3, 0)),
+    "textwrap.indent": (None, (3, 3)),
+    "textwrap.shorten": (None, (3, 4)),
+    "threading.excepthook": (None, (3, 8)),
+    "threading.get_ident": (None, (3, 3)),
+    "threading.get_native_id": (None, (3, 8)),
+    "threading.main_thread": (None, (3, 4)),
+    "time.clock_getres": (None, (3, 3)),
+    "time.clock_gettime": (None, (3, 3)),
+    "time.clock_gettime_ns": (None, (3, 7)),
+    "time.clock_settime": (None, (3, 3)),
+    "time.clock_settime_ns": (None, (3, 7)),
+    "time.get_clock_info": (None, (3, 3)),
+    "time.monotonic": (None, (3, 3)),
+    "time.monotonic_ns": (None, (3, 7)),
+    "time.perf_counter": (None, (3, 3)),
+    "time.perf_counter_ns": (None, (3, 7)),
+    "time.process_time": (None, (3, 3)),
+    "time.process_time_ns": (None, (3, 7)),
+    "time.pthread_getcpuclockid": (None, (3, 7)),
+    "time.thread_time": (None, (3, 7)),
+    "time.thread_time_ns": (None, (3, 7)),
+    "time.time_ns": (None, (3, 7)),
+    "timeit.repeat": ((2, 6), (3, 0)),
+    "timeit.timeit": ((2, 6), (3, 0)),
+    "tokenize.generate_tokens": ((2, 2), (3, 0)),
+    "tokenize.open": (None, (3, 2)),
+    "tokenize.untokenize": ((2, 5), (3, 0)),
+    "typing.NewType": (None if not bp("typing") else (2, 7), (3, 5)),
+    "typing.get_args": (None, (3, 8)),
+    "typing.get_origin": (None, (3, 8)),
+    "unicodedata.is_normalized": (None, (3, 8)),
+    "unittest.TestCase.addCleanup": ((2, 7), (3, 1)),
+    "unittest.TestCase.addTypeEqualityFunc": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertDictContainsSubset": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertDictEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertGreater": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertGreaterEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertIn": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertIs": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertIsInstance": ((2, 7), (3, 2)),
+    "unittest.TestCase.assertIsNone": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertIsNot": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertIsNotNone": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertItemsEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertLess": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertLessEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertListEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertMultilineEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertNotIn": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertNotIsInstance": ((2, 7), (3, 2)),
+    "unittest.TestCase.assertNotRegex": (None, (3, 1)),
+    "unittest.TestCase.assertNotRegexpMatches": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertRaisesRegexp": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertRegex": (None, (3, 1)),
+    "unittest.TestCase.assertRegexpMatches": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertSequenceEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertSetEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.assertTupleEqual": ((2, 7), (3, 1)),
+    "unittest.TestCase.doCleanups": ((2, 7), (3, 1)),
+    "unittest.TestCase.longMessage": ((2, 7), (3, 1)),
+    "unittest.TestCase.maxDiff": ((2, 7), (3, 2)),
+    "unittest.TestLoader.discover": ((2, 7), (3, 2)),
+    "unittest.TestResult.startTestRun": ((2, 7), (3, 1)),
+    "unittest.TestResult.stopTestRun": ((2, 7), (3, 1)),
+    "unittest.addModuleCleanup": (None, (3, 8)),
+    "unittest.doModuleCleanups": (None, (3, 8)),
+    "warnings.warnpy3k": ((2, 6), (3, 0)),
+    "weakref.WeakKeyDictionary.iterkeyrefs": ((2, 5), (3, 0)),
+    "weakref.WeakKeyDictionary.keyrefs": ((2, 5), (3, 0)),
+    "weakref.WeakValueDictionary.itervaluerefs": ((2, 5), (3, 0)),
+    "weakref.WeakValueDictionary.valuerefs": ((2, 5), (3, 0)),
+    "wsgiref.handlers.read_environ": (None, (3, 2)),
+    "xml.etree.ElementTree.Element.extend": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.Element.iter": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.Element.iterfind": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.Element.itertext": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.ElementTree.iterfind": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.TreeBuilder.comment": (None, (3, 8)),
+    "xml.etree.ElementTree.TreeBuilder.doctype": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.TreeBuilder.end_ns": (None, (3, 8)),
+    "xml.etree.ElementTree.TreeBuilder.start_ns": (None, (3, 8)),
+    "xml.etree.ElementTree.canonicalize": (None, (3, 8)),
+    "xml.etree.ElementTree.fromstringlist": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.register_namespace": ((2, 7), (3, 2)),
+    "xml.etree.ElementTree.tostringlist": ((2, 7), (3, 2)),
+    "xmlrpc.server.SimpleXMLRPCServer.register_introspection_functions": (None, (3, 0)),
+    "zipfile.ZipFile.extract": ((2, 6), (3, 0)),
+    "zipfile.ZipFile.extractall": ((2, 6), (3, 0)),
+    "zipfile.ZipFile.open": ((2, 6), (3, 0)),
+    "zipfile.ZipFile.setpassword": ((2, 6), (3, 0)),
+    "zipfile.ZipInfo.from_file": (None, (3, 6)),
+    "zipfile.ZipInfo.is_dir": (None, (3, 6)),
+    "zipimport.zipimporter.get_filename": ((2, 7), (3, 1)),
 
-  # Variables and Constants
-  "SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.encode_threshold": ((2, 7), None),
-  "SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.rpc_paths": ((2, 5), None),
-  "__future__.absolute_import": ((2, 5), (3, 0)),
-  "__future__.division": ((2, 2), (3, 0)),
-  "__future__.generator_stop": (None, (3, 5)),
-  "__future__.generators": ((2, 2), (3, 0)),
-  "__future__.nested_scopes": ((2, 1), (3, 0)),
-  "__future__.print_function": ((2, 6), (3, 0)),
-  "__future__.unicode_literals": ((2, 6), (3, 0)),
-  "__future__.with_statement": ((2, 5), (3, 0)),
-  "bz2.BZ2Decompressor.eof": (None, (3, 3)),
-  "bz2.BZ2Decompressor.needs_input": (None, (3, 5)),
-  "calendar.HTMLCalendar.cssclass_month_head": (None, (3, 7)),
-  "calendar.HTMLCalendar.cssclass_noday": (None, (3, 7)),
-  "calendar.HTMLCalendar.cssclass_year": (None, (3, 7)),
-  "calendar.HTMLCalendar.cssclass_year_head": (None, (3, 7)),
-  "calendar.HTMLCalendar.cssclasses_weekday_head": (None, (3, 7)),
-  "cmath.inf": (None, (3, 6)),
-  "cmath.infj": (None, (3, 6)),
-  "cmath.nan": (None, (3, 6)),
-  "cmath.nanj": (None, (3, 6)),
-  "cmath.tau": (None, (3, 6)),
-  "collections.deque.maxlen": ((2, 7), (3, 1)),
-  "contextvars.ContextVar.name": (None, (3, 7)),
-  "crypt.METHOD_BLOWFISH": (None, (3, 7)),
-  "crypt.METHOD_CRYPT": (None, (3, 3)),
-  "crypt.METHOD_MD5": (None, (3, 3)),
-  "crypt.METHOD_SHA256": (None, (3, 3)),
-  "crypt.METHOD_SHA512": (None, (3, 3)),
-  "crypt.methods": (None, (3, 3)),
-  "curses.A_ITALIC": (None, (3, 7)),
-  "curses.ncurses_version": (None, (3, 8)),
-  "datetime.datetime.fold": (None, (3, 6)),
-  "datetime.time.fold": (None, (3, 6)),
-  "dis.Instruction.is_jump_target": (None, (3, 4)),
-  "doctest.FAIL_FAST": (None, (3, 4)),
-  "hashlib.algorithms": ((2, 7), None),
-  "hashlib.algorithms_available": ((2, 7), (3, 2)),
-  "hashlib.algorithms_guaranteed": ((2, 7), (3, 2)),
-  "hmac.HMAC.block_size": (None, (3, 4)),
-  "hmac.HMAC.name": (None, (3, 4)),
-  "importlib.machinery.BYTECODE_SUFFIXES": (None, (3, 3)),
-  "importlib.machinery.DEBUG_BYTECODE_SUFFIXES": (None, (3, 3)),
-  "importlib.machinery.EXTENSION_SUFFIXES": (None, (3, 3)),
-  "importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES": (None, (3, 3)),
-  "importlib.machinery.SOURCE_SUFFIXES": (None, (3, 3)),
-  "importlib.util.MAGIC_NUMBER": (None, (3, 4)),
-  "inspect.CO_ASYNC_GENERATOR": (None, (3, 6)),
-  "inspect.CO_COROUTINE": (None, (3, 5)),
-  "inspect.CO_ITERABLE_COROUTINE": (None, (3, 5)),
-  "ipaddress.IPv4Address.is_global": (None, (3, 4)),
-  "ipaddress.IPv4Address.reverse_pointer": (None, (3, 5)),
-  "ipaddress.IPv6Address.is_global": (None, (3, 4)),
-  "logging.lastResort": (None, (3, 2)),
-  "math.inf": (None, (3, 5)),
-  "math.nan": (None, (3, 5)),
-  "math.tau": (None, (3, 6)),
-  "mmap.ACCESS_DEFAULT": (None, (3, 7)),
-  "mmap.MADV_AUTOSYNC": (None, (3, 8)),
-  "mmap.MADV_CORE": (None, (3, 8)),
-  "mmap.MADV_DODUMP": (None, (3, 8)),
-  "mmap.MADV_DOFORK": (None, (3, 8)),
-  "mmap.MADV_DONTDUMP": (None, (3, 8)),
-  "mmap.MADV_DONTFORK": (None, (3, 8)),
-  "mmap.MADV_DONTNEED": (None, (3, 8)),
-  "mmap.MADV_FREE": (None, (3, 8)),
-  "mmap.MADV_HUGEPAGE": (None, (3, 8)),
-  "mmap.MADV_HWPOISON": (None, (3, 8)),
-  "mmap.MADV_MERGEABLE": (None, (3, 8)),
-  "mmap.MADV_NOCORE": (None, (3, 8)),
-  "mmap.MADV_NOHUGEPAGE": (None, (3, 8)),
-  "mmap.MADV_NORMAL": (None, (3, 8)),
-  "mmap.MADV_NOSYNC": (None, (3, 8)),
-  "mmap.MADV_PROTECT": (None, (3, 8)),
-  "mmap.MADV_RANDOM": (None, (3, 8)),
-  "mmap.MADV_REMOVE": (None, (3, 8)),
-  "mmap.MADV_SEQUENTIAL": (None, (3, 8)),
-  "mmap.MADV_SOFT_OFFLINE": (None, (3, 8)),
-  "mmap.MADV_UNMERGEABLE": (None, (3, 8)),
-  "mmap.MADV_WILLNEED": (None, (3, 8)),
-  "multiprocessing.Process.sentinel": (None, (3, 3)),
-  "os.F_LOCK": (None, (3, 3)),
-  "os.F_TEST": (None, (3, 3)),
-  "os.F_TLOCK": (None, (3, 3)),
-  "os.F_ULOCK": (None, (3, 3)),
-  "os.MFD_ALLOW_SEALING": (None, (3, 8)),
-  "os.MFD_CLOEXEC": (None, (3, 8)),
-  "os.MFD_HUGETLB": (None, (3, 8)),
-  "os.MFD_HUGE_16GB": (None, (3, 8)),
-  "os.MFD_HUGE_16MB": (None, (3, 8)),
-  "os.MFD_HUGE_1GB": (None, (3, 8)),
-  "os.MFD_HUGE_1MB": (None, (3, 8)),
-  "os.MFD_HUGE_256MB": (None, (3, 8)),
-  "os.MFD_HUGE_2GB": (None, (3, 8)),
-  "os.MFD_HUGE_2MB": (None, (3, 8)),
-  "os.MFD_HUGE_32MB": (None, (3, 8)),
-  "os.MFD_HUGE_512KB": (None, (3, 8)),
-  "os.MFD_HUGE_512MB": (None, (3, 8)),
-  "os.MFD_HUGE_64KB": (None, (3, 8)),
-  "os.MFD_HUGE_8MB": (None, (3, 8)),
-  "os.MFD_HUGE_MASK": (None, (3, 8)),
-  "os.MFD_HUGE_SHIFT": (None, (3, 8)),
-  "os.O_CLOEXEC": (None, (3, 3)),
-  "os.O_PATH": (None, (3, 4)),
-  "os.O_TMPFILE": (None, (3, 4)),
-  "os.POSIX_FADV_DONTNEED": (None, (3, 3)),
-  "os.POSIX_FADV_NOREUSE": (None, (3, 3)),
-  "os.POSIX_FADV_NORMAL": (None, (3, 3)),
-  "os.POSIX_FADV_RANDOM": (None, (3, 3)),
-  "os.POSIX_FADV_SEQUENTIAL": (None, (3, 3)),
-  "os.POSIX_FADV_WILLNEED": (None, (3, 3)),
-  "os.PRIO_PGRP": (None, (3, 3)),
-  "os.PRIO_PROCESS": (None, (3, 3)),
-  "os.PRIO_USER": (None, (3, 3)),
-  "os.SF_MNOWAIT": (None, (3, 3)),
-  "os.SF_NODISKIO": (None, (3, 3)),
-  "os.SF_SYNC": (None, (3, 3)),
-  "os.XATTR_CREATE": (None, (3, 3)),
-  "os.XATTR_REPLACE": (None, (3, 3)),
-  "os.XATTR_SIZE_MAX": (None, (3, 3)),
-  "os.environb": (None, (3, 2)),
-  "os.path.supports_unicode_filenames": ((2, 3), (3, 0)),
-  "os.supports_bytes_environ": (None, (3, 2)),
-  "plistlib.FMT_BINARY": (None, (3, 4)),
-  "plistlib.FMT_XML": (None, (3, 4)),
-  "shlex.eof": ((2, 3), (3, 0)),
-  "shlex.escape": ((2, 3), (3, 0)),
-  "shlex.escapedquotes": ((2, 3), (3, 0)),
-  "shlex.punctuation_chars": (None, (3, 6)),
-  "shlex.whitespace_split": ((2, 3), (3, 0)),
-  "ssl.ALERT_DESCRIPTION_HANDSHAKE_FAILURE": ((2, 7), (3, 4)),
-  "ssl.ALERT_DESCRIPTION_INTERNAL_ERROR": ((2, 7), (3, 4)),
-  "ssl.CHANNEL_BINDING_TYPES": ((2, 7), (3, 3)),
-  "ssl.HAS_ALPN": ((2, 7), (3, 5)),
-  "ssl.HAS_ECDH": ((2, 7), (3, 3)),
-  "ssl.HAS_NPN": ((2, 7), (3, 3)),
-  "ssl.HAS_SNI": ((2, 7), (3, 2)),
-  "ssl.HAS_TLSv1_3": ((2, 7), (3, 6)),
-  "ssl.OPENSSL_VERSION": ((2, 7), (3, 2)),
-  "ssl.OPENSSL_VERSION_INFO": ((2, 7), (3, 2)),
-  "ssl.OPENSSL_VERSION_NUMBER": ((2, 7), (3, 2)),
-  "ssl.OP_ALL": ((2, 7), (3, 2)),
-  "ssl.OP_CIPHER_SERVER_PREFERENCE": ((2, 7), (3, 3)),
-  "ssl.OP_NO_COMPRESSION": ((2, 7), (3, 3)),
-  "ssl.OP_NO_SSLv2": ((2, 7), (3, 2)),
-  "ssl.OP_NO_SSLv3": ((2, 7), (3, 2)),
-  "ssl.OP_NO_TLSv1": ((2, 7), (3, 2)),
-  "ssl.OP_NO_TLSv1_1": ((2, 7), (3, 4)),
-  "ssl.OP_NO_TLSv1_2": ((2, 7), (3, 4)),
-  "ssl.OP_NO_TLSv1_3": ((2, 7), (3, 6)),
-  "ssl.OP_SINGLE_DH_USE": ((2, 7), (3, 3)),
-  "ssl.OP_SINGLE_ECDH_USE": ((2, 7), (3, 3)),
-  "ssl.PROTOCOL_TLS": ((2, 7), (3, 6)),
-  "ssl.PROTOCOL_TLSv1_1": ((2, 7), (3, 4)),
-  "ssl.PROTOCOL_TLSv1_2": ((2, 7), (3, 4)),
-  "ssl.SSLContext.check_hostname": (None, (3, 4)),
-  "ssl.SSLContext.hostname_checks_common_name": (None, (3, 7)),
-  "ssl.SSLContext.post_handshake_auth": (None, (3, 8)),
-  "ssl.SSLContext.verify_flags": (None, (3, 4)),
-  "ssl.SSLError.library": ((2, 7), (3, 3)),
-  "ssl.SSLError.reason": ((2, 7), (3, 3)),
-  "ssl.SSLSocket.context": ((2, 7), (3, 2)),
-  "ssl.SSLSocket.server_hostname": (None, (3, 2)),
-  "ssl.SSLSocket.server_side": (None, (3, 2)),
-  "ssl.SSLSocket.session": (None, (3, 2)),
-  "ssl.SSLSocket.session_reused": (None, (3, 2)),
-  "ssl.VERIFY_CRL_CHECK_CHAIN": ((2, 7), (3, 4)),
-  "ssl.VERIFY_CRL_CHECK_LEAF": ((2, 7), (3, 4)),
-  "ssl.VERIFY_DEFAULT": ((2, 7), (3, 4)),
-  "ssl.VERIFY_X509_STRICT": ((2, 7), (3, 4)),
-  "ssl.VERIFY_X509_TRUSTED_FIRST": ((2, 7), (3, 4)),
-  "subprocess.DEVNULL": (None, (3, 3)),
-  "subprocess.Popen.args": (None, (3, 3)),
-  "sys.api_version": ((2, 3), (3, 0)),
-  "sys.flags": ((2, 6), (3, 0)),
-  "sys.float_info": ((2, 6), (3, 0)),
-  "sys.float_repr_style": ((2, 7), (3, 0)),
-  "sys.long_info": ((2, 7), None),
-  "sys.py3kwarning": ((2, 6), None),
-  "sys.subversion": ((2, 5), None),
-  "sys.version_info": ((2, 0), (3, 0)),
-  "tarfile.TarFile.pax_headers": ((2, 6), (3, 0)),
-  "tarfile.TarInfo.pax_headers": ((2, 6), (3, 0)),
-  "threading.TIMEOUT_MAX": (None, (3, 2)),
-  "time.CLOCK_BOOTTIME": (None, (3, 7)),
-  "time.CLOCK_HIGHRES": (None, (3, 3)),
-  "time.CLOCK_MONOTONIC": (None, (3, 3)),
-  "time.CLOCK_MONOTONIC_RAW": (None, (3, 3)),
-  "time.CLOCK_PROCESS_CPUTIME_ID": (None, (3, 3)),
-  "time.CLOCK_PROF": (None, (3, 7)),
-  "time.CLOCK_REALTIME": (None, (3, 3)),
-  "time.CLOCK_THREAD_CPUTIME_ID": (None, (3, 3)),
-  "time.CLOCK_UPTIME": (None, (3, 7)),
-  "time.CLOCK_UPTIME_RAW": (None, (3, 8)),
-  "typing.TYPE_CHECKING": (None, (3, 5)),
-  "unittest.TestResult.buffer": ((2, 7), (3, 0)),
-  "unittest.TestResult.failfast": ((2, 7), (3, 0)),
-  "unittest.TestResult.skipped": ((2, 7), (3, 0)),
-  "xmlrpc.server.SimpleXMLRPCRequestHandler.rpc_paths": (None, (3, 0)),
-  "zipfile.ZIP_BZIP2": (None, (3, 3)),
-  "zipfile.ZIP_LZMA": (None, (3, 3)),
+    # Variables and Constants
+    "SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.encode_threshold": ((2, 7), None),
+    "SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.rpc_paths": ((2, 5), None),
+    "__future__.absolute_import": ((2, 5), (3, 0)),
+    "__future__.division": ((2, 2), (3, 0)),
+    "__future__.generator_stop": (None, (3, 5)),
+    "__future__.generators": ((2, 2), (3, 0)),
+    "__future__.nested_scopes": ((2, 1), (3, 0)),
+    "__future__.print_function": ((2, 6), (3, 0)),
+    "__future__.unicode_literals": ((2, 6), (3, 0)),
+    "__future__.with_statement": ((2, 5), (3, 0)),
+    "bz2.BZ2Decompressor.eof": (None, (3, 3)),
+    "bz2.BZ2Decompressor.needs_input": (None, (3, 5)),
+    "calendar.HTMLCalendar.cssclass_month_head": (None, (3, 7)),
+    "calendar.HTMLCalendar.cssclass_noday": (None, (3, 7)),
+    "calendar.HTMLCalendar.cssclass_year": (None, (3, 7)),
+    "calendar.HTMLCalendar.cssclass_year_head": (None, (3, 7)),
+    "calendar.HTMLCalendar.cssclasses_weekday_head": (None, (3, 7)),
+    "cmath.inf": (None, (3, 6)),
+    "cmath.infj": (None, (3, 6)),
+    "cmath.nan": (None, (3, 6)),
+    "cmath.nanj": (None, (3, 6)),
+    "cmath.tau": (None, (3, 6)),
+    "collections.deque.maxlen": ((2, 7), (3, 1)),
+    "contextvars.ContextVar.name": (None, (3, 7)),
+    "crypt.METHOD_BLOWFISH": (None, (3, 7)),
+    "crypt.METHOD_CRYPT": (None, (3, 3)),
+    "crypt.METHOD_MD5": (None, (3, 3)),
+    "crypt.METHOD_SHA256": (None, (3, 3)),
+    "crypt.METHOD_SHA512": (None, (3, 3)),
+    "crypt.methods": (None, (3, 3)),
+    "curses.A_ITALIC": (None, (3, 7)),
+    "curses.ncurses_version": (None, (3, 8)),
+    "datetime.datetime.fold": (None, (3, 6)),
+    "datetime.time.fold": (None, (3, 6)),
+    "dis.Instruction.is_jump_target": (None, (3, 4)),
+    "doctest.FAIL_FAST": (None, (3, 4)),
+    "hashlib.algorithms": ((2, 7), None),
+    "hashlib.algorithms_available": ((2, 7), (3, 2)),
+    "hashlib.algorithms_guaranteed": ((2, 7), (3, 2)),
+    "hmac.HMAC.block_size": (None, (3, 4)),
+    "hmac.HMAC.name": (None, (3, 4)),
+    "importlib.machinery.BYTECODE_SUFFIXES": (None, (3, 3)),
+    "importlib.machinery.DEBUG_BYTECODE_SUFFIXES": (None, (3, 3)),
+    "importlib.machinery.EXTENSION_SUFFIXES": (None, (3, 3)),
+    "importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES": (None, (3, 3)),
+    "importlib.machinery.SOURCE_SUFFIXES": (None, (3, 3)),
+    "importlib.util.MAGIC_NUMBER": (None, (3, 4)),
+    "inspect.CO_ASYNC_GENERATOR": (None, (3, 6)),
+    "inspect.CO_COROUTINE": (None, (3, 5)),
+    "inspect.CO_ITERABLE_COROUTINE": (None, (3, 5)),
+    "ipaddress.IPv4Address.is_global": (None, (3, 4)),
+    "ipaddress.IPv4Address.reverse_pointer": (None, (3, 5)),
+    "ipaddress.IPv6Address.is_global": (None, (3, 4)),
+    "logging.lastResort": (None, (3, 2)),
+    "math.inf": (None, (3, 5)),
+    "math.nan": (None, (3, 5)),
+    "math.tau": (None, (3, 6)),
+    "mmap.ACCESS_DEFAULT": (None, (3, 7)),
+    "mmap.MADV_AUTOSYNC": (None, (3, 8)),
+    "mmap.MADV_CORE": (None, (3, 8)),
+    "mmap.MADV_DODUMP": (None, (3, 8)),
+    "mmap.MADV_DOFORK": (None, (3, 8)),
+    "mmap.MADV_DONTDUMP": (None, (3, 8)),
+    "mmap.MADV_DONTFORK": (None, (3, 8)),
+    "mmap.MADV_DONTNEED": (None, (3, 8)),
+    "mmap.MADV_FREE": (None, (3, 8)),
+    "mmap.MADV_HUGEPAGE": (None, (3, 8)),
+    "mmap.MADV_HWPOISON": (None, (3, 8)),
+    "mmap.MADV_MERGEABLE": (None, (3, 8)),
+    "mmap.MADV_NOCORE": (None, (3, 8)),
+    "mmap.MADV_NOHUGEPAGE": (None, (3, 8)),
+    "mmap.MADV_NORMAL": (None, (3, 8)),
+    "mmap.MADV_NOSYNC": (None, (3, 8)),
+    "mmap.MADV_PROTECT": (None, (3, 8)),
+    "mmap.MADV_RANDOM": (None, (3, 8)),
+    "mmap.MADV_REMOVE": (None, (3, 8)),
+    "mmap.MADV_SEQUENTIAL": (None, (3, 8)),
+    "mmap.MADV_SOFT_OFFLINE": (None, (3, 8)),
+    "mmap.MADV_UNMERGEABLE": (None, (3, 8)),
+    "mmap.MADV_WILLNEED": (None, (3, 8)),
+    "multiprocessing.Process.sentinel": (None, (3, 3)),
+    "os.F_LOCK": (None, (3, 3)),
+    "os.F_TEST": (None, (3, 3)),
+    "os.F_TLOCK": (None, (3, 3)),
+    "os.F_ULOCK": (None, (3, 3)),
+    "os.MFD_ALLOW_SEALING": (None, (3, 8)),
+    "os.MFD_CLOEXEC": (None, (3, 8)),
+    "os.MFD_HUGETLB": (None, (3, 8)),
+    "os.MFD_HUGE_16GB": (None, (3, 8)),
+    "os.MFD_HUGE_16MB": (None, (3, 8)),
+    "os.MFD_HUGE_1GB": (None, (3, 8)),
+    "os.MFD_HUGE_1MB": (None, (3, 8)),
+    "os.MFD_HUGE_256MB": (None, (3, 8)),
+    "os.MFD_HUGE_2GB": (None, (3, 8)),
+    "os.MFD_HUGE_2MB": (None, (3, 8)),
+    "os.MFD_HUGE_32MB": (None, (3, 8)),
+    "os.MFD_HUGE_512KB": (None, (3, 8)),
+    "os.MFD_HUGE_512MB": (None, (3, 8)),
+    "os.MFD_HUGE_64KB": (None, (3, 8)),
+    "os.MFD_HUGE_8MB": (None, (3, 8)),
+    "os.MFD_HUGE_MASK": (None, (3, 8)),
+    "os.MFD_HUGE_SHIFT": (None, (3, 8)),
+    "os.O_CLOEXEC": (None, (3, 3)),
+    "os.O_PATH": (None, (3, 4)),
+    "os.O_TMPFILE": (None, (3, 4)),
+    "os.POSIX_FADV_DONTNEED": (None, (3, 3)),
+    "os.POSIX_FADV_NOREUSE": (None, (3, 3)),
+    "os.POSIX_FADV_NORMAL": (None, (3, 3)),
+    "os.POSIX_FADV_RANDOM": (None, (3, 3)),
+    "os.POSIX_FADV_SEQUENTIAL": (None, (3, 3)),
+    "os.POSIX_FADV_WILLNEED": (None, (3, 3)),
+    "os.PRIO_PGRP": (None, (3, 3)),
+    "os.PRIO_PROCESS": (None, (3, 3)),
+    "os.PRIO_USER": (None, (3, 3)),
+    "os.SF_MNOWAIT": (None, (3, 3)),
+    "os.SF_NODISKIO": (None, (3, 3)),
+    "os.SF_SYNC": (None, (3, 3)),
+    "os.XATTR_CREATE": (None, (3, 3)),
+    "os.XATTR_REPLACE": (None, (3, 3)),
+    "os.XATTR_SIZE_MAX": (None, (3, 3)),
+    "os.environb": (None, (3, 2)),
+    "os.path.supports_unicode_filenames": ((2, 3), (3, 0)),
+    "os.supports_bytes_environ": (None, (3, 2)),
+    "plistlib.FMT_BINARY": (None, (3, 4)),
+    "plistlib.FMT_XML": (None, (3, 4)),
+    "shlex.eof": ((2, 3), (3, 0)),
+    "shlex.escape": ((2, 3), (3, 0)),
+    "shlex.escapedquotes": ((2, 3), (3, 0)),
+    "shlex.punctuation_chars": (None, (3, 6)),
+    "shlex.whitespace_split": ((2, 3), (3, 0)),
+    "ssl.ALERT_DESCRIPTION_HANDSHAKE_FAILURE": ((2, 7), (3, 4)),
+    "ssl.ALERT_DESCRIPTION_INTERNAL_ERROR": ((2, 7), (3, 4)),
+    "ssl.CHANNEL_BINDING_TYPES": ((2, 7), (3, 3)),
+    "ssl.HAS_ALPN": ((2, 7), (3, 5)),
+    "ssl.HAS_ECDH": ((2, 7), (3, 3)),
+    "ssl.HAS_NPN": ((2, 7), (3, 3)),
+    "ssl.HAS_SNI": ((2, 7), (3, 2)),
+    "ssl.HAS_TLSv1_3": ((2, 7), (3, 6)),
+    "ssl.OPENSSL_VERSION": ((2, 7), (3, 2)),
+    "ssl.OPENSSL_VERSION_INFO": ((2, 7), (3, 2)),
+    "ssl.OPENSSL_VERSION_NUMBER": ((2, 7), (3, 2)),
+    "ssl.OP_ALL": ((2, 7), (3, 2)),
+    "ssl.OP_CIPHER_SERVER_PREFERENCE": ((2, 7), (3, 3)),
+    "ssl.OP_NO_COMPRESSION": ((2, 7), (3, 3)),
+    "ssl.OP_NO_SSLv2": ((2, 7), (3, 2)),
+    "ssl.OP_NO_SSLv3": ((2, 7), (3, 2)),
+    "ssl.OP_NO_TLSv1": ((2, 7), (3, 2)),
+    "ssl.OP_NO_TLSv1_1": ((2, 7), (3, 4)),
+    "ssl.OP_NO_TLSv1_2": ((2, 7), (3, 4)),
+    "ssl.OP_NO_TLSv1_3": ((2, 7), (3, 6)),
+    "ssl.OP_SINGLE_DH_USE": ((2, 7), (3, 3)),
+    "ssl.OP_SINGLE_ECDH_USE": ((2, 7), (3, 3)),
+    "ssl.PROTOCOL_TLS": ((2, 7), (3, 6)),
+    "ssl.PROTOCOL_TLSv1_1": ((2, 7), (3, 4)),
+    "ssl.PROTOCOL_TLSv1_2": ((2, 7), (3, 4)),
+    "ssl.SSLContext.check_hostname": (None, (3, 4)),
+    "ssl.SSLContext.hostname_checks_common_name": (None, (3, 7)),
+    "ssl.SSLContext.post_handshake_auth": (None, (3, 8)),
+    "ssl.SSLContext.verify_flags": (None, (3, 4)),
+    "ssl.SSLError.library": ((2, 7), (3, 3)),
+    "ssl.SSLError.reason": ((2, 7), (3, 3)),
+    "ssl.SSLSocket.context": ((2, 7), (3, 2)),
+    "ssl.SSLSocket.server_hostname": (None, (3, 2)),
+    "ssl.SSLSocket.server_side": (None, (3, 2)),
+    "ssl.SSLSocket.session": (None, (3, 2)),
+    "ssl.SSLSocket.session_reused": (None, (3, 2)),
+    "ssl.VERIFY_CRL_CHECK_CHAIN": ((2, 7), (3, 4)),
+    "ssl.VERIFY_CRL_CHECK_LEAF": ((2, 7), (3, 4)),
+    "ssl.VERIFY_DEFAULT": ((2, 7), (3, 4)),
+    "ssl.VERIFY_X509_STRICT": ((2, 7), (3, 4)),
+    "ssl.VERIFY_X509_TRUSTED_FIRST": ((2, 7), (3, 4)),
+    "subprocess.DEVNULL": (None, (3, 3)),
+    "subprocess.Popen.args": (None, (3, 3)),
+    "sys.api_version": ((2, 3), (3, 0)),
+    "sys.flags": ((2, 6), (3, 0)),
+    "sys.float_info": ((2, 6), (3, 0)),
+    "sys.float_repr_style": ((2, 7), (3, 0)),
+    "sys.long_info": ((2, 7), None),
+    "sys.py3kwarning": ((2, 6), None),
+    "sys.subversion": ((2, 5), None),
+    "sys.version_info": ((2, 0), (3, 0)),
+    "tarfile.TarFile.pax_headers": ((2, 6), (3, 0)),
+    "tarfile.TarInfo.pax_headers": ((2, 6), (3, 0)),
+    "threading.TIMEOUT_MAX": (None, (3, 2)),
+    "time.CLOCK_BOOTTIME": (None, (3, 7)),
+    "time.CLOCK_HIGHRES": (None, (3, 3)),
+    "time.CLOCK_MONOTONIC": (None, (3, 3)),
+    "time.CLOCK_MONOTONIC_RAW": (None, (3, 3)),
+    "time.CLOCK_PROCESS_CPUTIME_ID": (None, (3, 3)),
+    "time.CLOCK_PROF": (None, (3, 7)),
+    "time.CLOCK_REALTIME": (None, (3, 3)),
+    "time.CLOCK_THREAD_CPUTIME_ID": (None, (3, 3)),
+    "time.CLOCK_UPTIME": (None, (3, 7)),
+    "time.CLOCK_UPTIME_RAW": (None, (3, 8)),
+    "typing.TYPE_CHECKING": (None if not bp("typing") else (2, 7), (3, 5)),
+    "unittest.TestResult.buffer": ((2, 7), (3, 0)),
+    "unittest.TestResult.failfast": ((2, 7), (3, 0)),
+    "unittest.TestResult.skipped": ((2, 7), (3, 0)),
+    "xmlrpc.server.SimpleXMLRPCRequestHandler.rpc_paths": (None, (3, 0)),
+    "zipfile.ZIP_BZIP2": (None, (3, 3)),
+    "zipfile.ZIP_LZMA": (None, (3, 3)),
 
-  # Decorators
-  "contextlib.asynccontextmanager": (None, (3, 7)),
-  "typing.final": (None, (3, 8)),
-  "typing.runtime_checkable": (None, (3, 8)),
-}
+    # Decorators
+    "contextlib.asynccontextmanager": (None, (3, 7)),
+    "typing.final": (None, (3, 8)),
+    "typing.runtime_checkable": (None, (3, 8)),
+   }
 
 # Keyword arguments requirements: (function, keyword argument) -> (requirements)
 KWARGS_REQS = {
diff --git a/vermin/source_visitor.py b/vermin/source_visitor.py
index 13876f5..6af9cad 100644
--- a/vermin/source_visitor.py
+++ b/vermin/source_visitor.py
@@ -79,6 +79,9 @@ class SourceVisitor(ast.NodeVisitor):
     # Lines that should be ignored if they have the comment "novermin" or "novm".
     self.__no_lines = set()
 
+    self.__mod_rules = MOD_REQS()
+    self.__mod_mem_reqs_rules = MOD_MEM_REQS()
+
   def modules(self):
     return self.__modules
 
@@ -285,15 +288,15 @@ class SourceVisitor(ast.NodeVisitor):
 
     mods = self.modules()
     for mod in mods:
-      if mod in MOD_REQS:
-        vers = MOD_REQS[mod]
+      if mod in self.__mod_rules:
+        vers = self.__mod_rules[mod]
         self.__vvprint("'{}' requires {}".format(mod, version_strings(vers)), mod)
         mins = combine_versions(mins, vers)
 
     mems = self.members()
     for mem in mems:
-      if mem in MOD_MEM_REQS:
-        vers = MOD_MEM_REQS[mem]
+      if mem in self.__mod_mem_reqs_rules:
+        vers = self.__mod_mem_reqs_rules[mem]
         self.__vvprint("'{}' requires {}".format(mem, version_strings(vers)), mem)
         mins = combine_versions(mins, vers)
 
@@ -382,7 +385,7 @@ class SourceVisitor(ast.NodeVisitor):
       self.__vvprint("Excluding member: {}".format(member))
       return
 
-    if member in MOD_MEM_REQS:
+    if member in self.__mod_mem_reqs_rules:
       self.__members.append(member)
       self.__add_line_col(member, line, col)
 


Test patch:
diff --git a/runtests.py b/runtests.py
index 397951f..22247fb 100755
--- a/runtests.py
+++ b/runtests.py
@@ -31,3 +31,4 @@ if __name__ == '__main__':
   runsuite("codecs_encodings")
   runsuite("exclusions")
   runsuite("comment_exclusions")
+  runsuite("backports")
diff --git a/tests/arguments.py b/tests/arguments.py
index 88a21ca..d67de8a 100644
--- a/tests/arguments.py
+++ b/tests/arguments.py
@@ -2,7 +2,7 @@ import os
 from multiprocessing import cpu_count
 from tempfile import NamedTemporaryFile
 
-from vermin import Arguments, Config
+from vermin import Arguments, Config, Backports
 
 from .testutils import VerminTest
 
@@ -32,7 +32,8 @@ class VerminArgumentsTests(VerminTest):
                             parse_args(["file.py", "file2.py", "folder/folder2"]))
 
   def test_mix_options_and_files(self):
-    self.assertContainsDict({"code": 0, "paths": ["file.py"], "targets": [(True, (2, 7))]},
+    self.assertContainsDict({"code": 0, "paths": ["file.py"], "targets": [(True, (2, 7))],
+                             "no-tips": False},
                             parse_args(["-q", "-t=2.7", "file.py"]))
 
   def test_quiet(self):
@@ -144,3 +145,21 @@ class VerminArgumentsTests(VerminTest):
     self.assertContainsDict({"code": 0}, parse_args(["--exclude-file", fp.name]))
     os.remove(fp.name)
     self.assertEqual(["aaa", "bbb"], self.config.exclusions())  # Expect it sorted.
+
+  def test_backport(self):
+    # Needs <name> part.
+    self.assertContainsDict({"code": 1}, parse_args(["--backport"]))
+    self.assertEmpty(self.config.backports())
+
+    # Unknown module.
+    self.assertContainsDict({"code": 1}, parse_args(["--backport", "foobarbaz"]))
+    self.assertEmpty(self.config.backports())
+
+    # Known modules.
+    for mod in Backports.modules():
+      self.config.reset()
+      self.assertContainsDict({"code": 0}, parse_args(["--backport", mod]))
+      self.assertEqualItems([mod], self.config.backports())
+
+  def test_no_tips(self):
+    self.assertContainsDict({"no-tips": True}, parse_args(["--no-tips"]))
diff --git a/tests/backports.py b/tests/backports.py
new file mode 100644
index 0000000..37fe6af
--- /dev/null
+++ b/tests/backports.py
@@ -0,0 +1,24 @@
+from vermin import Backports
+
+from .testutils import VerminTest
+
+class VerminBackportsTests(VerminTest):
+  def test_modules(self):
+    self.assertEqualItems((
+      "argparse",
+      "configparser",
+      "enum",
+      "faulthandler",
+      "typing",
+    ), Backports.modules())
+
+  def test_is_backport(self):
+    for mod in Backports.modules():
+      self.assertTrue(Backports.is_backport(mod))
+
+  def test_str(self):
+    self.assertEqual("""   argparse     - https://pypi.org/project/argparse/
+   configparser - https://pypi.org/project/configparser/
+   enum         - https://pypi.org/project/enum34/
+   faulthandler - https://pypi.org/project/faulthandler/
+   typing       - https://pypi.org/project/typing/""", Backports.str(3))
diff --git a/tests/class.py b/tests/class.py
index 807b926..2ec7aab 100644
--- a/tests/class.py
+++ b/tests/class.py
@@ -1,6 +1,18 @@
+from vermin import Config
+
 from .testutils import VerminTest, detect
 
 class VerminClassMemberTests(VerminTest):
+  def __init__(self, methodName):
+    super(VerminClassMemberTests, self).__init__(methodName)
+    self.config = Config.get()
+
+  def setUp(self):
+    self.config.reset()
+
+  def tearDown(self):
+    self.config.reset()
+
   def test_ABC_of_abc(self):
     self.assertOnlyIn((3, 4), detect("from abc import ABC"))
 
@@ -24,15 +36,21 @@ class VerminClassMemberTests(VerminTest):
 
   def test_Deque_of_typing(self):
     self.assertOnlyIn((3, 6), detect("from typing import Deque"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 6)), detect("from typing import Deque"))
 
   def test_ContextManager_of_typing(self):
     self.assertOnlyIn((3, 6), detect("from typing import ContextManager"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 6)), detect("from typing import ContextManager"))
 
   def test_AsyncContextManager_of_typing(self):
     self.assertOnlyIn((3, 6), detect("from typing import AsyncContextManager"))
 
   def test_Counter_of_typing(self):
     self.assertOnlyIn((3, 6), detect("from typing import Counter"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 6)), detect("from typing import Counter"))
 
   def test_ChainMap_of_typing(self):
     self.assertOnlyIn((3, 6), detect("from typing import ChainMap"))
@@ -42,15 +60,21 @@ class VerminClassMemberTests(VerminTest):
 
   def test_Text_of_typing(self):
     self.assertOnlyIn((3, 6), detect("from typing import Text"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 6)), detect("from typing import Text"))
 
   def test_ClassVar_of_typing(self):
     self.assertOnlyIn((3, 5), detect("from typing import ClassVar"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 5)), detect("from typing import ClassVar"))
 
   def test_Protocol_of_typing(self):
     self.assertOnlyIn((3, 8), detect("from typing import Protocol"))
 
   def test_Type_of_typing(self):
     self.assertOnlyIn((3, 5), detect("from typing import Type"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 5)), detect("from typing import Type"))
 
   def test_SupportsIndex_of_typing(self):
     self.assertOnlyIn((3, 8), detect("from typing import SupportsIndex"))
diff --git a/tests/constants.py b/tests/constants.py
index 823fa73..0ed82c5 100644
--- a/tests/constants.py
+++ b/tests/constants.py
@@ -1,6 +1,18 @@
+from vermin import Config
+
 from .testutils import VerminTest, detect
 
 class VerminConstantMemberTests(VerminTest):
+  def __init__(self, methodName):
+    super(VerminConstantMemberTests, self).__init__(methodName)
+    self.config = Config.get()
+
+  def setUp(self):
+    self.config.reset()
+
+  def tearDown(self):
+    self.config.reset()
+
   def test_flags_of_sys(self):
     self.assertOnlyIn(((2, 6), (3, 0)), detect("from sys import flags"))
 
@@ -115,6 +127,8 @@ class VerminConstantMemberTests(VerminTest):
 
   def test_TYPE_CHECKING_of_typing(self):
     self.assertOnlyIn((3, 5), detect("from typing import TYPE_CHECKING"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 5)), detect("from typing import TYPE_CHECKING"))
 
   def test_algorithms_of_hashlib(self):
     self.assertOnlyIn((2, 7), detect("from hashlib import algorithms"))
diff --git a/tests/function.py b/tests/function.py
index 6d26fde..d5a9bf4 100644
--- a/tests/function.py
+++ b/tests/function.py
@@ -1,6 +1,18 @@
+from vermin import Config
+
 from .testutils import VerminTest, detect
 
 class VerminFunctionMemberTests(VerminTest):
+  def __init__(self, methodName):
+    super(VerminFunctionMemberTests, self).__init__(methodName)
+    self.config = Config.get()
+
+  def setUp(self):
+    self.config.reset()
+
+  def tearDown(self):
+    self.config.reset()
+
   def test_exc_clear_of_sys(self):
     self.assertOnlyIn((2, 3), detect("from sys import exc_clear"))
 
@@ -308,6 +320,8 @@ class VerminFunctionMemberTests(VerminTest):
 
   def test_NewType_of_typing(self):
     self.assertOnlyIn((3, 5), detect("import typing\ntyping.NewType()"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 5)), detect("import typing\ntyping.NewType()"))
 
   def test_pbkdf2_hmac_of_hashlib(self):
     self.assertOnlyIn(((2, 7), (3, 4)), detect("import hashlib\nhashlib.pbkdf2_hmac()"))
diff --git a/tests/general.py b/tests/general.py
index 958bd43..aec19b9 100644
--- a/tests/general.py
+++ b/tests/general.py
@@ -89,7 +89,7 @@ class VerminGeneralTests(VerminTest):
 
   def test_detect_paths(self):
     paths = detect_paths([abspath("vermin")])
-    self.assertEqual(12, len(paths))
+    self.assertEqual(13, len(paths))
 
   def test_detect_hidden_paths(self):
     tmp_fld = mkdtemp()
@@ -126,8 +126,9 @@ class VerminGeneralTests(VerminTest):
   def test_detect_vermin_min_versions(self):
     paths = detect_paths([abspath("vermin")])
     processor = Processor()
-    (mins, incomp, unique_versions) = processor.process(paths)
+    (mins, incomp, unique_versions, backports) = processor.process(paths)
     self.assertOnlyIn(((2, 7), (3, 0)), mins)
+    self.assertEmpty(backports)
 
   def test_combine_versions(self):
     with self.assertRaises(AssertionError):
@@ -277,19 +278,21 @@ class VerminGeneralTests(VerminTest):
       process_individual(("nonexistent", self.config))
 
   def test_process_runtests_py(self):
-    (path, mins, text) = process_individual((sys.argv[0], self.config))
+    (path, mins, text, bps) = process_individual((sys.argv[0], self.config))
     self.assertEqual(basename(path), "runtests.py")
     self.assertEqual(mins, [(2, 7), (3, 0)])
     self.assertEmpty(text)
+    self.assertEmpty(bps)
 
   def test_process_syntax_error(self):
     # Syntax error triggers minimum versions [0, 0].
     fp = NamedTemporaryFile(suffix=".py", delete=False)
     fp.write(b"(")  # SyntaxError: unexpected EOF while parsing
     fp.close()
-    (path, mins, text) = process_individual((fp.name, self.config))
+    (path, mins, text, bps) = process_individual((fp.name, self.config))
     self.assertEqual(mins, [(0, 0), (0, 0)])
     self.assertEmpty(text)
+    self.assertEmpty(bps)
     os.remove(fp.name)
 
   def test_process_invalid_versions(self):
@@ -297,7 +300,17 @@ class VerminGeneralTests(VerminTest):
     fp.write(b"long(42)\n")  # long is a v2 feature: 2.0 !3
     fp.write(b"breakpoint()\n")  # breakpoint(): !2, 3.7
     fp.close()
-    (path, mins, text) = process_individual((fp.name, self.config))
+    (path, mins, text, bps) = process_individual((fp.name, self.config))
     self.assertEqual(mins, None)
     self.assertTrue(text.startswith("Versions could not be combined"))
+    self.assertEmpty(bps)
+    os.remove(fp.name)
+
+  def test_process_file_using_backport(self):
+    fp = NamedTemporaryFile(suffix=".py", delete=False)
+    fp.write(b"import typing\n")
+    fp.close()
+    (path, mins, text, bps) = process_individual((fp.name, self.config))
+    self.assertEmpty(text)
+    self.assertEqualItems(["typing"], bps)
     os.remove(fp.name)
diff --git a/tests/module.py b/tests/module.py
index 78c4889..6b9e582 100644
--- a/tests/module.py
+++ b/tests/module.py
@@ -1,9 +1,23 @@
+from vermin import Config
+
 from .testutils import VerminTest, detect
 
 class VerminModuleTests(VerminTest):
+  def __init__(self, methodName):
+    super(VerminModuleTests, self).__init__(methodName)
+    self.config = Config.get()
+
+  def setUp(self):
+    self.config.reset()
+
+  def tearDown(self):
+    self.config.reset()
+
   def test_argparse(self):
     self.assertOnlyIn(((2, 7), (3, 2)), detect("import argparse"))
     self.assertOnlyIn(((2, 7), (3, 2)), detect("from argparse import *"))
+    self.assertTrue(self.config.add_backport("argparse"))
+    self.assertOnlyIn(((2, 3), (3, 1)), detect("import argparse"))
 
   def test_abc(self):
     self.assertOnlyIn(((2, 6), (3, 0)), detect("import abc"))
@@ -25,6 +39,8 @@ class VerminModuleTests(VerminTest):
 
   def test_configparser(self):
     self.assertOnlyIn((3, 0), detect("import configparser"))
+    self.assertTrue(self.config.add_backport("configparser"))
+    self.assertOnlyIn(((2, 6), (3, 0)), detect("import configparser"))
 
   def test_copy_reg(self):
     self.assertOnlyIn((2, 0), detect("import copy_reg"))
@@ -142,6 +158,8 @@ class VerminModuleTests(VerminTest):
 
   def test_typing(self):
     self.assertOnlyIn((3, 5), detect("import typing"))
+    self.assertTrue(self.config.add_backport("typing"))
+    self.assertOnlyIn(((2, 7), (3, 4)), detect("import typing"))
 
   def test_tracemalloc(self):
     self.assertOnlyIn((3, 4), detect("import tracemalloc"))
@@ -151,6 +169,8 @@ class VerminModuleTests(VerminTest):
 
   def test_faulthandler(self):
     self.assertOnlyIn((3, 3), detect("import faulthandler"))
+    self.assertTrue(self.config.add_backport("faulthandler"))
+    self.assertOnlyIn(((2, 6), (3, 0)), detect("import faulthandler"))
 
   def test_ipaddress(self):
     self.assertOnlyIn((3, 3), detect("import ipaddress"))
@@ -388,6 +408,8 @@ class VerminModuleTests(VerminTest):
 
   def test_enum(self):
     self.assertOnlyIn((3, 4), detect("import enum"))
+    self.assertTrue(self.config.add_backport("enum"))
+    self.assertOnlyIn(((2, 4), (3, 3)), detect("import enum"))
 
   def test_selectors(self):
     self.assertOnlyIn((3, 4), detect("import selectors"))