summaryrefslogtreecommitdiff
path: root/src/tox/tox_env/python/package.py
diff options
context:
space:
mode:
authorBernát Gábor <bgabor8@bloomberg.net>2021-09-26 09:39:34 +0100
committerGitHub <noreply@github.com>2021-09-26 09:39:34 +0100
commitcc04fe987ff006f54b3d15ae0227d233816e7e4f (patch)
tree000ae4bc9e0c539af342810fcfd7fb09cde6f462 /src/tox/tox_env/python/package.py
parentd448d824f3d8514b560dfebc6819b5ddb09876ad (diff)
downloadtox-git-cc04fe987ff006f54b3d15ae0227d233816e7e4f.tar.gz
Support for external packages and builders (#2235)
Diffstat (limited to 'src/tox/tox_env/python/package.py')
-rw-r--r--src/tox/tox_env/python/package.py57
1 files changed, 55 insertions, 2 deletions
diff --git a/src/tox/tox_env/python/package.py b/src/tox/tox_env/python/package.py
index 5964bcaf..2d43a96f 100644
--- a/src/tox/tox_env/python/package.py
+++ b/src/tox/tox_env/python/package.py
@@ -3,12 +3,19 @@ A tox build environment that handles Python packages.
"""
from abc import ABC, abstractmethod
from pathlib import Path
-from typing import Any, Sequence, Tuple
+from typing import TYPE_CHECKING, Any, Dict, Generator, Iterator, Optional, Sequence, Tuple, Union, cast
from packaging.requirements import Requirement
+from ...config.sets import EnvConfigSet
+from ..api import ToxEnvCreateArgs
from ..package import Package, PackageToxEnv, PathPackage
+from ..runner import RunToxEnv
from .api import Python
+from .pip.req_file import PythonDeps
+
+if TYPE_CHECKING:
+ from tox.config.main import Config
class PythonPackage(Package):
@@ -34,6 +41,10 @@ class DevLegacyPackage(PythonPathPackageWithDeps):
class PythonPackageToxEnv(Python, PackageToxEnv, ABC):
+ def __init__(self, create_args: ToxEnvCreateArgs) -> None:
+ self._wheel_build_envs: Dict[str, PythonPackageToxEnv] = {}
+ super().__init__(create_args)
+
def register_config(self) -> None:
super().register_config()
@@ -43,5 +54,47 @@ class PythonPackageToxEnv(Python, PackageToxEnv, ABC):
self.installer.install(self.requires(), PythonPackageToxEnv.__name__, "requires")
@abstractmethod
- def requires(self) -> Tuple[Requirement, ...]:
+ def requires(self) -> Union[Tuple[Requirement, ...], PythonDeps]:
raise NotImplementedError
+
+ def register_run_env(self, run_env: RunToxEnv) -> Generator[Tuple[str, str], PackageToxEnv, None]:
+ yield from super().register_run_env(run_env)
+ if not isinstance(run_env, Python) or run_env.conf["package"] != "wheel" or "wheel_build_env" in run_env.conf:
+ return
+
+ def default_wheel_tag(conf: "Config", env_name: Optional[str]) -> str:
+ # https://www.python.org/dev/peps/pep-0427/#file-name-convention
+ # when building wheels we need to ensure that the built package is compatible with the target env
+ # compatibility is documented within https://www.python.org/dev/peps/pep-0427/#file-name-convention
+ # a wheel tag example: {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/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
+ run_py = cast(Python, run_env).base_python
+ if run_py is None:
+ raise ValueError(f"could not resolve base python for {self.conf.name}")
+
+ default_pkg_py = self.base_python
+ if (
+ default_pkg_py.version_no_dot == run_py.version_no_dot
+ and default_pkg_py.impl_lower == run_py.impl_lower
+ ):
+ return self.conf.name
+
+ return f"{self.conf.name}-{run_py.impl_lower}{run_py.version_no_dot}"
+
+ run_env.conf.add_config(
+ keys=["wheel_build_env"],
+ of_type=str,
+ default=default_wheel_tag,
+ desc="wheel tag to use for building applications",
+ )
+ pkg_env = run_env.conf["wheel_build_env"]
+ result = yield pkg_env, run_env.conf["package_tox_env_type"]
+ self._wheel_build_envs[pkg_env] = cast(PythonPackageToxEnv, result)
+
+ def child_pkg_envs(self, run_conf: EnvConfigSet) -> Iterator[PackageToxEnv]:
+ if run_conf["package"] == "wheel":
+ env = self._wheel_build_envs.get(run_conf["wheel_build_env"])
+ if env is not None and env.name != self.name:
+ yield env