From a3c9bd52d2d5303644212e32beb43f22becfd6de Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 14 Jul 2020 00:42:00 +0200 Subject: [PATCH] Implement support for PEP517 backend-path setting Resolves #1575 --- src/tox/helper/build_isolated.py | 11 +++++++++++ src/tox/package/builder/isolated.py | 27 +++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/tox/helper/build_isolated.py b/src/tox/helper/build_isolated.py index 55ea41b8d..6f5944385 100644 --- a/src/tox/helper/build_isolated.py +++ b/src/tox/helper/build_isolated.py @@ -1,8 +1,19 @@ +"""PEP 517 build backend invocation script. + +It accepts externally parsed build configuration from `[build-system]` +in `pyproject.toml` and invokes an API endpoint for building an sdist +tarball. +""" + +import os import sys dist_folder = sys.argv[1] backend_spec = sys.argv[2] backend_obj = sys.argv[3] if len(sys.argv) >= 4 else None +backend_paths = sys.argv[4].split(os.path.pathsep) if sys.argv[4] else [] + +sys.path[:0] = backend_paths backend = __import__(backend_spec, fromlist=["_trash"]) if backend_obj: diff --git a/src/tox/package/builder/isolated.py b/src/tox/package/builder/isolated.py index c02aa109a..8c53ea8e9 100644 --- a/src/tox/package/builder/isolated.py +++ b/src/tox/package/builder/isolated.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import json +import os from collections import namedtuple import six @@ -11,7 +12,9 @@ from tox.config import DepConfig, get_py_project_toml from tox.constants import BUILD_ISOLATED, BUILD_REQUIRE_SCRIPT -BuildInfo = namedtuple("BuildInfo", ["requires", "backend_module", "backend_object"]) +BuildInfo = namedtuple( + "BuildInfo", ["requires", "backend_module", "backend_object", "backend_paths"], +) def build(config, session): @@ -47,6 +50,17 @@ def build(config, session): return perform_isolated_build(build_info, package_venv, config.distdir, config.setupdir) +def _ensure_importable_in_path(python_path, importable): + """Figure out if the importable exists in the given path.""" + mod_chunks = importable.split(".") + pkg_path = python_path.join(*mod_chunks[:-1]) + if not pkg_path.exists(): + return False + if pkg_path.join(mod_chunks[-1], "__init__.py").exists(): + return True + return pkg_path.join(mod_chunks[-1] + ".py").exists() + + def get_build_info(folder): toml_file = folder.join("pyproject.toml") @@ -84,7 +98,15 @@ def abort(message): module = args[0] obj = args[1] if len(args) > 1 else "" - return BuildInfo(requires, module, obj) + backend_paths = build_system.get("backend-path", []) + if not isinstance(backend_paths, list): + abort("backend-path key at build-system section must be a list, if specified") + backend_paths = [folder.join(p) for p in backend_paths] + + if backend_paths and not any(_ensure_importable_in_path(p, module) for p in backend_paths): + abort("build-backend must exist in one of the paths specified by backend-path") + + return BuildInfo(requires, module, obj, backend_paths) def perform_isolated_build(build_info, package_venv, dist_dir, setup_dir): @@ -103,6 +125,7 @@ def perform_isolated_build(build_info, package_venv, dist_dir, setup_dir): str(dist_dir), build_info.backend_module, build_info.backend_object, + os.path.pathsep.join(str(p) for p in build_info.backend_paths), ], returnout=True, action=action,