Skip to content

Commit

Permalink
Use tomllib on Python 3.11 (#1359)
Browse files Browse the repository at this point in the history
Co-authored-by: hauntsaninja <>
  • Loading branch information
hauntsaninja committed May 15, 2022
1 parent e8973c5 commit 0d67ae1
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 18 deletions.
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

0 comments on commit 0d67ae1

Please sign in to comment.