diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index cf94f7d8164c2ed2bc64822d09cb615738441252..ac0ff69e22507b430cfb9a91d98e2451a3de52d9 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -4,8 +4,11 @@ on:
   merge_group:
   push:
     branches-ignore:
-    # disabled for jaraco/skeleton#103
-    # - gh-readonly-queue/**  # Temporary merge queue-related GH-made branches
+    # temporary GH branches relating to merge queues (jaraco/skeleton#93)
+    - gh-readonly-queue/**
+    tags:
+    # required if branches-ignore is supplied (jaraco/skeleton#103)
+    - '**'
   pull_request:
 
 permissions:
@@ -28,10 +31,10 @@ env:
 jobs:
   test:
     strategy:
+      # https://blog.jaraco.com/efficient-use-of-ci-resources/
       matrix:
         python:
         - "3.8"
-        - "3.11"
         - "3.12"
         platform:
         - ubuntu-latest
@@ -42,6 +45,8 @@ jobs:
           platform: ubuntu-latest
         - python: "3.10"
           platform: ubuntu-latest
+        - python: "3.11"
+          platform: ubuntu-latest
         - python: pypy3.10
           platform: ubuntu-latest
     runs-on: ${{ matrix.platform }}
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 68489063748342f1bc06704ed0d1135a8b935cca..dc8516ac20b3d38cf754fe8d804ffdffafc15ef0 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -3,10 +3,14 @@ python:
   install:
   - path: .
     extra_requirements:
-      - docs
+      - doc
 
 # required boilerplate readthedocs/readthedocs.org#10401
 build:
   os: ubuntu-lts-latest
   tools:
     python: latest
+  # post-checkout job to ensure the clone isn't shallow jaraco/skeleton#114
+  jobs:
+    post_checkout:
+    - git fetch --unshallow || true
diff --git a/NEWS.rst b/NEWS.rst
index 850e8f009c025a69222730a7fe955b92d1cd9d35..a49f5ec902b57ca7858981cdb0568822fae966b1 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,32 @@
+v8.0.0
+======
+
+Deprecations and Removals
+-------------------------
+
+- Message.__getitem__ now raises a KeyError on missing keys. (#371)
+- Removed deprecated support for Distribution subclasses not implementing abstract methods.
+
+
+v7.2.1
+======
+
+Bugfixes
+--------
+
+- When reading installed files from an egg, use ``relative_to(walk_up=True)`` to honor files installed outside of the installation root. (#455)
+
+
+v7.2.0
+======
+
+Features
+--------
+
+- Deferred select imports in for speedup (python/cpython#109829).
+- Updated fixtures for python/cpython#120801.
+
+
 v7.1.0
 ======
 
diff --git a/PKG-INFO b/PKG-INFO
index 2b304d91deb188049374b57c227d64002a401e32..b7a7ec7446e2c3217dd0d925b5c6297b00a19961 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,39 +1,39 @@
 Metadata-Version: 2.1
 Name: importlib_metadata
-Version: 7.1.0
+Version: 8.0.0
 Summary: Read metadata from Python packages
-Home-page: https://github.com/python/importlib_metadata
-Author: Jason R. Coombs
-Author-email: jaraco@jaraco.com
+Author-email: "Jason R. Coombs" <jaraco@jaraco.com>
+Project-URL: Source, https://github.com/python/importlib_metadata
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
 Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
 License-File: LICENSE
 Requires-Dist: zipp>=0.5
 Requires-Dist: typing-extensions>=3.6.4; python_version < "3.8"
-Provides-Extra: testing
-Requires-Dist: pytest>=6; extra == "testing"
-Requires-Dist: pytest-checkdocs>=2.4; extra == "testing"
-Requires-Dist: pytest-cov; extra == "testing"
-Requires-Dist: pytest-mypy; platform_python_implementation != "PyPy" and extra == "testing"
-Requires-Dist: pytest-enabler>=2.2; extra == "testing"
-Requires-Dist: pytest-ruff>=0.2.1; extra == "testing"
-Requires-Dist: importlib_resources>=1.3; python_version < "3.9" and extra == "testing"
-Requires-Dist: packaging; extra == "testing"
-Requires-Dist: pyfakefs; extra == "testing"
-Requires-Dist: flufl.flake8; extra == "testing"
-Requires-Dist: pytest-perf>=0.9.2; extra == "testing"
-Requires-Dist: jaraco.test>=5.4; extra == "testing"
-Provides-Extra: docs
-Requires-Dist: sphinx>=3.5; extra == "docs"
-Requires-Dist: jaraco.packaging>=9.3; extra == "docs"
-Requires-Dist: rst.linker>=1.9; extra == "docs"
-Requires-Dist: furo; extra == "docs"
-Requires-Dist: sphinx-lint; extra == "docs"
-Requires-Dist: jaraco.tidelift>=1.4; extra == "docs"
+Provides-Extra: test
+Requires-Dist: pytest!=8.1.*,>=6; extra == "test"
+Requires-Dist: pytest-checkdocs>=2.4; extra == "test"
+Requires-Dist: pytest-cov; extra == "test"
+Requires-Dist: pytest-mypy; extra == "test"
+Requires-Dist: pytest-enabler>=2.2; extra == "test"
+Requires-Dist: pytest-ruff>=0.2.1; extra == "test"
+Requires-Dist: importlib_resources>=1.3; python_version < "3.9" and extra == "test"
+Requires-Dist: packaging; extra == "test"
+Requires-Dist: pyfakefs; extra == "test"
+Requires-Dist: flufl.flake8; extra == "test"
+Requires-Dist: pytest-perf>=0.9.2; extra == "test"
+Requires-Dist: jaraco.test>=5.4; extra == "test"
+Provides-Extra: doc
+Requires-Dist: sphinx>=3.5; extra == "doc"
+Requires-Dist: jaraco.packaging>=9.3; extra == "doc"
+Requires-Dist: rst.linker>=1.9; extra == "doc"
+Requires-Dist: furo; extra == "doc"
+Requires-Dist: sphinx-lint; extra == "doc"
+Requires-Dist: jaraco.tidelift>=1.4; extra == "doc"
 Provides-Extra: perf
 Requires-Dist: ipython; extra == "perf"
 
diff --git a/docs/conf.py b/docs/conf.py
index 90a1da2ada756c9645dbafc034018e1fb2c5c784..2cd8fb0c0eb8fc652b107a2e6af61de761b963b2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -69,6 +69,4 @@ nitpick_ignore = [
     ('py:class', 'importlib_metadata._meta._T'),
     # Workaround for #435
     ('py:class', '_T'),
-    # Other workarounds
-    ('py:class', 'importlib_metadata.DeprecatedNonAbstract'),
 ]
diff --git a/importlib_metadata.egg-info/PKG-INFO b/importlib_metadata.egg-info/PKG-INFO
index 2b304d91deb188049374b57c227d64002a401e32..b7a7ec7446e2c3217dd0d925b5c6297b00a19961 100644
--- a/importlib_metadata.egg-info/PKG-INFO
+++ b/importlib_metadata.egg-info/PKG-INFO
@@ -1,39 +1,39 @@
 Metadata-Version: 2.1
 Name: importlib_metadata
-Version: 7.1.0
+Version: 8.0.0
 Summary: Read metadata from Python packages
-Home-page: https://github.com/python/importlib_metadata
-Author: Jason R. Coombs
-Author-email: jaraco@jaraco.com
+Author-email: "Jason R. Coombs" <jaraco@jaraco.com>
+Project-URL: Source, https://github.com/python/importlib_metadata
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
 Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
 License-File: LICENSE
 Requires-Dist: zipp>=0.5
 Requires-Dist: typing-extensions>=3.6.4; python_version < "3.8"
-Provides-Extra: testing
-Requires-Dist: pytest>=6; extra == "testing"
-Requires-Dist: pytest-checkdocs>=2.4; extra == "testing"
-Requires-Dist: pytest-cov; extra == "testing"
-Requires-Dist: pytest-mypy; platform_python_implementation != "PyPy" and extra == "testing"
-Requires-Dist: pytest-enabler>=2.2; extra == "testing"
-Requires-Dist: pytest-ruff>=0.2.1; extra == "testing"
-Requires-Dist: importlib_resources>=1.3; python_version < "3.9" and extra == "testing"
-Requires-Dist: packaging; extra == "testing"
-Requires-Dist: pyfakefs; extra == "testing"
-Requires-Dist: flufl.flake8; extra == "testing"
-Requires-Dist: pytest-perf>=0.9.2; extra == "testing"
-Requires-Dist: jaraco.test>=5.4; extra == "testing"
-Provides-Extra: docs
-Requires-Dist: sphinx>=3.5; extra == "docs"
-Requires-Dist: jaraco.packaging>=9.3; extra == "docs"
-Requires-Dist: rst.linker>=1.9; extra == "docs"
-Requires-Dist: furo; extra == "docs"
-Requires-Dist: sphinx-lint; extra == "docs"
-Requires-Dist: jaraco.tidelift>=1.4; extra == "docs"
+Provides-Extra: test
+Requires-Dist: pytest!=8.1.*,>=6; extra == "test"
+Requires-Dist: pytest-checkdocs>=2.4; extra == "test"
+Requires-Dist: pytest-cov; extra == "test"
+Requires-Dist: pytest-mypy; extra == "test"
+Requires-Dist: pytest-enabler>=2.2; extra == "test"
+Requires-Dist: pytest-ruff>=0.2.1; extra == "test"
+Requires-Dist: importlib_resources>=1.3; python_version < "3.9" and extra == "test"
+Requires-Dist: packaging; extra == "test"
+Requires-Dist: pyfakefs; extra == "test"
+Requires-Dist: flufl.flake8; extra == "test"
+Requires-Dist: pytest-perf>=0.9.2; extra == "test"
+Requires-Dist: jaraco.test>=5.4; extra == "test"
+Provides-Extra: doc
+Requires-Dist: sphinx>=3.5; extra == "doc"
+Requires-Dist: jaraco.packaging>=9.3; extra == "doc"
+Requires-Dist: rst.linker>=1.9; extra == "doc"
+Requires-Dist: furo; extra == "doc"
+Requires-Dist: sphinx-lint; extra == "doc"
+Requires-Dist: jaraco.tidelift>=1.4; extra == "doc"
 Provides-Extra: perf
 Requires-Dist: ipython; extra == "perf"
 
diff --git a/importlib_metadata.egg-info/SOURCES.txt b/importlib_metadata.egg-info/SOURCES.txt
index 7eebfbc9aaedb6f54d998f079845653d26025607..54844679c120c75d6ec8b55477dfd33d1efd04c7 100644
--- a/importlib_metadata.egg-info/SOURCES.txt
+++ b/importlib_metadata.egg-info/SOURCES.txt
@@ -13,7 +13,6 @@ mypy.ini
 pyproject.toml
 pytest.ini
 ruff.toml
-setup.cfg
 towncrier.toml
 tox.ini
 .github/FUNDING.yml
@@ -41,6 +40,7 @@ importlib_metadata.egg-info/dependency_links.txt
 importlib_metadata.egg-info/requires.txt
 importlib_metadata.egg-info/top_level.txt
 importlib_metadata/compat/__init__.py
+importlib_metadata/compat/py311.py
 importlib_metadata/compat/py39.py
 tests/__init__.py
 tests/_context.py
diff --git a/importlib_metadata.egg-info/requires.txt b/importlib_metadata.egg-info/requires.txt
index e9b4f31729ea29c3c986a14ab54427d596f5b486..cb1da9aea4f496990ba6548e4cebdc9fcef16c30 100644
--- a/importlib_metadata.egg-info/requires.txt
+++ b/importlib_metadata.egg-info/requires.txt
@@ -3,7 +3,7 @@ zipp>=0.5
 [:python_version < "3.8"]
 typing-extensions>=3.6.4
 
-[docs]
+[doc]
 sphinx>=3.5
 jaraco.packaging>=9.3
 rst.linker>=1.9
@@ -14,10 +14,11 @@ jaraco.tidelift>=1.4
 [perf]
 ipython
 
-[testing]
-pytest>=6
+[test]
+pytest!=8.1.*,>=6
 pytest-checkdocs>=2.4
 pytest-cov
+pytest-mypy
 pytest-enabler>=2.2
 pytest-ruff>=0.2.1
 packaging
@@ -26,8 +27,5 @@ flufl.flake8
 pytest-perf>=0.9.2
 jaraco.test>=5.4
 
-[testing:platform_python_implementation != "PyPy"]
-pytest-mypy
-
-[testing:python_version < "3.9"]
+[test:python_version < "3.9"]
 importlib_resources>=1.3
diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py
index 32ee3b4d42d75c4668e4fbc1175ad307fa27f4fc..ed4813551ac238bfb9b5a48f4476463355415d27 100644
--- a/importlib_metadata/__init__.py
+++ b/importlib_metadata/__init__.py
@@ -12,14 +12,13 @@ import inspect
 import pathlib
 import operator
 import textwrap
-import warnings
 import functools
 import itertools
 import posixpath
 import collections
 
-from . import _adapters, _meta
-from .compat import py39
+from . import _meta
+from .compat import py39, py311
 from ._collections import FreezableDefaultDict, Pair
 from ._compat import (
     NullFinder,
@@ -334,27 +333,7 @@ class FileHash:
         return f'<FileHash mode: {self.mode} value: {self.value}>'
 
 
-class DeprecatedNonAbstract:
-    # Required until Python 3.14
-    def __new__(cls, *args, **kwargs):
-        all_names = {
-            name for subclass in inspect.getmro(cls) for name in vars(subclass)
-        }
-        abstract = {
-            name
-            for name in all_names
-            if getattr(getattr(cls, name), '__isabstractmethod__', False)
-        }
-        if abstract:
-            warnings.warn(
-                f"Unimplemented abstract methods {abstract}",
-                DeprecationWarning,
-                stacklevel=2,
-            )
-        return super().__new__(cls)
-
-
-class Distribution(DeprecatedNonAbstract):
+class Distribution(metaclass=abc.ABCMeta):
     """
     An abstract Python distribution package.
 
@@ -461,6 +440,9 @@ class Distribution(DeprecatedNonAbstract):
         Custom providers may provide the METADATA file or override this
         property.
         """
+        # deferred for performance (python/cpython#109829)
+        from . import _adapters
+
         opt_text = (
             self.read_text('METADATA')
             or self.read_text('PKG-INFO')
@@ -567,9 +549,8 @@ class Distribution(DeprecatedNonAbstract):
             return
 
         paths = (
-            (subdir / name)
-            .resolve()
-            .relative_to(self.locate_file('').resolve())
+            py311.relative_fix((subdir / name).resolve())
+            .relative_to(self.locate_file('').resolve(), walk_up=True)
             .as_posix()
             for name in text.splitlines()
         )
diff --git a/importlib_metadata/_adapters.py b/importlib_metadata/_adapters.py
index 120e43a048873215f7a8f119c273ce1fd67a9a1c..6223263ed53f22fc25c09de06789718d2cd3b6ea 100644
--- a/importlib_metadata/_adapters.py
+++ b/importlib_metadata/_adapters.py
@@ -1,20 +1,8 @@
-import functools
-import warnings
 import re
 import textwrap
 import email.message
 
 from ._text import FoldedCase
-from ._compat import pypy_partial
-
-
-# Do not remove prior to 2024-01-01 or Python 3.14
-_warn = functools.partial(
-    warnings.warn,
-    "Implicit None on return values is deprecated and will raise KeyErrors.",
-    DeprecationWarning,
-    stacklevel=pypy_partial(2),
-)
 
 
 class Message(email.message.Message):
@@ -53,12 +41,17 @@ class Message(email.message.Message):
 
     def __getitem__(self, item):
         """
-        Warn users that a ``KeyError`` can be expected when a
-        missing key is supplied. Ref python/importlib_metadata#371.
+        Override parent behavior to typical dict behavior.
+
+        ``email.message.Message`` will emit None values for missing
+        keys. Typical mappings, including this ``Message``, will raise
+        a key error for missing keys.
+
+        Ref python/importlib_metadata#371.
         """
         res = super().__getitem__(item)
         if res is None:
-            _warn()
+            raise KeyError(item)
         return res
 
     def _repair_headers(self):
diff --git a/importlib_metadata/compat/py311.py b/importlib_metadata/compat/py311.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a5327436f9b1d9eae371e321c491a270634b3cf
--- /dev/null
+++ b/importlib_metadata/compat/py311.py
@@ -0,0 +1,22 @@
+import os
+import pathlib
+import sys
+import types
+
+
+def wrap(path):  # pragma: no cover
+    """
+    Workaround for https://github.com/python/cpython/issues/84538
+    to add backward compatibility for walk_up=True.
+    An example affected package is dask-labextension, which uses
+    jupyter-packaging to install JupyterLab javascript files outside
+    of site-packages.
+    """
+
+    def relative_to(root, *, walk_up=False):
+        return pathlib.Path(os.path.relpath(path, root))
+
+    return types.SimpleNamespace(relative_to=relative_to)
+
+
+relative_fix = wrap if sys.version_info < (3, 12) else lambda x: x
diff --git a/pyproject.toml b/pyproject.toml
index a853c5789e0cf4cfd8cf680110f4c3fcd39f04be..8cf8aeb9adfc881f39b431a11daaf6f2ab9e52d8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,62 @@
 [build-system]
-requires = ["setuptools>=56", "setuptools_scm[toml]>=3.4.1"]
+requires = ["setuptools>=61.2", "setuptools_scm[toml]>=3.4.1"]
 build-backend = "setuptools.build_meta"
 
+[project]
+name = "importlib_metadata"
+authors = [
+	{ name = "Jason R. Coombs", email = "jaraco@jaraco.com" },
+]
+description = "Read metadata from Python packages"
+readme = "README.rst"
+classifiers = [
+	"Development Status :: 5 - Production/Stable",
+	"Intended Audience :: Developers",
+	"License :: OSI Approved :: Apache Software License",
+	"Programming Language :: Python :: 3",
+	"Programming Language :: Python :: 3 :: Only",
+]
+requires-python = ">=3.8"
+dependencies = [
+	"zipp>=0.5",
+	'typing-extensions>=3.6.4; python_version < "3.8"',
+]
+dynamic = ["version"]
+
+[project.urls]
+Source = "https://github.com/python/importlib_metadata"
+
+[project.optional-dependencies]
+test = [
+	# upstream
+	"pytest >= 6, != 8.1.*",
+	"pytest-checkdocs >= 2.4",
+	"pytest-cov",
+	"pytest-mypy",
+	"pytest-enabler >= 2.2",
+	"pytest-ruff >= 0.2.1",
+
+	# local
+	'importlib_resources>=1.3; python_version < "3.9"',
+	"packaging",
+	"pyfakefs",
+	"flufl.flake8",
+	"pytest-perf >= 0.9.2",
+	"jaraco.test >= 5.4",
+]
+doc = [
+	# upstream
+	"sphinx >= 3.5",
+	"jaraco.packaging >= 9.3",
+	"rst.linker >= 1.9",
+	"furo",
+	"sphinx-lint",
+
+	# tidelift
+	"jaraco.tidelift >= 1.4",
+
+	# local
+]
+perf = ["ipython"]
+
 [tool.setuptools_scm]
diff --git a/pytest.ini b/pytest.ini
index 022a723e7e3e64762302b163b98bb37ca4132eca..9a0f3bce1395ae3819030b822e5d9076c85068ea 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,6 +1,9 @@
 [pytest]
 norecursedirs=dist build .tox .eggs
-addopts=--doctest-modules
+addopts=
+	--doctest-modules
+	--import-mode importlib
+consider_namespace_packages=true
 filterwarnings=
 	## upstream
 
diff --git a/setup.cfg b/setup.cfg
index 86cbe8cf2561160cffc5b45febf2e2960002abad..8bfd5a12f85b8fbb6c058cf67dd23da690835ea0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,53 +1,3 @@
-[metadata]
-name = importlib_metadata
-author = Jason R. Coombs
-author_email = jaraco@jaraco.com
-description = Read metadata from Python packages
-long_description = file:README.rst
-url = https://github.com/python/importlib_metadata
-classifiers = 
-	Development Status :: 5 - Production/Stable
-	Intended Audience :: Developers
-	License :: OSI Approved :: Apache Software License
-	Programming Language :: Python :: 3
-	Programming Language :: Python :: 3 :: Only
-
-[options]
-include_package_data = true
-python_requires = >=3.8
-install_requires = 
-	zipp>=0.5
-	typing-extensions>=3.6.4; python_version < "3.8"
-
-[options.extras_require]
-testing = 
-	pytest >= 6
-	pytest-checkdocs >= 2.4
-	pytest-cov
-	pytest-mypy; \
-	python_implementation != "PyPy"
-	pytest-enabler >= 2.2
-	pytest-ruff >= 0.2.1
-	
-	importlib_resources>=1.3; python_version < "3.9"
-	packaging
-	pyfakefs
-	flufl.flake8
-	pytest-perf >= 0.9.2
-	jaraco.test >= 5.4
-docs = 
-	sphinx >= 3.5
-	jaraco.packaging >= 9.3
-	rst.linker >= 1.9
-	furo
-	sphinx-lint
-	
-	jaraco.tidelift >= 1.4
-perf = 
-	ipython
-
-[options.entry_points]
-
 [egg_info]
 tag_build = 
 tag_date = 0
diff --git a/tests/compat/py39.py b/tests/compat/py39.py
index 16c8b5745bc7e8bbaef6d1e800078aa458bb671a..9476eb355f4c64b2b0a6bd1e696151fcdc053fd1 100644
--- a/tests/compat/py39.py
+++ b/tests/compat/py39.py
@@ -2,7 +2,7 @@ from jaraco.test.cpython import from_test_support, try_import
 
 
 os_helper = try_import('os_helper') or from_test_support(
-    'FS_NONASCII', 'skip_unless_symlink'
+    'FS_NONASCII', 'skip_unless_symlink', 'temp_dir'
 )
 import_helper = try_import('import_helper') or from_test_support(
     'modules_setup', 'modules_cleanup'
diff --git a/tests/fixtures.py b/tests/fixtures.py
index f8082df08352fcb7d1830bb92cb3c63f9859db2c..187f170562414d86f526e20b5daf7ea6e7869ab6 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -1,10 +1,8 @@
-import os
 import sys
 import copy
 import json
 import shutil
 import pathlib
-import tempfile
 import textwrap
 import functools
 import contextlib
@@ -26,29 +24,12 @@ except (ImportError, AttributeError):
 
 
 @contextlib.contextmanager
-def tempdir():
-    tmpdir = tempfile.mkdtemp()
-    try:
-        yield pathlib.Path(tmpdir)
-    finally:
-        shutil.rmtree(tmpdir)
-
-
-@contextlib.contextmanager
-def save_cwd():
-    orig = os.getcwd()
-    try:
-        yield
-    finally:
-        os.chdir(orig)
-
-
-@contextlib.contextmanager
-def tempdir_as_cwd():
-    with tempdir() as tmp:
-        with save_cwd():
-            os.chdir(str(tmp))
-            yield tmp
+def tmp_path():
+    """
+    Like os_helper.temp_dir, but yields a pathlib.Path.
+    """
+    with os_helper.temp_dir() as path:
+        yield pathlib.Path(path)
 
 
 @contextlib.contextmanager
@@ -69,7 +50,7 @@ class Fixtures:
 class SiteDir(Fixtures):
     def setUp(self):
         super().setUp()
-        self.site_dir = self.fixtures.enter_context(tempdir())
+        self.site_dir = self.fixtures.enter_context(tmp_path())
 
 
 class OnSysPath(Fixtures):
@@ -252,6 +233,40 @@ class EggInfoPkgPipInstalledNoToplevel(OnSysPath, SiteBuilder):
     }
 
 
+class EggInfoPkgPipInstalledExternalDataFiles(OnSysPath, SiteBuilder):
+    files: FilesSpec = {
+        "egg_with_module_pkg.egg-info": {
+            "PKG-INFO": "Name: egg_with_module-pkg",
+            # SOURCES.txt is made from the source archive, and contains files
+            # (setup.py) that are not present after installation.
+            "SOURCES.txt": """
+                egg_with_module.py
+                setup.py
+                egg_with_module.json
+                egg_with_module_pkg.egg-info/PKG-INFO
+                egg_with_module_pkg.egg-info/SOURCES.txt
+                egg_with_module_pkg.egg-info/top_level.txt
+            """,
+            # installed-files.txt is written by pip, and is a strictly more
+            # accurate source than SOURCES.txt as to the installed contents of
+            # the package.
+            "installed-files.txt": """
+                ../../../etc/jupyter/jupyter_notebook_config.d/relative.json
+                /etc/jupyter/jupyter_notebook_config.d/absolute.json
+                ../egg_with_module.py
+                PKG-INFO
+                SOURCES.txt
+                top_level.txt
+            """,
+            # missing top_level.txt (to trigger fallback to installed-files.txt)
+        },
+        "egg_with_module.py": """
+            def main():
+                print("hello world")
+            """,
+    }
+
+
 class EggInfoPkgPipInstalledNoModules(OnSysPath, SiteBuilder):
     files: FilesSpec = {
         "egg_with_no_modules_pkg.egg-info": {
diff --git a/tests/test_api.py b/tests/test_api.py
index a85c62adda3d6832a1353db61c71e9aac564bcf2..7ce0cd64f8608f962dff2c85a4397fa0d1444b99 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,9 +1,7 @@
 import re
 import textwrap
 import unittest
-import warnings
 import importlib
-import contextlib
 
 from . import fixtures
 from importlib_metadata import (
@@ -18,17 +16,11 @@ from importlib_metadata import (
 )
 
 
-@contextlib.contextmanager
-def suppress_known_deprecation():
-    with warnings.catch_warnings(record=True) as ctx:
-        warnings.simplefilter('default', category=DeprecationWarning)
-        yield ctx
-
-
 class APITests(
     fixtures.EggInfoPkg,
     fixtures.EggInfoPkgPipInstalledNoToplevel,
     fixtures.EggInfoPkgPipInstalledNoModules,
+    fixtures.EggInfoPkgPipInstalledExternalDataFiles,
     fixtures.EggInfoPkgSourcesFallback,
     fixtures.DistInfoPkg,
     fixtures.DistInfoPkgWithDot,
@@ -109,7 +101,7 @@ class APITests(
         Entry points should only be exposed for the first package
         on sys.path with a given name (even when normalized).
         """
-        alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
+        alt_site_dir = self.fixtures.enter_context(fixtures.tmp_path())
         self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
         alt_pkg = {
             "DistInfo_pkg-1.1.0.dist-info": {
@@ -156,13 +148,13 @@ class APITests(
         resolved = version('importlib-metadata')
         assert re.match(self.version_pattern, resolved)
 
-    def test_missing_key_legacy(self):
+    def test_missing_key(self):
         """
-        Requesting a missing key will still return None, but warn.
+        Requesting a missing key raises KeyError.
         """
         md = metadata('distinfo-pkg')
-        with suppress_known_deprecation():
-            assert md['does-not-exist'] is None
+        with self.assertRaises(KeyError):
+            md['does-not-exist']
 
     def test_get_key(self):
         """
diff --git a/tests/test_main.py b/tests/test_main.py
index af79e6984b1c8abb4226083e63cb04cb12f3370c..f1c12855dfe3b740228c2033364d908e0f5e8454 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,16 +1,13 @@
 import re
 import pickle
 import unittest
-import warnings
 import importlib
 import importlib_metadata
-import contextlib
 from .compat.py39 import os_helper
 
 import pyfakefs.fake_filesystem_unittest as ffs
 
 from . import fixtures
-from ._context import suppress
 from ._path import Symlink
 from importlib_metadata import (
     Distribution,
@@ -25,13 +22,6 @@ from importlib_metadata import (
 )
 
 
-@contextlib.contextmanager
-def suppress_known_deprecation():
-    with warnings.catch_warnings(record=True) as ctx:
-        warnings.simplefilter('default', category=DeprecationWarning)
-        yield ctx
-
-
 class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
     version_pattern = r'\d+\.\d+(\.\d)?'
 
@@ -56,9 +46,6 @@ class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
 
         assert "metadata" in str(ctx.exception)
 
-    # expected to fail until ABC is enforced
-    @suppress(AssertionError)
-    @suppress_known_deprecation()
     def test_abc_enforced(self):
         with self.assertRaises(TypeError):
             type('DistributionSubclass', (Distribution,), {})()
@@ -135,7 +122,7 @@ class NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.Test
         fixtures.build_files(self.make_pkg('abc'), self.site_dir)
         before = list(_unique(distributions()))
 
-        alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
+        alt_site_dir = self.fixtures.enter_context(fixtures.tmp_path())
         self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
         fixtures.build_files(self.make_pkg('ABC'), alt_site_dir)
         after = list(_unique(distributions()))
diff --git a/tox.ini b/tox.ini
index 3604f01f181265ba2469efe489dd9fb1b51efcf6..71fd05f64d4e81ef00c420112705fc3cd1eb0081 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,7 +9,7 @@ passenv =
 	HOME
 usedevelop = True
 extras =
-	testing
+	test
 
 [testenv:diffcov]
 description = run tests and check that diff from main is covered
@@ -24,8 +24,8 @@ commands =
 [testenv:docs]
 description = build the documentation
 extras =
-	docs
-	testing
+	doc
+	test
 changedir = docs
 commands =
 	python -m sphinx -W --keep-going . {toxinidir}/build/html