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

Use tomllib on Python 3.11 #1359

Merged
merged 1 commit into from May 15, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 14 additions & 9 deletions coverage/tomlconfig.py
Expand Up @@ -6,16 +6,21 @@
import configparser
import os
import re
import sys

from coverage.exceptions import ConfigError
from coverage.misc import import_third_party, substitute_variables

# TOML support is an install-time extra option. (Import typing is here because
# import_third_party will unload any module that wasn't already imported.
# tomli imports typing, and if we unload it, later it's imported again, and on
# Python 3.6, this causes infinite recursion.)
import typing # pylint: disable=unused-import, wrong-import-order
tomli = import_third_party("tomli")
if sys.version_info >= (3, 11):
import tomllib
else:
# TOML support on Python 3.10 and below is an install-time extra option.
# (Import typing is here because import_third_party will unload any module
# that wasn't already imported. tomli imports typing, and if we unload it,
# later it's imported again, and on Python 3.6, this causes infinite
# recursion.)
import typing # pylint: disable=unused-import, wrong-import-order
tomllib = import_third_party("tomli")


class TomlDecodeError(Exception):
Expand Down Expand Up @@ -45,11 +50,11 @@ def read(self, filenames):
toml_text = fp.read()
except OSError:
return []
if tomli is not None:
if tomllib is not None:
toml_text = substitute_variables(toml_text, os.environ)
try:
self.data = tomli.loads(toml_text)
except tomli.TOMLDecodeError as err:
self.data = tomllib.loads(toml_text)
except tomllib.TOMLDecodeError as err:
raise TomlDecodeError(str(err)) from err
return [filename]
else:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -108,7 +108,7 @@ def better_set_verbosity(v):

extras_require={
# Enable pyproject.toml support.
'toml': ['tomli'],
'toml': ['tomli; python_version < "3.11"'],
},

# We need to get HTML assets from our htmlfiles directory.
Expand Down
11 changes: 7 additions & 4 deletions tests/test_config.py
Expand Up @@ -4,6 +4,7 @@
"""Test the config file handling for coverage.py"""

import math
import sys
from collections import OrderedDict

from unittest import mock
Expand Down Expand Up @@ -706,27 +707,29 @@ 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, 'tomli'):
with without_module(coverage.tomlconfig, 'tomllib'):
msg = "Couldn't read 'cov.toml' as a config file"
with pytest.raises(ConfigError, match=msg):
coverage.Coverage(config_file="cov.toml")

@pytest.mark.skipif(sys.version_info >= (3, 11), reason="Python 3.11 has toml in stdlib")
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, 'tomli'):
with without_module(coverage.tomlconfig, 'tomllib'):
msg = "Can't read 'cov.toml' without TOML support"
with pytest.raises(ConfigError, match=msg):
coverage.Coverage(config_file="cov.toml")

@pytest.mark.skipif(sys.version_info >= (3, 11), reason="Python 3.11 has toml in stdlib")
def test_no_toml_installed_pyproject_toml(self):
# Can't have coverage config in pyproject.toml without toml installed.
self.make_file("pyproject.toml", """\
# A toml file!
[tool.coverage.run]
xyzzy = 17
""")
with without_module(coverage.tomlconfig, 'tomli'):
with without_module(coverage.tomlconfig, 'tomllib'):
msg = "Can't read 'pyproject.toml' without TOML support"
with pytest.raises(ConfigError, match=msg):
coverage.Coverage()
Expand All @@ -738,7 +741,7 @@ def test_no_toml_installed_pyproject_no_coverage(self):
[tool.something]
xyzzy = 17
""")
with without_module(coverage.tomlconfig, 'tomli'):
with without_module(coverage.tomlconfig, 'tomllib'):
cov = coverage.Coverage()
# We get default settings:
assert not cov.config.timid
Expand Down
8 changes: 4 additions & 4 deletions tests/test_testing.py
Expand Up @@ -357,10 +357,10 @@ def _same_python_executable(e1, e2):


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

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