tox-dev / tox
1 15
import inspect
2 15
import re
3 15
from concurrent.futures import Future
4 15
from configparser import ConfigParser, SectionProxy
5 15
from contextlib import contextmanager
6 15
from typing import TYPE_CHECKING, Generator, List, Optional, Set, Type, TypeVar
7

8 15
from tox.config.loader.api import Loader, Override
9 15
from tox.config.loader.ini.factor import filter_for_env
10 15
from tox.config.loader.ini.replace import replace
11 15
from tox.config.loader.str_convert import StrConvert
12 15
from tox.config.set_env import SetEnv
13 15
from tox.report import HandledError
14

15
if TYPE_CHECKING:
16
    from tox.config.main import Config
17

18 15
V = TypeVar("V")
19 15
_COMMENTS = re.compile(r"(\s)*(?<!\\)#.*")
20

21

22 15
class IniLoader(StrConvert, Loader[str]):
23
    """Load configuration from an ini section (ini file is a string to string dictionary)"""
24

25 15
    def __init__(self, section: str, parser: ConfigParser, overrides: List[Override], core_prefix: str) -> None:
26 15
        self._section: SectionProxy = parser[section]
27 15
        self._parser = parser
28 15
        self.core_prefix = core_prefix
29 15
        super().__init__(overrides)
30

31 15
    def load_raw(self, key: str, conf: Optional["Config"], env_name: Optional[str]) -> str:
32 15
        value = self._section[key]
33

34
        # strip comments
35 15
        elements: List[str] = []
36 15
        for line in value.split("\n"):
37 15
            if not line.startswith("#"):
38 15
                part = _COMMENTS.sub("", line)
39 15
                elements.append(part.replace("\\#", "#"))
40 15
        strip_comments = "\n".join(elements)
41

42 15
        if conf is None:  # conf is None when we're loading the global tox configuration file for the CLI
43 15
            factor_filtered = strip_comments  # we don't support factor and replace functionality there
44
        else:
45 15
            factor_filtered = filter_for_env(strip_comments, env_name)  # select matching factors
46 15
        collapsed = factor_filtered.replace("\r", "").replace("\\\n", "")  # collapse explicit new-line escape
47 15
        return collapsed
48

49 15
    @contextmanager
50 15
    def build(
51
        self,
52
        future: "Future[V]",
53
        key: str,
54
        of_type: Type[V],
55
        conf: Optional["Config"],
56
        env_name: Optional[str],
57
        raw: str,
58
        chain: List[str],
59
    ) -> Generator[str, None, None]:
60 15
        delay_replace = inspect.isclass(of_type) and issubclass(of_type, SetEnv)
61

62 15
        def replacer(raw_: str, chain_: List[str]) -> str:
63 15
            if conf is None:
64 15
                replaced = raw_  # no replacement supported in the core section
65
            else:
66 15
                try:
67 15
                    replaced = replace(conf, env_name, self, raw_, chain_)  # do replacements
68 15
                except Exception as exception:
69 15
                    if isinstance(exception, HandledError):
70
                        raise
71 15
                    msg = f"replace failed in {'tox' if env_name is None else env_name}.{key} with {exception!r}"
72 15
                    raise HandledError(msg) from exception
73 15
            return replaced
74

75 15
        if not delay_replace:
76 15
            raw = replacer(raw, chain)
77 15
        yield raw
78 15
        if delay_replace:
79 15
            converted = future.result()
80 15
            if hasattr(converted, "replacer"):  # pragma: no branch
81 15
                converted.replacer = replacer  # type: ignore[attr-defined]
82

83 15
    def found_keys(self) -> Set[str]:
84 15
        return set(self._section.keys())
85

86 15
    def get_section(self, name: str) -> Optional[SectionProxy]:
87
        # needed for non tox environment replacements
88 15
        if self._parser.has_section(name):
89 15
            return self._parser[name]
90 15
        return None
91

92 15
    def __repr__(self) -> str:
93 15
        return f"{self.__class__.__name__}(section={self._section}, overrides={self.overrides!r})"

Read our documentation on viewing source code .

Loading