Repository: pypa/pip

Issue:
Test suite assumes virtualenv installs setuptools
### Description

Running `nox -s test-3.11` in a system that has virtualenv (globally) configured to not install setuptools or wheel, fails with the error

```
Traceback (most recent call last):
  File "C:\Work\Projects\pip\setup.py", line 4, in <module>
    from setuptools import find_packages, setup
ModuleNotFoundError: No module named 'setuptools'
```

### Expected behavior

Test suite doesn't depend on the user's configuration being the default. (It's worth noting that I think virtualenv is going to stop installing setuptools in Python 3.12).

### pip version

23.2.dev0

### Python version

3.11.2

### OS

Windows

### How to Reproduce

1. Put `no_setuptools=True` in your `virtualenv.ini`.
2. Run `nox -s test-3.11`

### Output

_No response_

### Code of Conduct

- [X] I agree to follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/).


Discussion:
I'm logging this as a reminder, it's not a critical issue right now

Resolving patch:
diff --git a/noxfile.py b/noxfile.py
--- a/noxfile.py
+++ b/noxfile.py
@@ -67,7 +67,7 @@ def should_update_common_wheels() -> bool:
 # -----------------------------------------------------------------------------
 # Development Commands
 # -----------------------------------------------------------------------------
-@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "pypy3"])
+@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"])
 def test(session: nox.Session) -> None:
     # Get the common wheels.
     if should_update_common_wheels():
@@ -89,6 +89,7 @@ def test(session: nox.Session) -> None:
         shutil.rmtree(sdist_dir, ignore_errors=True)
 
     # fmt: off
+    session.install("setuptools")
     session.run(
         "python", "setup.py", "sdist", "--formats=zip", "--dist-dir", sdist_dir,
         silent=True,
@@ -351,6 +352,7 @@ def build_dists(session: nox.Session) -> List[str]:
         )
 
     session.log("# Build distributions")
+    session.install("setuptools", "wheel")
     session.run("python", "setup.py", "sdist", "bdist_wheel", silent=True)
     produced_dists = glob.glob("dist/*")
 


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -49,7 +49,7 @@
 if TYPE_CHECKING:
     from typing import Protocol
 
-    from wsgi import WSGIApplication
+    from _typeshed.wsgi import WSGIApplication
 else:
     # TODO: Protocol was introduced in Python 3.8. Remove this branch when
     # dropping support for Python 3.7.
@@ -645,7 +645,12 @@ def pip(self, *args: Union[str, Path]) -> InMemoryPipResult:
         try:
             returncode = pip_entry_point([os.fspath(a) for a in args])
         except SystemExit as e:
-            returncode = e.code or 0
+            if isinstance(e.code, int):
+                returncode = e.code
+            elif e.code:
+                returncode = 1
+            else:
+                returncode = 0
         finally:
             sys.stdout = orig_stdout
         return InMemoryPipResult(returncode, stdout.getvalue())
diff --git a/tests/functional/test_build_env.py b/tests/functional/test_build_env.py
--- a/tests/functional/test_build_env.py
+++ b/tests/functional/test_build_env.py
@@ -1,4 +1,5 @@
 import os
+import sys
 from textwrap import dedent
 from typing import Optional
 
@@ -203,6 +204,31 @@ def test_build_env_overlay_prefix_has_priority(script: PipTestEnvironment) -> No
     assert result.stdout.strip() == "2.0", str(result)
 
 
+if sys.version_info < (3, 12):
+    BUILD_ENV_ERROR_DEBUG_CODE = r"""
+            from distutils.sysconfig import get_python_lib
+            print(
+                f'imported `pkg` from `{pkg.__file__}`',
+                file=sys.stderr)
+            print('system sites:\n  ' + '\n  '.join(sorted({
+                            get_python_lib(plat_specific=0),
+                            get_python_lib(plat_specific=1),
+                    })), file=sys.stderr)
+    """
+else:
+    BUILD_ENV_ERROR_DEBUG_CODE = r"""
+            from sysconfig import get_paths
+            paths = get_paths()
+            print(
+                f'imported `pkg` from `{pkg.__file__}`',
+                file=sys.stderr)
+            print('system sites:\n  ' + '\n  '.join(sorted({
+                            paths['platlib'],
+                            paths['purelib'],
+                    })), file=sys.stderr)
+    """
+
+
 @pytest.mark.usefixtures("enable_user_site")
 def test_build_env_isolation(script: PipTestEnvironment) -> None:
     # Create dummy `pkg` wheel.
@@ -231,8 +257,7 @@ def test_build_env_isolation(script: PipTestEnvironment) -> None:
     run_with_build_env(
         script,
         "",
-        r"""
-        from distutils.sysconfig import get_python_lib
+        f"""
         import sys
 
         try:
@@ -240,17 +265,9 @@ def test_build_env_isolation(script: PipTestEnvironment) -> None:
         except ImportError:
             pass
         else:
-            print(
-                f'imported `pkg` from `{pkg.__file__}`',
-                file=sys.stderr)
-            print('system sites:\n  ' + '\n  '.join(sorted({
-                          get_python_lib(plat_specific=0),
-                          get_python_lib(plat_specific=1),
-                    })), file=sys.stderr)
-            print('sys.path:\n  ' + '\n  '.join(sys.path), file=sys.stderr)
+            {BUILD_ENV_ERROR_DEBUG_CODE}
+            print('sys.path:\\n  ' + '\\n  '.join(sys.path), file=sys.stderr)
             sys.exit(1)
-        """
-        f"""
         # second check: direct check of exclusion of system site packages
         import os
 
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
@@ -848,14 +848,18 @@ def test_editable_install__local_dir_no_setup_py(
     )
 
 
+@pytest.mark.skipif(
+    sys.version_info >= (3, 12),
+    reason="Setuptools<64 does not support Python 3.12+",
+)
 @pytest.mark.network
-def test_editable_install__local_dir_no_setup_py_with_pyproject(
+def test_editable_install_legacy__local_dir_no_setup_py_with_pyproject(
     script: PipTestEnvironment,
 ) -> None:
     """
-    Test installing in editable mode from a local directory with no setup.py
-    but that does have pyproject.toml with a build backend that does not support
-    the build_editable hook.
+    Test installing in legacy editable mode from a local directory with no
+    setup.py but that does have pyproject.toml with a build backend that does
+    not support the build_editable hook.
     """
     local_dir = script.scratch_path.joinpath("temp")
     local_dir.mkdir()
@@ -1383,8 +1387,14 @@ def test_install_editable_with_prefix_setup_py(script: PipTestEnvironment) -> No
     _test_install_editable_with_prefix(script, {"setup.py": setup_py})
 
 
+@pytest.mark.skipif(
+    sys.version_info >= (3, 12),
+    reason="Setuptools<64 does not support Python 3.12+",
+)
 @pytest.mark.network
-def test_install_editable_with_prefix_setup_cfg(script: PipTestEnvironment) -> None:
+def test_install_editable_legacy_with_prefix_setup_cfg(
+    script: PipTestEnvironment,
+) -> None:
     setup_cfg = """[metadata]
 name = pkga
 version = 0.1
diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py
--- a/tests/functional/test_uninstall.py
+++ b/tests/functional/test_uninstall.py
@@ -37,6 +37,10 @@ def test_basic_uninstall(script: PipTestEnvironment) -> None:
     assert_all_changes(result, result2, [script.venv / "build", "cache"])
 
 
+@pytest.mark.skipif(
+    sys.version_info >= (3, 12),
+    reason="distutils is no longer available in Python 3.12+",
+)
 def test_basic_uninstall_distutils(script: PipTestEnvironment) -> None:
     """
     Test basic install and uninstall.
@@ -68,6 +72,10 @@ def test_basic_uninstall_distutils(script: PipTestEnvironment) -> None:
     ) in result.stderr
 
 
+@pytest.mark.skipif(
+    sys.version_info >= (3, 12),
+    reason="Setuptools<64 does not support Python 3.12+",
+)
 @pytest.mark.network
 def test_basic_uninstall_with_scripts(script: PipTestEnvironment) -> None:
     """
@@ -101,6 +109,10 @@ def test_uninstall_invalid_parameter(
     assert expected_message in result.stderr
 
 
+@pytest.mark.skipif(
+    sys.version_info >= (3, 12),
+    reason="Setuptools<64 does not support Python 3.12+",
+)
 @pytest.mark.network
 def test_uninstall_easy_install_after_import(script: PipTestEnvironment) -> None:
     """
@@ -126,6 +138,10 @@ def test_uninstall_easy_install_after_import(script: PipTestEnvironment) -> None
     )
 
 
+@pytest.mark.skipif(
+    sys.version_info >= (3, 12),
+    reason="Setuptools<64 does not support Python 3.12+",
+)
 @pytest.mark.network
 def test_uninstall_trailing_newline(script: PipTestEnvironment) -> None:
     """
@@ -337,6 +353,10 @@ def test_uninstall_console_scripts_uppercase_name(script: PipTestEnvironment) ->
     assert not script_name.exists()
 
 
+@pytest.mark.skipif(
+    sys.version_info >= (3, 12),
+    reason="Setuptools<64 does not support Python 3.12+",
+)
 @pytest.mark.network
 def test_uninstall_easy_installed_console_scripts(script: PipTestEnvironment) -> None:
     """
diff --git a/tests/lib/venv.py b/tests/lib/venv.py
--- a/tests/lib/venv.py
+++ b/tests/lib/venv.py
@@ -124,7 +124,7 @@ def _create(self, clear: bool = False) -> None:
                 )
             elif self._venv_type == "venv":
                 builder = _venv.EnvBuilder()
-                context = builder.ensure_directories(self.location)
+                context = builder.ensure_directories(os.fspath(self.location))
                 builder.create_configuration(context)
                 builder.setup_python(context)
                 self.site.mkdir(parents=True, exist_ok=True)