Skip to content

Commit 957a280

Browse files
authored
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 <[email protected]>
1 parent 6a4174e commit 957a280

File tree

6 files changed

+37
-7
lines changed

6 files changed

+37
-7
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ repos:
1212
- id: end-of-file-fixer
1313
- id: trailing-whitespace
1414
- repo: https://github.com/asottile/pyupgrade
15-
rev: v2.23.3
15+
rev: v2.25.0
1616
hooks:
1717
- id: pyupgrade
1818
args: ["--py36-plus"]
@@ -24,7 +24,7 @@ repos:
2424
hooks:
2525
- id: isort
2626
- repo: https://github.com/psf/black
27-
rev: 21.7b0
27+
rev: 21.8b0
2828
hooks:
2929
- id: black
3030
args:
@@ -44,11 +44,11 @@ repos:
4444
- id: tox-ini-fmt
4545
args: [ "-p", "fix" ]
4646
- repo: https://github.com/asottile/blacken-docs
47-
rev: v1.10.0
47+
rev: v1.11.0
4848
hooks:
4949
- id: blacken-docs
5050
additional_dependencies:
51-
- black==21.7b0
51+
- black==21.8b0
5252
- repo: https://github.com/pre-commit/pygrep-hooks
5353
rev: v1.9.0
5454
hooks:
@@ -67,7 +67,7 @@ repos:
6767
- id: flake8
6868
additional_dependencies:
6969
- flake8-bugbear==21.4.3
70-
- flake8-comprehensions==3.5
70+
- flake8-comprehensions==3.6.1
7171
- flake8-pytest-style==1.5
7272
- flake8-spellcheck==0.24
7373
- flake8-unused-arguments==0.0.6

docs/changelog/2209.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Do not allow constructing ``ConfigSet`` directly and implement ``__contains__`` for ``Loader`` -- by
2+
:user:`gaborbernat`.

src/tox/config/loader/api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ def found_keys(self) -> Set[str]:
7777
def __repr__(self) -> str:
7878
return f"{type(self).__name__}"
7979

80+
def __contains__(self, item: str) -> bool:
81+
return item in self.found_keys()
82+
8083
def load(
8184
self,
8285
key: str,
@@ -91,8 +94,10 @@ def load(
9194
9295
:param key: the key under it lives
9396
:param of_type: the type to convert to
97+
:param kwargs: keyword arguments to forward
9498
:param conf: the configuration object of this tox session (needed to manifest the value)
9599
:param env_name: env name
100+
:param chain: a chain of lookups
96101
:return: the converted type
97102
"""
98103
if key in self.overrides:

src/tox/config/sets.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from abc import ABC, abstractmethod
12
from pathlib import Path
23
from typing import (
34
TYPE_CHECKING,
@@ -27,7 +28,7 @@
2728
V = TypeVar("V")
2829

2930

30-
class ConfigSet:
31+
class ConfigSet(ABC):
3132
"""A set of configuration that belong together (such as a tox environment settings, core tox settings)"""
3233

3334
def __init__(self, conf: "Config"):
@@ -118,8 +119,9 @@ def load(self, item: str, chain: Optional[List[str]] = None) -> Any:
118119
return config_definition.__call__(self._conf, self.loaders, self.name, chain)
119120

120121
@property
122+
@abstractmethod
121123
def name(self) -> Optional[str]:
122-
return None
124+
raise NotImplementedError
123125

124126
def __repr__(self) -> str:
125127
return f"{self.__class__.__name__}(loaders={self.loaders!r})"
@@ -197,6 +199,10 @@ def work_dir_builder(conf: "Config", env_name: Optional[str]) -> Path: # noqa
197199
def _on_duplicate_conf(self, key: str, definition: ConfigDefinition[V]) -> None: # noqa: U100
198200
pass # core definitions may be defined multiple times as long as all their options match, first defined wins
199201

202+
@property
203+
def name(self) -> Optional[str]:
204+
return None
205+
200206

201207
class EnvConfigSet(ConfigSet):
202208
"""Configuration set for a tox environment"""

tests/config/loader/test_memory_loader.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,9 @@ def test_memory_loader(value: Any, of_type: Type[Any]) -> None:
4545
def test_memory_found_keys() -> None:
4646
loader = MemoryLoader(a=1, c=2)
4747
assert loader.found_keys() == {"a", "c"}
48+
49+
50+
def test_memory_loader_contains() -> None:
51+
loader = MemoryLoader(a=1)
52+
assert "a" in loader
53+
assert "b" not in loader

tests/config/test_sets.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Callable, Dict, Optional, Set, TypeVar
44

55
import pytest
6+
from pytest_mock import MockerFixture
67

78
from tests.conftest import ToxIniCreator
89
from tox.config.cli.parser import Parsed
@@ -134,13 +135,18 @@ def test_config_dynamic_not_equal(conf_builder: ConfBuilder) -> None:
134135

135136
def test_define_custom_set(tox_project: ToxProjectCreator) -> None:
136137
class MagicConfigSet(ConfigSet):
138+
137139
SECTION = "magic"
138140

139141
def __init__(self, conf: Config):
140142
super().__init__(conf)
141143
self.add_config("a", of_type=int, default=0, desc="number")
142144
self.add_config("b", of_type=str, default="", desc="string")
143145

146+
@property
147+
def name(self) -> Optional[str]:
148+
return self.SECTION
149+
144150
project = tox_project({"tox.ini": "[testenv]\npackage=skip\n[magic]\na = 1\nb = ok"})
145151
result = project.run()
146152

@@ -150,3 +156,8 @@ def __init__(self, conf: Config):
150156
assert repr(conf) == "MagicConfigSet(loaders=[IniLoader(section=<Section: magic>, overrides={})])"
151157

152158
assert isinstance(result.state.conf.options, Parsed)
159+
160+
161+
def test_do_not_allow_create_config_set(mocker: MockerFixture) -> None:
162+
with pytest.raises(TypeError, match="Can't instantiate"):
163+
ConfigSet(mocker.create_autospec(Config)) # type: ignore # the type checker also warns that ABC

0 commit comments

Comments
 (0)