Repository: pypa/pip

Issue:
Traceback running setup.py clean: ModuleNotFoundError: No module named 'setuptools'
It was reported in issue #6634 that when building a wheel using (PEP 517), a traceback displayed when `setup.py clean` failed was unneeded.

Excerpt:

```
  Running `pyo3-pack build -i python --no-sdist --bindings=cffi`
  Error: Command '['pyo3-pack', 'build', '-i', 'python', '--no-sdist', '--bindings=cffi']' returned non-zero exit status 1.
  Building wheel for cffi-pure (PEP 517) ... error
  ERROR: Failed building wheel for cffi-pure
  Running setup.py clean for cffi-pure
  Running command /home/konsti/pyo3-pack/venv/bin/python -u -c 'import setuptools, tokenize;__file__='"'"'/tmp/pip-req-build-sphjswqi/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' clean --all
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
  ModuleNotFoundError: No module named 'setuptools'
  ERROR: Failed cleaning build dir for cffi-pure
Failed to build cffi-pure
```




Discussion:
:eyes:

You don't have to explain what happened here, but it'd be nice if you do. 😛 
I closed this after creating it because when I looked at the code, it seemed like the traceback only displays when verbose mode is enabled, in which case it doesn't really seem like an issue (it's verbose mode). But I'm not 100% sure, so I'll leave it open for completeness for now.
This looks like a bug, unless I'm missing something.

1. [pep517 build takes place](https://github.com/pypa/pip/blob/6343e8093fbbb471e8a35d0c8982cf4897215a7f/src/pip/_internal/wheel_builder.py#L301-L303)
2. [`_build_one_pep517` returns `None` on exception](https://github.com/pypa/pip/blob/6343e8093fbbb471e8a35d0c8982cf4897215a7f/src/pip/_internal/wheel_builder.py#L373-L375)
3. [if `wheel` is `None`, then execute `_clean_one`](https://github.com/pypa/pip/blob/6343e8093fbbb471e8a35d0c8982cf4897215a7f/src/pip/_internal/wheel_builder.py#L330)
4. [unconditionally execute `setup.py clean` and log exceptions at error level](https://github.com/pypa/pip/blob/6343e8093fbbb471e8a35d0c8982cf4897215a7f/src/pip/_internal/wheel_builder.py#L380-L391)

Resolving patch:
diff --git a/src/pip/_internal/wheel_builder.py b/src/pip/_internal/wheel_builder.py
--- a/src/pip/_internal/wheel_builder.py
+++ b/src/pip/_internal/wheel_builder.py
@@ -231,11 +231,12 @@ def _build_one_inside_env(
                     req.name, e,
                 )
         # Ignore return, we can't do anything else useful.
-        _clean_one(req, global_options)
+        if not req.use_pep517:
+            _clean_one_legacy(req, global_options)
         return None
 
 
-def _clean_one(req, global_options):
+def _clean_one_legacy(req, global_options):
     # type: (InstallRequirement, List[str]) -> bool
     clean_args = make_setuptools_clean_args(
         req.setup_py_path,


Test patch:
diff --git a/tests/data/packages/pep517_wrapper_buildsys/mybuildsys.py b/tests/data/packages/pep517_wrapper_buildsys/mybuildsys.py
--- a/tests/data/packages/pep517_wrapper_buildsys/mybuildsys.py
+++ b/tests/data/packages/pep517_wrapper_buildsys/mybuildsys.py
@@ -8,6 +8,9 @@
 
 
 def build_wheel(*a, **kw):
+    if os.environ.get("PIP_TEST_FAIL_BUILD_WHEEL"):
+        raise RuntimeError("Failing build_wheel, as requested.")
+
     # Create the marker file to record that the hook was called
     with open(os.environ['PIP_TEST_MARKER_FILE'], 'wb'):
         pass
diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py
--- a/tests/functional/test_install.py
+++ b/tests/functional/test_install.py
@@ -1256,6 +1256,7 @@ def test_cleanup_after_failed_wheel(script, with_wheel):
     shebang = open(script_py, 'r').readline().strip()
     assert shebang != '#!python', shebang
     # OK, assert that we *said* we were cleaning up:
+    # /!\ if in need to change this, also change test_pep517_no_legacy_cleanup
     assert "Running setup.py clean for wheelbrokenafter" in str(res), str(res)
 
 
diff --git a/tests/functional/test_install_cleanup.py b/tests/functional/test_install_cleanup.py
--- a/tests/functional/test_install_cleanup.py
+++ b/tests/functional/test_install_cleanup.py
@@ -136,3 +136,19 @@ def test_cleanup_prevented_upon_build_dir_exception(script, data):
     assert result.returncode == PREVIOUS_BUILD_DIR_ERROR, str(result)
     assert "pip can't proceed" in result.stderr, str(result)
     assert exists(build_simple), str(result)
+
+
+@pytest.mark.network
+def test_pep517_no_legacy_cleanup(script, data, with_wheel):
+    """Test a PEP 517 failed build does not attempt a legacy cleanup"""
+    to_install = data.packages.joinpath('pep517_wrapper_buildsys')
+    script.environ["PIP_TEST_FAIL_BUILD_WHEEL"] = "1"
+    res = script.pip(
+        'install', '-f', data.find_links, to_install,
+        expect_error=True
+    )
+    # Must not have built the package
+    expected = "Failed building wheel for pep517-wrapper-buildsys"
+    assert expected in str(res)
+    # Must not have attempted legacy cleanup
+    assert "setup.py clean" not in str(res)