summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/backward.py7
-rw-r--r--coverage/config.py5
-rw-r--r--coverage/optional.py68
-rw-r--r--coverage/tomlconfig.py3
-rw-r--r--tests/test_testing.py12
5 files changed, 86 insertions, 9 deletions
diff --git a/coverage/backward.py b/coverage/backward.py
index e051fa55..17f04219 100644
--- a/coverage/backward.py
+++ b/coverage/backward.py
@@ -3,7 +3,7 @@
"""Add things to old Pythons so I can pretend they are newer."""
-# This file does tricky stuff, so disable a pylint warning.
+# This file's purpose is to provide modules to be imported from here.
# pylint: disable=unused-import
import os
@@ -27,11 +27,6 @@ try:
except ImportError:
import configparser
-try:
- import toml
-except ImportError:
- toml = None
-
# What's a string called?
try:
string_class = basestring
diff --git a/coverage/config.py b/coverage/config.py
index 89a0321e..ca3de3bd 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -9,7 +9,7 @@ import os
import re
from coverage import env
-from coverage.backward import configparser, iitems, string_class, toml
+from coverage.backward import configparser, iitems, string_class
from coverage.misc import contract, CoverageException, isolate_module
from coverage.misc import substitute_variables
@@ -260,6 +260,7 @@ class CoverageConfig(object):
"""
_, ext = os.path.splitext(filename)
if ext == '.toml':
+ from coverage.optional import toml
if toml is None:
return False
cp = TomlConfigParser(our_file)
@@ -482,9 +483,9 @@ def config_files_to_try(config_file):
config_file = ".coveragerc"
files_to_try = [
(config_file, True, specified_file),
- ("pyproject.toml", False, False),
("setup.cfg", False, False),
("tox.ini", False, False),
+ ("pyproject.toml", False, False),
]
return files_to_try
diff --git a/coverage/optional.py b/coverage/optional.py
new file mode 100644
index 00000000..ee617b62
--- /dev/null
+++ b/coverage/optional.py
@@ -0,0 +1,68 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""
+Imports that we need at runtime, but might not be present.
+
+When importing one of these modules, always do it in the function where you
+need the module. Some tests will need to remove the module. If you import
+it at the top level of your module, then the test won't be able to simulate
+the module being unimportable.
+
+The import will always succeed, but the value will be None if the module is
+unavailable.
+
+Bad::
+
+ # MyModule.py
+ from coverage.optional import unsure
+
+ def use_unsure():
+ unsure.something()
+
+Good::
+
+ # MyModule.py
+
+ def use_unsure():
+ from coverage.optional import unsure
+ if unsure is None:
+ raise Exception("Module unsure isn't available!")
+
+ unsure.something()
+
+"""
+
+import contextlib
+
+# This file's purpose is to provide modules to be imported from here.
+# pylint: disable=unused-import
+
+# TOML support is an install-time extra option.
+try:
+ import toml
+except ImportError: # pragma: not covered
+ toml = None
+
+
+@contextlib.contextmanager
+def without(modname):
+ """Hide a module for testing.
+
+ Use this in a test function to make an optional module unavailable during
+ the test::
+
+ with coverage.optional.without('toml'):
+ use_toml_somehow()
+
+ Arguments:
+ modname (str): the name of a module importable from
+ `coverage.optional`.
+
+ """
+ real_module = globals()[modname]
+ try:
+ globals()[modname] = None
+ yield
+ finally:
+ globals()[modname] = real_module
diff --git a/coverage/tomlconfig.py b/coverage/tomlconfig.py
index 144b13ca..33647309 100644
--- a/coverage/tomlconfig.py
+++ b/coverage/tomlconfig.py
@@ -8,7 +8,7 @@ import os
import re
from coverage import env
-from coverage.backward import configparser, path_types, toml
+from coverage.backward import configparser, path_types
from coverage.misc import CoverageException, substitute_variables
@@ -32,6 +32,7 @@ class TomlConfigParser:
self._data = []
def read(self, filenames):
+ from coverage.optional import toml
if toml is None:
raise RuntimeError('toml module is not installed.')
diff --git a/tests/test_testing.py b/tests/test_testing.py
index 4a4a013c..15e6f84f 100644
--- a/tests/test_testing.py
+++ b/tests/test_testing.py
@@ -15,6 +15,7 @@ import coverage
from coverage.backunittest import TestCase, unittest
from coverage.files import actual_path
from coverage.misc import StopEverything
+import coverage.optional
from tests.coveragetest import CoverageTest, convert_skip_exceptions
from tests.helpers import CheckUniqueFilenames, re_lines, re_line
@@ -308,3 +309,14 @@ def _same_python_executable(e1, e2):
return True
return False # pragma: only failure
+
+
+def test_optional_without():
+ # pylint: disable=reimported
+ from coverage.optional import toml as toml1
+ with coverage.optional.without('toml'):
+ from coverage.optional import toml as toml2
+ from coverage.optional import toml as toml3
+
+ assert toml1 is toml3 is not None
+ assert toml2 is None