Skip to content

Commit

Permalink
Switch from toml to tomli/tomllib (#152)
Browse files Browse the repository at this point in the history
* Switch from toml to tomli/tomllib

The toml package is unmaintained, so it was appropriate to move away
from it as a dependency. The tomli package is the most appropriate
alternative, because it was incorporated into the Python standard
library as of Python 3.11, under the name tomllib.

Due to the prevalence of tomli as a compulsory dependency of many other
Python code quality tools (such as black, mypy, pytest and pylint),
there's a big chance that users will already have it installed if
they're using Import Linter. Therefore, tomli is now a compulsory
dependency (if you're using versions of Python earlier than 3.11).

This change has several benefits:

- Reduces the overall number of dependencies in the average Python
  application
- Removes a dependency on an unmaintained project
- Reduces the complexity of configuration-related testing
- Greatly simplifies the tox configuration

Co-authored-by: Matthew Gamble <matthew.gamble@rea-group.com>
  • Loading branch information
mwgamble and mwg-rea committed Jan 10, 2023
1 parent a993014 commit c74d593
Show file tree
Hide file tree
Showing 9 changed files with 26 additions and 97 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Contributors
* Daniele Esposti - https://github.com/expobrain
* Petter Friberg - https://github.com/flaeppe
* James Owen - https://github.com/leamingrad
* Matthew Gamble - https://github.com/mwgamble
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

latest
------

* Switch from optional dependency of ``toml`` to required dependency of ``tomli`` for Python versions < 3.11.

1.6.0 (2022-12-7)
-----------------

Expand Down
4 changes: 0 additions & 4 deletions docs/toml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,3 @@ Following, an example with a layered configuration:
"medium",
"low",
]
Please note, that in order to use TOML files, you need to install the extra require ``toml``::

pip install import-linter[toml]
4 changes: 0 additions & 4 deletions pytest.ini

This file was deleted.

8 changes: 6 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ def read(*names, **kwargs):
"Topic :: Utilities",
],
python_requires=">=3.7",
install_requires=["click>=6", "grimp>=2.0", "typing-extensions>=3.10.0.0"],
extras_require={"toml": ["toml"]},
install_requires=[
"click>=6",
"grimp>=2.0",
"tomli>=1.2.1; python_version < '3.11'",
"typing-extensions>=3.10.0.0",
],
entry_points={
"console_scripts": ["lint-imports = importlinter.cli:lint_imports_command"]
},
Expand Down
16 changes: 6 additions & 10 deletions src/importlinter/adapters/user_options.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import configparser
from typing import Any, Dict, Optional, List
import abc
import sys

try:
import toml

_HAS_TOML = True
except ImportError:
_HAS_TOML = False
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib

from importlinter.application import file_finding
from importlinter.application.app_config import settings
Expand Down Expand Up @@ -86,11 +85,8 @@ class TomlFileUserOptionReader(AbstractUserOptionReader):
potential_config_filenames = ["pyproject.toml"]

def _read_config_filename(self, config_filename: str) -> Optional[UserOptions]:
if not _HAS_TOML:
return None

file_contents = settings.FILE_SYSTEM.read(config_filename)
data = toml.loads(file_contents)
data = tomllib.loads(file_contents)

tool_data = data.get("tool", {})
session_options = tool_data.get("importlinter", {})
Expand Down
46 changes: 3 additions & 43 deletions tests/functional/test_lint_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,50 +30,10 @@
(testpackage_directory, ".externalbrokencontract.ini", cli.EXIT_STATUS_ERROR),
(multipleroots_directory, ".multiplerootskeptcontract.ini", cli.EXIT_STATUS_SUCCESS),
(multipleroots_directory, ".multiplerootsbrokencontract.ini", cli.EXIT_STATUS_ERROR),
# TOML versions.
pytest.param(
testpackage_directory,
".setup.toml",
cli.EXIT_STATUS_ERROR,
marks=pytest.mark.toml_not_installed,
),
pytest.param(
testpackage_directory,
".setup.toml",
cli.EXIT_STATUS_SUCCESS,
marks=pytest.mark.toml_installed,
),
pytest.param(
testpackage_directory,
".customkeptcontract.toml",
cli.EXIT_STATUS_ERROR,
marks=pytest.mark.toml_not_installed,
),
pytest.param(
testpackage_directory,
".customkeptcontract.toml",
cli.EXIT_STATUS_SUCCESS,
marks=pytest.mark.toml_installed,
),
pytest.param(
testpackage_directory,
".customkeptcontract.toml",
cli.EXIT_STATUS_ERROR,
marks=pytest.mark.toml_not_installed,
),
pytest.param(
testpackage_directory,
".customkeptcontract.toml",
cli.EXIT_STATUS_SUCCESS,
marks=pytest.mark.toml_installed,
),
(testpackage_directory, ".setup.toml", cli.EXIT_STATUS_SUCCESS),
(testpackage_directory, ".customkeptcontract.toml", cli.EXIT_STATUS_SUCCESS),
(testpackage_directory, ".externalkeptcontract.ini", cli.EXIT_STATUS_SUCCESS),
pytest.param(
testpackage_directory,
".externalkeptcontract.toml",
cli.EXIT_STATUS_SUCCESS,
marks=pytest.mark.toml_installed,
),
(testpackage_directory, ".externalkeptcontract.toml", cli.EXIT_STATUS_SUCCESS),
# Unmatched ignore imports alerting.
# The return value depends on what this is set to.
(
Expand Down
22 changes: 0 additions & 22 deletions tests/unit/adapters/test_user_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ def test_respects_passed_filename(passed_filename, expected_foo_value):
assert expected_options == options


@pytest.mark.toml_installed
@pytest.mark.parametrize(
"contents, expected_options",
(
Expand Down Expand Up @@ -190,24 +189,3 @@ def test_toml_file_reader(contents, expected_options):

options = TomlFileUserOptionReader().read_options()
assert expected_options == options


@pytest.mark.toml_not_installed
def test_toml_file_reader_returns_none_when_toml_not_installed():
valid_toml = """
[something]
foo = 1
bar = "hello"
[tool.importlinter]
foo = "hello"
bar = 999
"""
settings.configure(
FILE_SYSTEM=FakeFileSystem(
content_map={"/path/to/folder/pyproject.toml": valid_toml},
working_directory="/path/to/folder",
)
)

assert TomlFileUserOptionReader().read_options() is None
17 changes: 5 additions & 12 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ envlist =
clean,
check,
docs,
{py37,py38,py39,py310,py311}-{notoml,toml},
py37,py38,py39,py310,py311
report

[testenv]
Expand All @@ -25,24 +25,16 @@ deps =
pytest-cov~=3.0.0
PyYAML~=6.0
commands =
{posargs:pytest -m "not toml_installed" --cov --cov-report=term-missing -vv tests}

[testenv:{py37,py38,py39,py310,py311}-toml]
deps =
{[testenv]deps}
toml~=0.10.2
commands =
{posargs:pytest -m "not toml_not_installed" --cov --cov-report=term-missing -vv tests}
{posargs:pytest --cov --cov-report=term-missing -vv tests}


[testenv:check]
deps =
{[testenv:py311-toml]deps}
{[testenv]deps}
black~=22.3.0
flake8~=4.0.1
mypy~=0.730
types-PyYAML
types-toml
commands =
black --check src tests
flake8 src tests setup.py
Expand All @@ -69,9 +61,10 @@ skip_install = true
deps = coverage

[gh-actions]
# Run check on both Python 3.10 and 3.11, because of our version-dependent dependency on tomli.
python =
3.7: py37, report
3.8: py38, report
3.9: py39, report
3.10: py310, report
3.10: py310, report, check
3.11: py311, report, check, docs

0 comments on commit c74d593

Please sign in to comment.