summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst5
-rw-r--r--coverage/misc.py27
-rw-r--r--coverage/tomlconfig.py7
-rw-r--r--tests/test_misc.py20
4 files changed, 52 insertions, 7 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 50a81aaf..937d2d0f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -22,7 +22,10 @@ This list is detailed and covers changes in each pre-release version.
Unreleased
----------
-Nothing yet.
+- Changed an internal detail of how tomli is imported, so that tomli can use
+ coverage.py for their own test suite (`issue 1228`_).
+
+.. _issue 1228: https://github.com/nedbat/coveragepy/issues/1228
.. _changes_60:
diff --git a/coverage/misc.py b/coverage/misc.py
index 11dad23e..cd4a7740 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
@@ -43,6 +44,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 203192c9..3301acc8 100644
--- a/coverage/tomlconfig.py
+++ b/coverage/tomlconfig.py
@@ -8,13 +8,10 @@ import os
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/tests/test_misc.py b/tests/test_misc.py
index 3858c4f8..077c2434 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