Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support TOML v1.0.0 syntax in pyproject.toml #1186

Merged
merged 4 commits into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions coverage/tomlconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

# TOML support is an install-time extra option.
try:
import toml
import tomli
except ImportError: # pragma: not covered
toml = None
tomli = None


class TomlDecodeError(Exception):
Expand Down Expand Up @@ -44,12 +44,12 @@ def read(self, filenames):
toml_text = fp.read()
except OSError:
return []
if toml:
if tomli is not None:
toml_text = substitute_variables(toml_text, os.environ)
try:
self.data = toml.loads(toml_text)
except toml.TomlDecodeError as err:
raise TomlDecodeError(*err.args)
self.data = tomli.loads(toml_text)
except tomli.TOMLDecodeError as err:
raise TomlDecodeError(str(err))
return [filename]
else:
has_toml = re.search(r"^\[tool\.coverage\.", toml_text, flags=re.MULTILINE)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def better_set_verbosity(v):

extras_require={
# Enable pyproject.toml support.
'toml': ['toml'],
'toml': ['tomli'],
},

# We need to get HTML assets from our htmlfiles directory.
Expand Down
2 changes: 1 addition & 1 deletion tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def without_module(using_module, missing_module_name):
Use this in a test function to make an optional module unavailable during
the test::

with without_module(product.something, 'toml'):
with without_module(product.something, 'tomli'):
use_toml_somehow()

Arguments:
Expand Down
53 changes: 25 additions & 28 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,31 +186,28 @@ def test_parse_errors(self):
with pytest.raises(CoverageException, match=msg):
coverage.Coverage()

def test_toml_parse_errors(self):
@pytest.mark.parametrize("bad_config,msg", [
("[tool.coverage.run]\ntimid = \"maybe?\"\n", r"maybe[?]"),
("[tool.coverage.run\n", None),
('[tool.coverage.report]\nexclude_lines = ["foo("]\n',
r"Invalid \[tool.coverage.report\].exclude_lines value u?'foo\(': "
r"(unbalanced parenthesis|missing \))"),
('[tool.coverage.report]\npartial_branches = ["foo["]\n',
r"Invalid \[tool.coverage.report\].partial_branches value u?'foo\[': "
r"(unexpected end of regular expression|unterminated character set)"),
('[tool.coverage.report]\npartial_branches_always = ["foo***"]\n',
r"Invalid \[tool.coverage.report\].partial_branches_always value "
r"u?'foo\*\*\*': "
r"multiple repeat"),
('[tool.coverage.run]\nconcurrency="foo"', "not a list"),
("[tool.coverage.report]\nprecision=1.23", "not an integer"),
('[tool.coverage.report]\nfail_under="s"', "not a float"),
])
def test_toml_parse_errors(self, bad_config, msg):
# Im-parsable values raise CoverageException, with details.
bad_configs_and_msgs = [
("[tool.coverage.run]\ntimid = \"maybe?\"\n", r"maybe[?]"),
("[tool.coverage.run\n", r"Key group"),
('[tool.coverage.report]\nexclude_lines = ["foo("]\n',
r"Invalid \[tool.coverage.report\].exclude_lines value u?'foo\(': "
r"(unbalanced parenthesis|missing \))"),
('[tool.coverage.report]\npartial_branches = ["foo["]\n',
r"Invalid \[tool.coverage.report\].partial_branches value u?'foo\[': "
r"(unexpected end of regular expression|unterminated character set)"),
('[tool.coverage.report]\npartial_branches_always = ["foo***"]\n',
r"Invalid \[tool.coverage.report\].partial_branches_always value "
r"u?'foo\*\*\*': "
r"multiple repeat"),
('[tool.coverage.run]\nconcurrency="foo"', "not a list"),
("[tool.coverage.report]\nprecision=1.23", "not an integer"),
('[tool.coverage.report]\nfail_under="s"', "not a float"),
]

for bad_config, msg in bad_configs_and_msgs:
print("Trying %r" % bad_config)
self.make_file("pyproject.toml", bad_config)
with pytest.raises(CoverageException, match=msg):
coverage.Coverage()
self.make_file("pyproject.toml", bad_config)
with pytest.raises(CoverageException, match=msg):
coverage.Coverage()

def test_environment_vars_in_config(self):
# Config files can have $envvars in them.
Expand Down Expand Up @@ -715,15 +712,15 @@ def test_note_is_obsolete(self):

def test_no_toml_installed_no_toml(self):
# Can't read a toml file that doesn't exist.
with without_module(coverage.tomlconfig, 'toml'):
with without_module(coverage.tomlconfig, 'tomli'):
msg = "Couldn't read 'cov.toml' as a config file"
with pytest.raises(CoverageException, match=msg):
coverage.Coverage(config_file="cov.toml")

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 without_module(coverage.tomlconfig, 'toml'):
with without_module(coverage.tomlconfig, 'tomli'):
msg = "Can't read 'cov.toml' without TOML support"
with pytest.raises(CoverageException, match=msg):
coverage.Coverage(config_file="cov.toml")
Expand All @@ -735,7 +732,7 @@ def test_no_toml_installed_pyproject_toml(self):
[tool.coverage.run]
xyzzy = 17
""")
with without_module(coverage.tomlconfig, 'toml'):
with without_module(coverage.tomlconfig, 'tomli'):
msg = "Can't read 'pyproject.toml' without TOML support"
with pytest.raises(CoverageException, match=msg):
coverage.Coverage()
Expand All @@ -747,7 +744,7 @@ def test_no_toml_installed_pyproject_no_coverage(self):
[tool.something]
xyzzy = 17
""")
with without_module(coverage.tomlconfig, 'toml'):
with without_module(coverage.tomlconfig, 'tomli'):
cov = coverage.Coverage()
# We get default settings:
assert not cov.config.timid
Expand Down
8 changes: 4 additions & 4 deletions tests/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,10 @@ def _same_python_executable(e1, e2):


def test_without_module():
toml1 = tomlconfig.toml
with without_module(tomlconfig, 'toml'):
toml2 = tomlconfig.toml
toml3 = tomlconfig.toml
toml1 = tomlconfig.tomli
with without_module(tomlconfig, 'tomli'):
toml2 = tomlconfig.tomli
toml3 = tomlconfig.tomli

assert toml1 is toml3 is not None
assert toml2 is None
Expand Down