diff options
author | Bernát Gábor <bgabor8@bloomberg.net> | 2021-09-10 16:23:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-10 16:23:50 +0100 |
commit | 957a280af356575b00ac6bee34fb02919e95766e (patch) | |
tree | 69a9028665391f3c1c14d9135bb207eb095e5a33 | |
parent | 6a4174e0aeef61511a1bde5f9c00c731d27c56fa (diff) | |
download | tox-git-957a280af356575b00ac6bee34fb02919e95766e.tar.gz |
Make ConfigSet ABC and add contains to Loader (#2209)
The ConfigSet should be always specialized via subclassing. The contains
is a quality of life imporvement for plugins. Also bumped the tools
while at it.
Signed-off-by: Bernát Gábor <gaborjbernat@gmail.com>
-rw-r--r-- | .pre-commit-config.yaml | 10 | ||||
-rw-r--r-- | docs/changelog/2209.bugfix.rst | 2 | ||||
-rw-r--r-- | src/tox/config/loader/api.py | 5 | ||||
-rw-r--r-- | src/tox/config/sets.py | 10 | ||||
-rw-r--r-- | tests/config/loader/test_memory_loader.py | 6 | ||||
-rw-r--r-- | tests/config/test_sets.py | 11 |
6 files changed, 37 insertions, 7 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 72dc1bfd..d6dd3e68 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v2.23.3 + rev: v2.25.0 hooks: - id: pyupgrade args: ["--py36-plus"] @@ -24,7 +24,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: 21.7b0 + rev: 21.8b0 hooks: - id: black args: @@ -44,11 +44,11 @@ repos: - id: tox-ini-fmt args: [ "-p", "fix" ] - repo: https://github.com/asottile/blacken-docs - rev: v1.10.0 + rev: v1.11.0 hooks: - id: blacken-docs additional_dependencies: - - black==21.7b0 + - black==21.8b0 - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.9.0 hooks: @@ -67,7 +67,7 @@ repos: - id: flake8 additional_dependencies: - flake8-bugbear==21.4.3 - - flake8-comprehensions==3.5 + - flake8-comprehensions==3.6.1 - flake8-pytest-style==1.5 - flake8-spellcheck==0.24 - flake8-unused-arguments==0.0.6 diff --git a/docs/changelog/2209.bugfix.rst b/docs/changelog/2209.bugfix.rst new file mode 100644 index 00000000..ec4e97c8 --- /dev/null +++ b/docs/changelog/2209.bugfix.rst @@ -0,0 +1,2 @@ +Do not allow constructing ``ConfigSet`` directly and implement ``__contains__`` for ``Loader`` -- by +:user:`gaborbernat`. diff --git a/src/tox/config/loader/api.py b/src/tox/config/loader/api.py index f436a1ff..1f664eb5 100644 --- a/src/tox/config/loader/api.py +++ b/src/tox/config/loader/api.py @@ -77,6 +77,9 @@ class Loader(Convert[T]): def __repr__(self) -> str: return f"{type(self).__name__}" + def __contains__(self, item: str) -> bool: + return item in self.found_keys() + def load( self, key: str, @@ -91,8 +94,10 @@ class Loader(Convert[T]): :param key: the key under it lives :param of_type: the type to convert to + :param kwargs: keyword arguments to forward :param conf: the configuration object of this tox session (needed to manifest the value) :param env_name: env name + :param chain: a chain of lookups :return: the converted type """ if key in self.overrides: diff --git a/src/tox/config/sets.py b/src/tox/config/sets.py index dc8c3eea..c11f7d82 100644 --- a/src/tox/config/sets.py +++ b/src/tox/config/sets.py @@ -1,3 +1,4 @@ +from abc import ABC, abstractmethod from pathlib import Path from typing import ( TYPE_CHECKING, @@ -27,7 +28,7 @@ if TYPE_CHECKING: V = TypeVar("V") -class ConfigSet: +class ConfigSet(ABC): """A set of configuration that belong together (such as a tox environment settings, core tox settings)""" def __init__(self, conf: "Config"): @@ -118,8 +119,9 @@ class ConfigSet: return config_definition.__call__(self._conf, self.loaders, self.name, chain) @property + @abstractmethod def name(self) -> Optional[str]: - return None + raise NotImplementedError def __repr__(self) -> str: return f"{self.__class__.__name__}(loaders={self.loaders!r})" @@ -197,6 +199,10 @@ class CoreConfigSet(ConfigSet): def _on_duplicate_conf(self, key: str, definition: ConfigDefinition[V]) -> None: # noqa: U100 pass # core definitions may be defined multiple times as long as all their options match, first defined wins + @property + def name(self) -> Optional[str]: + return None + class EnvConfigSet(ConfigSet): """Configuration set for a tox environment""" diff --git a/tests/config/loader/test_memory_loader.py b/tests/config/loader/test_memory_loader.py index 814148cc..6b35e300 100644 --- a/tests/config/loader/test_memory_loader.py +++ b/tests/config/loader/test_memory_loader.py @@ -45,3 +45,9 @@ def test_memory_loader(value: Any, of_type: Type[Any]) -> None: def test_memory_found_keys() -> None: loader = MemoryLoader(a=1, c=2) assert loader.found_keys() == {"a", "c"} + + +def test_memory_loader_contains() -> None: + loader = MemoryLoader(a=1) + assert "a" in loader + assert "b" not in loader diff --git a/tests/config/test_sets.py b/tests/config/test_sets.py index 9c03c71d..1f61486b 100644 --- a/tests/config/test_sets.py +++ b/tests/config/test_sets.py @@ -3,6 +3,7 @@ from pathlib import Path from typing import Callable, Dict, Optional, Set, TypeVar import pytest +from pytest_mock import MockerFixture from tests.conftest import ToxIniCreator from tox.config.cli.parser import Parsed @@ -134,6 +135,7 @@ def test_config_dynamic_not_equal(conf_builder: ConfBuilder) -> None: def test_define_custom_set(tox_project: ToxProjectCreator) -> None: class MagicConfigSet(ConfigSet): + SECTION = "magic" def __init__(self, conf: Config): @@ -141,6 +143,10 @@ def test_define_custom_set(tox_project: ToxProjectCreator) -> None: self.add_config("a", of_type=int, default=0, desc="number") self.add_config("b", of_type=str, default="", desc="string") + @property + def name(self) -> Optional[str]: + return self.SECTION + project = tox_project({"tox.ini": "[testenv]\npackage=skip\n[magic]\na = 1\nb = ok"}) result = project.run() @@ -150,3 +156,8 @@ def test_define_custom_set(tox_project: ToxProjectCreator) -> None: assert repr(conf) == "MagicConfigSet(loaders=[IniLoader(section=<Section: magic>, overrides={})])" assert isinstance(result.state.conf.options, Parsed) + + +def test_do_not_allow_create_config_set(mocker: MockerFixture) -> None: + with pytest.raises(TypeError, match="Can't instantiate"): + ConfigSet(mocker.create_autospec(Config)) # type: ignore # the type checker also warns that ABC |