Repository: tox-dev/tox

Issue:
PEP517 in-tree build backend-path support in get-build-requires
Using tox 3.19, in-tree build with `backend-path` fails with:
```
.package start: get-build-requires /Users/nicolas/Workspaces/proj/.tox/.package
setting PATH=/Users/nicolas/Workspaces/proj/.tox/.package/bin:/Users/nicolas/.virtualenvs/proj-python3.8/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
[15159] /Users/nicolas/Workspaces/proj$ /Users/nicolas/Workspaces/proj/.tox/.package/bin/python /Users/nicolas/.virtualenvs/proj-python3.8/lib/python3.8/site-packages/tox/helper/build_requires.py build_package '' >.tox/.package/log/.package-0.log
ERROR: invocation failed (exit code 1), logfile: /Users/nicolas/Workspaces/proj/.tox/.package/log/.package-0.log
=================================================================================== log start ====================================================================================
Traceback (most recent call last):
  File "/Users/nicolas/.virtualenvs/proj-python3.8/lib/python3.8/site-packages/tox/helper/build_requires.py", line 7, in <module>
    backend = __import__(backend_spec, fromlist=["_trash"])
ModuleNotFoundError: No module named 'build_package'
```

`pyproject.toml` content is:
```
[build-system]
requires = [
  "setuptools >= 40.9.0",
  "wheel",
]
build-backend = "build_package"
backend-path = ["scripts"]
```

A quick patch of `tox/helper/build_requires.py` to use the backend paths similarly to `tox/helper/build_isolated.py` fixes the issue. I can do a PR but the patch duplicates the `_ensure_module_in_paths` function.

Comments:

Patch:
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 8eac46c9..1c57f659 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -78,6 +78,7 @@ Morgan Fainberg
 Naveen S R
 Nick Douma
 Nick Prendergast
+Nicolas Vivet
 Oliver Bestwalter
 Pablo Galindo
 Paul Moore
diff --git a/docs/changelog/1654.bugfix.rst b/docs/changelog/1654.bugfix.rst
new file mode 100644
index 00000000..0d20d48a
--- /dev/null
+++ b/docs/changelog/1654.bugfix.rst
@@ -0,0 +1,1 @@
+Support for PEP517 in-tree build backend-path key in ``get-build-requires``. - by :user:`nizox`
diff --git a/src/tox/helper/build_requires.py b/src/tox/helper/build_requires.py
index cd074f97..aafb258c 100644
--- a/src/tox/helper/build_requires.py
+++ b/src/tox/helper/build_requires.py
@@ -1,8 +1,12 @@
 import json
+import os
 import sys
 
 backend_spec = sys.argv[1]
 backend_obj = sys.argv[2] if len(sys.argv) >= 3 else None
+backend_paths = sys.argv[3].split(os.path.pathsep) if len(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 38ebe8ea..998ce25f 100644
--- a/src/tox/package/builder/isolated.py
+++ b/src/tox/package/builder/isolated.py
@@ -92,6 +92,15 @@ def get_build_info(folder):
         abort("backend-path key at build-system section must be a list, if specified")
     backend_paths = [folder.join(p) for p in backend_paths]
 
+    normalized_folder = os.path.normcase(str(folder.realpath()))
+    normalized_paths = (os.path.normcase(str(path.realpath())) for path in backend_paths)
+
+    if not all(
+        os.path.commonprefix((normalized_folder, path)) == normalized_folder
+        for path in normalized_paths
+    ):
+        abort("backend-path must exist in the project root")
+
     return BuildInfo(requires, module, obj, backend_paths)
 
 
@@ -129,6 +138,7 @@ def get_build_requires(build_info, package_venv, setup_dir):
                 BUILD_REQUIRE_SCRIPT,
                 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,


Test patch:
diff --git a/tests/unit/package/builder/test_package_builder_isolated.py b/tests/unit/package/builder/test_package_builder_isolated.py
index 63e0685a..458e43bb 100644
--- a/tests/unit/package/builder/test_package_builder_isolated.py
+++ b/tests/unit/package/builder/test_package_builder_isolated.py
@@ -153,3 +153,43 @@ def test_package_isolated_toml_bad_backend_path(initproj):
     backend-path = 42
     """,
     )
+
+
+def test_package_isolated_toml_backend_path_outside_root(initproj):
+    """Verify that a 'backend-path' outside the project root is forbidden."""
+    toml_file_check(
+        initproj,
+        6,
+        "backend-path must exist in the project root",
+        """
+    [build-system]
+    requires = []
+    build-backend = 'setuptools.build_meta'
+    backend-path = ['..']
+    """,
+    )
+
+
+def test_verbose_isolated_build_in_tree(initproj, mock_venv, cmd):
+    initproj(
+        "example123-0.5",
+        filedefs={
+            "tox.ini": """
+                    [tox]
+                    isolated_build = true
+                    """,
+            "build.py": """
+                    from setuptools.build_meta import *
+                    """,
+            "pyproject.toml": """
+                    [build-system]
+                    requires = ["setuptools >= 35.0.2"]
+                    build-backend = 'build'
+                    backend-path = ['.']
+                                """,
+        },
+    )
+    result = cmd("--sdistonly", "-v", "-v", "-v", "-e", "py")
+    assert "running sdist" in result.out, result.out
+    assert "running egg_info" in result.out, result.out
+    assert "Writing example123-0.5{}setup.cfg".format(os.sep) in result.out, result.out