diff options
author | Bernát Gábor <bgabor8@bloomberg.net> | 2020-10-25 18:38:21 +0000 |
---|---|---|
committer | Bernát Gábor <bgabor8@bloomberg.net> | 2020-10-25 19:15:56 +0000 |
commit | 9ef0d811a3ce9e85ba86cec142f2c346c1d420c7 (patch) | |
tree | 55e465ae1cd65cae65c28235e31abb266879ce4f /src/tox | |
parent | afb3b395ddcffa6a62ddee8195b700d4d1abb47b (diff) | |
download | tox-git-9ef0d811a3ce9e85ba86cec142f2c346c1d420c7.tar.gz |
More fixes
Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
Diffstat (limited to 'src/tox')
-rw-r--r-- | src/tox/config/core.py | 7 | ||||
-rw-r--r-- | src/tox/config/sets.py | 4 | ||||
-rw-r--r-- | src/tox/config/source/ini/__init__.py | 19 | ||||
-rw-r--r-- | src/tox/config/source/ini/replace.py | 8 | ||||
-rw-r--r-- | src/tox/execute/local_sub_process/__init__.py | 2 | ||||
-rw-r--r-- | src/tox/provision/__init__.py | 2 | ||||
-rw-r--r-- | src/tox/session/cmd/show_config.py | 4 | ||||
-rw-r--r-- | src/tox/tox_env/api.py | 6 | ||||
-rw-r--r-- | src/tox/tox_env/builder.py | 1 | ||||
-rw-r--r-- | src/tox/tox_env/python/api.py | 6 | ||||
-rw-r--r-- | src/tox/tox_env/python/package.py | 4 | ||||
-rw-r--r-- | src/tox/tox_env/python/runner.py | 11 | ||||
-rw-r--r-- | src/tox/tox_env/python/virtual_env/api.py | 2 | ||||
-rw-r--r-- | src/tox/tox_env/python/virtual_env/package/api.py | 4 | ||||
-rw-r--r-- | src/tox/tox_env/python/virtual_env/package/artifact/wheel.py | 14 | ||||
-rw-r--r-- | src/tox/tox_env/python/virtual_env/runner.py | 75 | ||||
-rw-r--r-- | src/tox/tox_env/runner.py | 45 |
17 files changed, 104 insertions, 110 deletions
diff --git a/src/tox/config/core.py b/src/tox/config/core.py index 0a36233f..8b638180 100644 --- a/src/tox/config/core.py +++ b/src/tox/config/core.py @@ -1,5 +1,6 @@ """Define configuration options that are part of the core tox configurations""" from pathlib import Path +from typing import cast from tox.config.sets import ConfigSet from tox.config.source.api import EnvList @@ -11,19 +12,19 @@ def tox_add_core_config(core: ConfigSet) -> None: core.add_config( keys=["work_dir", "toxworkdir"], of_type=Path, - default=lambda conf, _: conf.core["tox_root"] / ".tox", + default=lambda conf, _: cast(Path, conf.core["tox_root"]) / ".tox", desc="working directory", ) core.add_config( keys=["temp_dir"], of_type=Path, - default=lambda conf, _: conf.core["tox_root"] / ".temp", + default=lambda conf, _: cast(Path, conf.core["tox_root"]) / ".temp", desc="temporary directory cleaned at start", ) core.add_config( keys=["env_list", "envlist"], of_type=EnvList, - default=[], + default=EnvList([]), desc="define environments to automatically run", ) core.add_config( diff --git a/src/tox/config/sets.py b/src/tox/config/sets.py index 03442af5..e6e900ad 100644 --- a/src/tox/config/sets.py +++ b/src/tox/config/sets.py @@ -68,7 +68,7 @@ class ConfigDynamicDefinition(ConfigDefinition[T]): self, keys: Iterable[str], of_type: Type[T], - default: T, + default: Union[Callable[["Config", Optional[str]], T], T], desc: str, post_process: Optional[Callable[[T, "Config"], T]] = None, ) -> None: @@ -133,7 +133,7 @@ class ConfigSet: self, keys: Union[str, Sequence[str]], of_type: Type[V], - default: Any, + default: Union[Callable[["Config", Optional[str]], V], V], desc: str, post_process: Optional[Callable[[V, "Config"], V]] = None, overwrite: bool = False, diff --git a/src/tox/config/source/ini/__init__.py b/src/tox/config/source/ini/__init__.py index dbb3cb47..b8be6779 100644 --- a/src/tox/config/source/ini/__init__.py +++ b/src/tox/config/source/ini/__init__.py @@ -3,7 +3,7 @@ from configparser import ConfigParser, SectionProxy from copy import deepcopy from itertools import chain from pathlib import Path -from typing import Any, Callable, Dict, Iterator, List, Optional, Set +from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Type, TypeVar from tox.config.main import Config from tox.config.sets import ConfigSet @@ -14,10 +14,17 @@ from tox.config.source.ini.replace import BASE_TEST_ENV, CORE_PREFIX, replace TEST_ENV_PREFIX = f"{BASE_TEST_ENV}:" +V = TypeVar("V") + class IniLoader(StrConvert, Loader[str]): """Load configuration from an ini section (ini file is a string to string dictionary)""" + def to(self, raw: str, of_type: Type[V]) -> V: + if of_type == IniLoader: + return self._src[raw] # type: ignore[return-value] + return super(IniLoader, self).to(raw, of_type) + def __init__( self, section: Optional[SectionProxy], @@ -49,19 +56,13 @@ class IniLoader(StrConvert, Loader[str]): def setup_with_conf(self, conf: ConfigSet) -> None: if self.name is None: return # no inheritance for the base tox environment - src = self._src - - class IniLoaderFromKey(IniLoader): - def __init__(self, key: str) -> None: # noqa - loader = src[key] - self.__dict__ = loader.__dict__ - # allow environment inheritance conf.add_config( keys="base", - of_type=List[IniLoaderFromKey], + of_type=List[IniLoader], default=self._default_base, desc="inherit missing keys from these sections", + # builder=lambda raw: self._src[raw], ) self._base = conf["base"] diff --git a/src/tox/config/source/ini/replace.py b/src/tox/config/source/ini/replace.py index 8f94c1c3..bf012b42 100644 --- a/src/tox/config/source/ini/replace.py +++ b/src/tox/config/source/ini/replace.py @@ -24,7 +24,8 @@ def replace( start, end, match = _find_replace_part(value) if not match: break - replaced = _replace_match(conf, name, section_loader, value[start + 1 : end]) + to_replace = value[start + 1 : end] + replaced = _replace_match(conf, name, section_loader, to_replace) new_value = value[:start] + replaced + value[end + 1 :] if new_value == value: # if we're not making progress stop (circular reference?) break @@ -39,6 +40,7 @@ def _find_replace_part(value: str) -> Tuple[int, int, bool]: if end == -1: continue if end > 1 and value[end - 1] == "\\": # ignore escaped + end += 1 continue while start != -1: start = value.rfind("{", 0, end) @@ -71,7 +73,7 @@ def _replace_match( _REPLACE_REF = re.compile( rf""" - (\[({BASE_TEST_ENV}(:(?P<env>[^]]+))?|(?P<section>\w+))\])? # env/section + (\[(?P<full_env>{BASE_TEST_ENV}(:(?P<env>[^]]+))?|(?P<section>\w+))\])? # env/section (?P<key>[a-zA-Z0-9_]+) # key (:(?P<default>.*))? # default value """, @@ -92,6 +94,8 @@ def replace_reference( # otherwise try first in core, then in current env try: key = settings["key"] + if settings["section"] is None and settings["full_env"] == BASE_TEST_ENV: + settings["section"] = BASE_TEST_ENV for src in _config_value_sources(settings["env"], settings["section"], current_env, conf, section_loader): try: return src[key] diff --git a/src/tox/execute/local_sub_process/__init__.py b/src/tox/execute/local_sub_process/__init__.py index be2aac39..0cad9b2f 100644 --- a/src/tox/execute/local_sub_process/__init__.py +++ b/src/tox/execute/local_sub_process/__init__.py @@ -89,7 +89,7 @@ class LocalSubProcessExecuteInstance(ExecuteInstance): return exit_code @staticmethod - def get_stream_file_no(key: str) -> Generator[int, 'Popen[bytes]', None]: + def get_stream_file_no(key: str) -> Generator[int, "Popen[bytes]", None]: if sys.platform != "win32" and getattr(sys, key).isatty(): # on UNIX if tty is set let's forward it via a pseudo terminal import pty diff --git a/src/tox/provision/__init__.py b/src/tox/provision/__init__.py index aa62855b..52816405 100644 --- a/src/tox/provision/__init__.py +++ b/src/tox/provision/__init__.py @@ -52,7 +52,7 @@ def tox_add_core_config(core: ConfigSet) -> None: core.add_config( keys=["min_version", "minversion"], of_type=Version, - default=current_version, + default=Version(current_version), desc="Define the minimal tox version required to run", ) core.add_config( diff --git a/src/tox/session/cmd/show_config.py b/src/tox/session/cmd/show_config.py index edf603bf..3fef8f83 100644 --- a/src/tox/session/cmd/show_config.py +++ b/src/tox/session/cmd/show_config.py @@ -40,11 +40,11 @@ def print_conf(conf: ConfigSet) -> None: value = conf[key] result = str_conf_value(value) if isinstance(result, list): - result = "{}{}".format("\n", "\n".join(f" {i}" for i in result)) + result = "{}{}".format("\n", "\n".join(f" {i}" for i in result)) if result else "" print("{} ={}{}".format(key, " " if result != "" and not result.startswith("\n") else "", result)) unused = conf.unused() if unused: - print(f"!!! unused: {','.join(unused)}") + print(f"# !!! unused: {', '.join(unused)}") def str_conf_value(value: Any) -> Union[List[str], str]: diff --git a/src/tox/tox_env/api.py b/src/tox/tox_env/api.py index 841e09c8..6c45ce79 100644 --- a/src/tox/tox_env/api.py +++ b/src/tox/tox_env/api.py @@ -8,7 +8,7 @@ import shutil import sys from abc import ABC, abstractmethod from pathlib import Path -from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Union +from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Union, cast from tox.config.main import Config from tox.config.sets import ConfigSet @@ -49,13 +49,13 @@ class ToxEnv(ABC): self.conf.add_config( keys=["env_dir", "envdir"], of_type=Path, - default=lambda conf, name: conf.core["work_dir"] / conf[name]["env_name"], + default=lambda conf, name: cast(Path, conf.core["work_dir"]) / cast(str, self.conf["env_name"]), desc="directory assigned to the tox environment", ) self.conf.add_config( keys=["env_tmp_dir", "envtmpdir"], of_type=Path, - default=lambda conf, name: conf.core["work_dir"] / conf[name]["env_name"] / "tmp", + default=lambda conf, name: cast(Path, conf.core["work_dir"]) / cast(str, self.conf["env_name"]) / "tmp", desc="a folder that is always reset at the start of the run", ) diff --git a/src/tox/tox_env/builder.py b/src/tox/tox_env/builder.py index 96ab1ef1..e29a6154 100644 --- a/src/tox/tox_env/builder.py +++ b/src/tox/tox_env/builder.py @@ -64,6 +64,7 @@ class Builder: return env def _build_package_env(self, env: RunToxEnv) -> None: + pkg_env_gen = env.set_package_env() try: name, packager = next(pkg_env_gen) diff --git a/src/tox/tox_env/python/api.py b/src/tox/tox_env/python/api.py index 2fb6b794..88cd79ad 100644 --- a/src/tox/tox_env/python/api.py +++ b/src/tox/tox_env/python/api.py @@ -44,9 +44,9 @@ Deps = Sequence[Union[Path, Requirement]] class Python(ToxEnv, ABC): def __init__(self, conf: ConfigSet, core: ConfigSet, options: Parsed) -> None: - super(Python, self).__init__(conf, core, options) self._base_python: Optional[PythonInfo] = None self._base_python_searched: bool = False + super(Python, self).__init__(conf, core, options) def register_config(self) -> None: super().register_config() @@ -77,10 +77,10 @@ class Python(ToxEnv, ABC): ) return env - def default_base_python(self, conf: "Config", env_name: str) -> List[str]: + def default_base_python(self, conf: "Config", env_name: Optional[str]) -> List[str]: spec = PythonSpec.from_string_spec(env_name) if spec.implementation is not None: - if spec.implementation.lower() in ("cpython", "pypy"): + if spec.implementation.lower() in ("cpython", "pypy") and env_name is not None: return [env_name] return [sys.executable] diff --git a/src/tox/tox_env/python/package.py b/src/tox/tox_env/python/package.py index 343b226c..eed39a7e 100644 --- a/src/tox/tox_env/python/package.py +++ b/src/tox/tox_env/python/package.py @@ -3,7 +3,7 @@ A tox build environment that handles Python packages. """ import sys from abc import ABC, abstractmethod -from typing import List, NoReturn, Union +from typing import List, NoReturn, Optional, Union from packaging.requirements import Requirement @@ -37,5 +37,5 @@ class PythonPackage(Python, PackageToxEnv, ABC): def build_requires(self) -> List[Requirement]: raise NotImplementedError - def default_base_python(self, conf: Config, env_name: str) -> List[str]: + def default_base_python(self, conf: Config, env_name: Optional[str]) -> List[str]: return [sys.executable] diff --git a/src/tox/tox_env/python/runner.py b/src/tox/tox_env/python/runner.py index 4f0816f8..54c95c63 100644 --- a/src/tox/tox_env/python/runner.py +++ b/src/tox/tox_env/python/runner.py @@ -2,7 +2,7 @@ A tox run environment that handles the Python language. """ from abc import ABC -from typing import List, NoReturn, Set +from typing import List, NoReturn from packaging.requirements import Requirement @@ -29,15 +29,6 @@ class PythonRun(Python, RunToxEnv, ABC): ) self.add_package_conf() - def add_package_conf(self) -> None: - if self.core["no_package"] is False: - self.conf.add_config( - keys=["extras"], - of_type=Set[str], - default=[], - desc="extras to install of the target package", - ) - def no_base_python_found(self, base_pythons: List[str]) -> NoReturn: if self.core["skip_missing_interpreters"]: raise Skip diff --git a/src/tox/tox_env/python/virtual_env/api.py b/src/tox/tox_env/python/virtual_env/api.py index 79db2897..5ed7394e 100644 --- a/src/tox/tox_env/python/virtual_env/api.py +++ b/src/tox/tox_env/python/virtual_env/api.py @@ -21,8 +21,8 @@ class VirtualEnv(Python, ABC): """A python executor that uses the virtualenv project with pip""" def __init__(self, conf: ConfigSet, core: ConfigSet, options: Parsed): - super().__init__(conf, core, options) self._virtualenv_session: Optional[Session] = None # type: ignore[no-any-unimported] + super().__init__(conf, core, options) def default_pass_env(self) -> List[str]: env = super().default_pass_env() diff --git a/src/tox/tox_env/python/virtual_env/package/api.py b/src/tox/tox_env/python/virtual_env/package/api.py index 2a3d74b9..0e7151ad 100644 --- a/src/tox/tox_env/python/virtual_env/package/api.py +++ b/src/tox/tox_env/python/virtual_env/package/api.py @@ -70,13 +70,13 @@ class Pep517VirtualEnvPackage(VirtualEnv, PythonPackage, ABC): self.conf.add_config( keys=["meta_dir"], of_type=Path, - default=lambda conf, name: conf[name]["env_dir"] / ".meta", + default=lambda conf, name: cast(Path, self.conf["env_dir"]) / ".meta", desc="directory assigned to the tox environment", ) self.conf.add_config( keys=["pkg_dir"], of_type=Path, - default=lambda conf, name: conf[name]["env_dir"] / "dist", + default=lambda conf, name: cast(Path, self.conf["env_dir"]) / "dist", desc="directory assigned to the tox environment", ) diff --git a/src/tox/tox_env/python/virtual_env/package/artifact/wheel.py b/src/tox/tox_env/python/virtual_env/package/artifact/wheel.py index 769c9255..e8ca02ac 100644 --- a/src/tox/tox_env/python/virtual_env/package/artifact/wheel.py +++ b/src/tox/tox_env/python/virtual_env/package/artifact/wheel.py @@ -1,7 +1,5 @@ -from configparser import ConfigParser, NoSectionError from typing import Any, Dict -from tox.config.sets import ConfigSet from tox.plugin.impl import impl from tox.tox_env.register import ToxEnvRegister @@ -9,18 +7,6 @@ from .api import Pep517VirtualEnvPackageArtifact class Pep517VirtualEnvPackageWheel(Pep517VirtualEnvPackageArtifact): - @staticmethod - def default_universal_wheel(core: ConfigSet) -> bool: - parser = ConfigParser() - success = parser.read(filenames=[str(core["tox_root"] / "setup.cfg")]) - universal = False - if success: - try: - universal = parser.get("bdist_wheel", "universal") == "1" - except NoSectionError: - pass - return universal - @property def build_type(self) -> str: return "wheel" diff --git a/src/tox/tox_env/python/virtual_env/runner.py b/src/tox/tox_env/python/virtual_env/runner.py index 5d92c147..a270c487 100644 --- a/src/tox/tox_env/python/virtual_env/runner.py +++ b/src/tox/tox_env/python/virtual_env/runner.py @@ -1,12 +1,10 @@ """ A tox python environment runner that uses the virtualenv project. """ -from typing import Optional, Tuple +from typing import Optional, Set +from tox.config.main import Config from tox.plugin.impl import impl -from tox.tox_env.python.virtual_env.package.artifact.wheel import ( - Pep517VirtualEnvPackageWheel, -) from tox.tox_env.register import ToxEnvRegister from ..runner import PythonRun @@ -22,53 +20,52 @@ class VirtualEnvRunner(VirtualEnv, PythonRun): def id() -> str: return "virtualenv" - def add_package_conf(self) -> None: - if self.core["no_package"] is True: - return + def add_package_conf(self) -> bool: + if super().add_package_conf() is False: + return False self.conf.add_config( keys="package", of_type=PackageType, default=PackageType.sdist, desc=f"package installation mode - {' | '.join(i.name for i in PackageType)} ", ) - if self.conf["package"] == PackageType.skip: - return - super().add_package_conf() - self.core.add_config( + pkg_type: PackageType = self.conf["package"] + if pkg_type == PackageType.skip: + return False + self.conf.add_constant( + keys=["package_tox_env_type"], + desc="tox package type used to package", + value=virtual_env_package_id(pkg_type), + ) + + def default_package_name(conf: Config, name: Optional[str]) -> str: + result = ".package" + + # when building wheels we need to ensure that the built package is compatible with the target environment + # compatibility is documented within https://www.python.org/dev/peps/pep-0427/#file-name-convention + # a wheel tag looks like: {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl + # python only code are often compatible at major level (unless universal wheel in which case both 2 and 3) + # c-extension codes are trickier, but as of today both poetry/setuptools uses pypa/wheels logic + # https://github.com/pypa/wheel/blob/master/src/wheel/bdist_wheel.py#L234-L280 + # technically, the build tags can be passed in as CLI args to the build backend, but for now it's easier + # to just create a new build env for every target env + if pkg_type is PackageType.wheel: + result = f"{result}-{self.conf['env_name']}" + return result + + self.conf.add_config( keys=["package_env", "isolated_build_env"], of_type=str, - default=".package", + default=default_package_name, desc="tox environment used to package", ) - package = self.conf["package"] self.conf.add_config( - keys="package_tox_env_type", - of_type=str, - default=virtual_env_package_id(package), - desc="tox package type used to package", + keys=["extras"], + of_type=Set[str], + default=set(), + desc="extras to install of the target package", ) - if self.conf["package"] is PackageType.wheel: - self.conf.add_config( - keys="universal_wheel", - of_type=bool, - default=Pep517VirtualEnvPackageWheel.default_universal_wheel(self.core), - desc="tox package type used to package", - ) - - def has_package(self) -> bool: - return self.core["no_package"] or self.conf["package"] is not PackageType.skip - - def package_env_name_type(self) -> Optional[Tuple[str, str]]: - if not self.has_package(): - return None - package = self.conf["package"] - package_env_type = self.conf["package_tox_env_type"] - name = self.core["package_env"] - # we can get away with a single common package if: sdist, dev, universal wheel - if package is PackageType.wheel and self.conf["universal_wheel"] is False: - # if version specific wheel one per env - name = "{}-{}".format(name, self.conf["env_name"]) - return name, package_env_type + return True def install_package(self) -> None: if self.package_env is not None: diff --git a/src/tox/tox_env/runner.py b/src/tox/tox_env/runner.py index 9e98a318..54b3d44a 100644 --- a/src/tox/tox_env/runner.py +++ b/src/tox/tox_env/runner.py @@ -1,6 +1,6 @@ from abc import ABC from pathlib import Path -from typing import TYPE_CHECKING, Generator, List, Optional, Tuple +from typing import TYPE_CHECKING, Generator, List, Optional, Tuple, cast from tox.config.sets import ConfigSet from tox.config.source.api import Command, EnvList @@ -14,15 +14,16 @@ if TYPE_CHECKING: class RunToxEnv(ToxEnv, ABC): def __init__(self, conf: ConfigSet, core: ConfigSet, options: "Parsed") -> None: - super().__init__(conf, core, options) + self.has_package = False self.package_env: Optional[PackageToxEnv] = None + super().__init__(conf, core, options) def register_config(self) -> None: super().register_config() self.conf.add_config( keys=["description"], of_type=str, - default=None, + default="", desc="description attached to the tox environment", ) self.conf.add_config( @@ -46,14 +47,14 @@ class RunToxEnv(ToxEnv, ABC): self.conf.add_config( keys=["change_dir", "changedir"], of_type=Path, - default=lambda conf, name: conf.core["tox_root"], + default=lambda conf, name: cast(Path, conf.core["tox_root"]), desc="Change to this working directory when executing the test command.", ) self.conf.add_config( "depends", of_type=EnvList, desc="tox environments that this environment depends on (must be run after those)", - default=[], + default=EnvList([]), ) self.conf.add_config( "parallel_show_output", @@ -61,20 +62,32 @@ class RunToxEnv(ToxEnv, ABC): default=False, desc="if set to True the content of the output will always be shown when running in parallel mode", ) + if self.add_package_conf() is True: + self.has_package = True + + def add_package_conf(self) -> bool: + """If this returns True package_env and package_tox_env_type configurations must be defined""" + core_no_package: bool = self.core["no_package"] + if core_no_package is True: + return False + self.conf.add_config( + keys="skip_install", + of_type=bool, + default=False, + desc="skip installation", + ) + skip_install: bool = self.conf["skip_install"] + if skip_install: + return False + return True def set_package_env(self) -> Generator[Tuple[str, str], PackageToxEnv, None]: - if self.core["no_package"]: + if self.has_package is False: return - res = self.package_env_name_type() - if res is not None: - package_tox_env = yield res - self.package_env = package_tox_env - - def package_env_name_type(self) -> Optional[Tuple[str, str]]: - raise NotImplementedError - - def has_package(self) -> bool: - return self.package_env_name_type() is not None + name = self.conf["package_env"] + of_type = self.conf["package_tox_env_type"] + package_tox_env = yield name, of_type + self.package_env = package_tox_env def clean(self, package_env: bool = True) -> None: super().clean() |