diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 5686493e2..c305477bc 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -37,7 +37,7 @@ jobs:
           - "3.7"
           - "3.8"
           - "3.9"
-          - "3.10.0-rc.2"
+          - "3.10"
           - "pypy3"
         exclude:
           # Windows PyPy doesn't seem to work?
diff --git a/.github/workflows/kit.yml b/.github/workflows/kit.yml
index 726cefaca..c6b9e43da 100644
--- a/.github/workflows/kit.yml
+++ b/.github/workflows/kit.yml
@@ -122,6 +122,7 @@ jobs:
 
   prerel:
     name: "Build ${{ matrix.python-version }} wheels on ${{ matrix.os }}"
+    if: ${{ false }}  # disable for now, since there are no pre-rel Python versions.
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml
index 49df01e6e..6b0de1b3d 100644
--- a/.github/workflows/testsuite.yml
+++ b/.github/workflows/testsuite.yml
@@ -35,7 +35,7 @@ jobs:
           - "3.7"
           - "3.8"
           - "3.9"
-          - "3.10.0-rc.2"
+          - "3.10"
           - "pypy3"
         exclude:
           # Windows PyPy doesn't seem to work?
diff --git a/CHANGES.rst b/CHANGES.rst
index d9fcc2b8c..9d810dbde 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -9,9 +9,7 @@ These changes are listed in decreasing version number order. Note this can be
 different from a strict chronological order when there are two branches in
 development at the same time, such as 4.5.x and 5.0.
 
-This list is detailed and covers changes in each pre-release version.  If you
-want to know what's different in 5.0 since 4.5.x, see :ref:`whatsnew5x`.
-
+This list is detailed and covers changes in each pre-release version.
 
     .. When updating the "Unreleased" header to a specific version, use this
     .. format.  Don't forget the jump target:
@@ -21,6 +19,29 @@ want to know what's different in 5.0 since 4.5.x, see :ref:`whatsnew5x`.
     ..  Version 9.8.1 --- 2027-07-27
     ..  ----------------------------
 
+.. _changes_601:
+
+Version 6.0.1 --- 2021-10-06
+----------------------------
+
+- In 6.0, the coverage.py exceptions moved from coverage.misc to
+  coverage.exceptions. These exceptions are not part of the public supported
+  API, CoverageException is. But a number of other third-party packages were
+  importing the exceptions from coverage.misc, so they are now available from
+  there again (`issue 1226`_).
+
+- Changed an internal detail of how tomli is imported, so that tomli can use
+  coverage.py for their own test suite (`issue 1228`_).
+
+- Defend against an obscure possibility under code obfuscation, where a
+  function can have an argument called "self", but no local named "self"
+  (`pull request 1210`_).  Thanks, Ben Carlsson.
+
+.. _pull request 1210: https://github.com/nedbat/coveragepy/pull/1210
+.. _issue 1226: https://github.com/nedbat/coveragepy/issues/1226
+.. _issue 1228: https://github.com/nedbat/coveragepy/issues/1228
+
+
 .. _changes_60:
 
 Version 6.0 --- 2021-10-03
@@ -462,6 +483,9 @@ Version 5.0 --- 2019-12-14
 
 Nothing new beyond 5.0b2.
 
+A summary of major changes in 5.0 since 4.5.x is in see :ref:`whatsnew5x`.
+
+
 
 .. _changes_50b2:
 
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 2642e6b1a..1c1fe0e9c 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -20,6 +20,7 @@ Arcadiy Ivanov
 Aron Griffis
 Artem Dayneko
 Arthur Deygin
+Ben Carlsson
 Ben Finney
 Bernát Gábor
 Bill Hart
diff --git a/coverage/context.py b/coverage/context.py
index 45e86a5c1..43d2b1cc7 100644
--- a/coverage/context.py
+++ b/coverage/context.py
@@ -48,7 +48,7 @@ def qualname_from_frame(frame):
     fname = co.co_name
     method = None
     if co.co_argcount and co.co_varnames[0] == "self":
-        self = frame.f_locals["self"]
+        self = frame.f_locals.get("self", None)
         method = getattr(self, fname, None)
 
     if method is None:
diff --git a/coverage/misc.py b/coverage/misc.py
index 11dad23e0..0f985be0e 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -5,6 +5,7 @@
 
 import errno
 import hashlib
+import importlib
 import importlib.util
 import inspect
 import locale
@@ -19,6 +20,11 @@
 from coverage import env
 from coverage.exceptions import CoverageException
 
+# In 6.0, the exceptions moved from misc.py to exceptions.py.  But a number of
+# other packages were importing the exceptions from misc, so import them here.
+# pylint: disable=unused-wildcard-import
+from coverage.exceptions import *   # pylint: disable=wildcard-import
+
 ISOLATED_MODULES = {}
 
 
@@ -43,6 +49,32 @@ def isolate_module(mod):
 os = isolate_module(os)
 
 
+def import_third_party(modname):
+    """Import a third-party module we need, but might not be installed.
+
+    This also cleans out the module after the import, so that coverage won't
+    appear to have imported it.  This lets the third party use coverage for
+    their own tests.
+
+    Arguments:
+        modname (str): the name of the module to import.
+
+    Returns:
+        The imported module, or None if the module couldn't be imported.
+
+    """
+    try:
+        mod = importlib.import_module(modname)
+    except ImportError:
+        mod = None
+
+    imported = [m for m in sys.modules if m.startswith(modname)]
+    for name in imported:
+        del sys.modules[name]
+
+    return mod
+
+
 def dummy_decorator_with_args(*args_unused, **kwargs_unused):
     """Dummy no-op implementation of a decorator with arguments."""
     def _decorator(func):
diff --git a/coverage/tomlconfig.py b/coverage/tomlconfig.py
index 203192c93..3301acc8e 100644
--- a/coverage/tomlconfig.py
+++ b/coverage/tomlconfig.py
@@ -8,13 +8,10 @@
 import re
 
 from coverage.exceptions import CoverageException
-from coverage.misc import substitute_variables
+from coverage.misc import import_third_party, substitute_variables
 
 # TOML support is an install-time extra option.
-try:
-    import tomli
-except ImportError:         # pragma: not covered
-    tomli = None
+tomli = import_third_party("tomli")
 
 
 class TomlDecodeError(Exception):
diff --git a/coverage/version.py b/coverage/version.py
index 98bbda8b5..c9b537e37 100644
--- a/coverage/version.py
+++ b/coverage/version.py
@@ -5,7 +5,7 @@
 # This file is exec'ed in setup.py, don't import anything!
 
 # Same semantics as sys.version_info.
-version_info = (6, 0, 0, "final", 0)
+version_info = (6, 0, 1, "final", 0)
 
 
 def _make_version(major, minor, micro, releaselevel, serial):
diff --git a/doc/conf.py b/doc/conf.py
index 5874b6376..5109ff726 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -67,9 +67,9 @@
 # The short X.Y version.
 version = "6.0"                                 # CHANGEME
 # The full version, including alpha/beta/rc tags.
-release = "6.0"                                 # CHANGEME
+release = "6.0.1"                               # CHANGEME
 # The date of release, in "monthname day, year" format.
-release_date = "October 3, 2021"                # CHANGEME
+release_date = "October 6, 2021"                # CHANGEME
 
 rst_epilog = """
 .. |release_date| replace:: {release_date}
diff --git a/doc/index.rst b/doc/index.rst
index 2b4a6a45c..dc34c3f79 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -23,7 +23,7 @@ supported on:
 .. ifconfig:: prerelease
 
     **This is a pre-release build.  The usual warnings about possible bugs
-    apply.** The latest stable version is coverage.py 6.0, `described here`_.
+    apply.** The latest stable version is coverage.py 6.0.1, `described here`_.
 
 .. _described here: http://coverage.readthedocs.io/
 
@@ -222,6 +222,5 @@ More information
     contributing
     trouble
     faq
-    whatsnew5x
     changes
     sleepy
diff --git a/doc/whatsnew5x.rst b/doc/whatsnew5x.rst
index bf0fe6cae..f49739ef0 100644
--- a/doc/whatsnew5x.rst
+++ b/doc/whatsnew5x.rst
@@ -1,6 +1,8 @@
 .. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
 .. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
 
+:orphan:
+
 .. _whatsnew5x:
 
 ====================
diff --git a/tests/test_context.py b/tests/test_context.py
index 3f80803bd..36eff2f0d 100644
--- a/tests/test_context.py
+++ b/tests/test_context.py
@@ -5,6 +5,7 @@
 
 import inspect
 import os.path
+from unittest import mock
 
 import coverage
 from coverage.context import qualname_from_frame
@@ -275,3 +276,10 @@ def test_bug_829(self):
         # A class with a name like a function shouldn't confuse qualname_from_frame.
         class test_something:               # pylint: disable=unused-variable
             assert get_qualname() is None
+
+    def test_bug_1210(self):
+        # Under pyarmor (an obfuscator), a function can have a "self" argument,
+        # but then not have a "self" local.
+        co = mock.Mock(co_name="a_co_name", co_argcount=1, co_varnames=["self"])
+        frame = mock.Mock(f_code=co, f_locals={})
+        assert qualname_from_frame(frame) == "unittest.mock.a_co_name"
diff --git a/tests/test_misc.py b/tests/test_misc.py
index 3858c4f8b..077c24344 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -3,11 +3,13 @@
 
 """Tests of miscellaneous stuff."""
 
+import sys
+
 import pytest
 
 from coverage.exceptions import CoverageException
 from coverage.misc import contract, dummy_decorator_with_args, file_be_gone
-from coverage.misc import Hasher, one_of, substitute_variables
+from coverage.misc import Hasher, one_of, substitute_variables, import_third_party
 from coverage.misc import USE_CONTRACTS
 
 from tests.coveragetest import CoverageTest
@@ -155,3 +157,19 @@ def test_substitute_variables_errors(text):
         substitute_variables(text, VARS)
     assert text in str(exc_info.value)
     assert "Variable NOTHING is undefined" in str(exc_info.value)
+
+
+class ImportThirdPartyTest(CoverageTest):
+    """Test import_third_party."""
+
+    run_in_temp_dir = False
+
+    def test_success(self):
+        mod = import_third_party("pytest")
+        assert mod.__name__ == "pytest"
+        assert "pytest" not in sys.modules
+
+    def test_failure(self):
+        mod = import_third_party("xyzzy")
+        assert mod is None
+        assert "xyzzy" not in sys.modules