From a09b1714c26cde1542044f44295600679d4368fc Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 18 Jan 2021 18:08:56 -0500 Subject: [PATCH] Simplify the testing of the toml extra, fixing #1084 --- CHANGES.rst | 4 +++ coverage/optional.py | 76 ------------------------------------------ coverage/tomlconfig.py | 8 +++-- tests/helpers.py | 19 +++++++++++ tests/test_config.py | 10 +++--- tests/test_testing.py | 19 ++++++----- 6 files changed, 44 insertions(+), 92 deletions(-) delete mode 100644 coverage/optional.py diff --git a/CHANGES.rst b/CHANGES.rst index 12468ea56..b517c0ce2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -38,12 +38,16 @@ Unreleased - Combining files on Windows across drives how works properly, fixing `issue 577`_. Thanks, `Valentine Lab `_. +- Fix an obscure warning from deep in the _decimal module, as reported in + `issue 1084`_. + - Update to support Python 3.10 alphas in progress, including `PEP 626: Precise line numbers for debugging and other tools `_. .. _issue 577: https://github.com/nedbat/coveragepy/issues/577 .. _issue 732: https://github.com/nedbat/coveragepy/issues/732 .. _issue 922: https://github.com/nedbat/coveragepy/issues/922 +.. _issue 1084: https://github.com/nedbat/coveragepy/issues/1084 .. _issue 1086: https://github.com/nedbat/coveragepy/issues/1086 .. _issue 1090: https://github.com/nedbat/coveragepy/issues/1090 .. _pr1080: https://github.com/nedbat/coveragepy/pull/1080 diff --git a/coverage/optional.py b/coverage/optional.py deleted file mode 100644 index 507a1ada7..000000000 --- a/coverage/optional.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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 - import unsure - - def use_unsure(): - unsure.something() - -Also 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 25542f99e..3ad581571 100644 --- a/coverage/tomlconfig.py +++ b/coverage/tomlconfig.py @@ -11,6 +11,12 @@ from coverage.backward import configparser, path_types from coverage.misc import CoverageException, substitute_variables +# TOML support is an install-time extra option. +try: + import toml +except ImportError: # pragma: not covered + toml = None + class TomlDecodeError(Exception): """An exception class that exists even when toml isn't installed.""" @@ -29,8 +35,6 @@ def __init__(self, our_file): self.data = None def read(self, filenames): - from coverage.optional import toml - # RawConfigParser takes a filename or list of filenames, but we only # ever call this with a single filename. assert isinstance(filenames, path_types) diff --git a/tests/helpers.py b/tests/helpers.py index 9c6a0ad8e..0621d7a94 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -10,6 +10,7 @@ import subprocess import sys +import mock from unittest_mixins import ModuleCleaner from coverage import env @@ -203,3 +204,21 @@ def arcs_to_arcz_repr(arcs): line += _arcs_to_arcz_repr_one(b) repr_list.append(line) return "\n".join(repr_list) + "\n" + + +def without_module(using_module, missing_module_name): + """ + Hide a module for testing. + + Use this in a test function to make an optional module unavailable during + the test:: + + with without_module(product.something, 'toml'): + use_toml_somehow() + + Arguments: + using_module: a module in which to hide `missing_module_name`. + missing_module_name (str): the name of the module to hide. + + """ + return mock.patch.object(using_module, missing_module_name, None) diff --git a/tests/test_config.py b/tests/test_config.py index dd86303f2..4225540c0 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,9 +10,9 @@ import coverage from coverage.misc import CoverageException -import coverage.optional from tests.coveragetest import CoverageTest, UsingModulesMixin +from tests.helpers import without_module class ConfigTest(CoverageTest): @@ -712,7 +712,7 @@ def test_nocoveragerc_file_when_specified(self): def test_no_toml_installed_no_toml(self): # Can't read a toml file that doesn't exist. - with coverage.optional.without('toml'): + with without_module(coverage.tomlconfig, 'toml'): msg = "Couldn't read 'cov.toml' as a config file" with self.assertRaisesRegex(CoverageException, msg): coverage.Coverage(config_file="cov.toml") @@ -720,7 +720,7 @@ def test_no_toml_installed_no_toml(self): def test_no_toml_installed_explicit_toml(self): # Can't specify a toml config file if toml isn't installed. self.make_file("cov.toml", "# A toml file!") - with coverage.optional.without('toml'): + with without_module(coverage.tomlconfig, 'toml'): msg = "Can't read 'cov.toml' without TOML support" with self.assertRaisesRegex(CoverageException, msg): coverage.Coverage(config_file="cov.toml") @@ -732,7 +732,7 @@ def test_no_toml_installed_pyproject_toml(self): [tool.coverage.run] xyzzy = 17 """) - with coverage.optional.without('toml'): + with without_module(coverage.tomlconfig, 'toml'): msg = "Can't read 'pyproject.toml' without TOML support" with self.assertRaisesRegex(CoverageException, msg): coverage.Coverage() @@ -744,7 +744,7 @@ def test_no_toml_installed_pyproject_no_coverage(self): [tool.something] xyzzy = 17 """) - with coverage.optional.without('toml'): + with without_module(coverage.tomlconfig, 'toml'): cov = coverage.Coverage() # We get default settings: self.assertFalse(cov.config.timid) diff --git a/tests/test_testing.py b/tests/test_testing.py index c5d464309..34ea32635 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -12,14 +12,16 @@ import pytest import coverage +from coverage import tomlconfig 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 arcs_to_arcz_repr, arcz_to_arcs -from tests.helpers import CheckUniqueFilenames, re_lines, re_line +from tests.helpers import ( + arcs_to_arcz_repr, arcz_to_arcs, + CheckUniqueFilenames, re_lines, re_line, without_module, +) def test_xdist_sys_path_nuttiness_is_fixed(): @@ -323,12 +325,11 @@ def _same_python_executable(e1, e2): 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 +def test_without_module(): + toml1 = tomlconfig.toml + with without_module(tomlconfig, 'toml'): + toml2 = tomlconfig.toml + toml3 = tomlconfig.toml assert toml1 is toml3 is not None assert toml2 is None