Repository: pypa/setuptools

Issue:
[BUG] 61.0.0 breaks "setup.py install" by missing dependencies in install_requires
### setuptools version

61.0.0

### Python version

3.10

### OS

ubuntu-latest in GitHub Actions

### Additional environment information

_No response_

### Description

In our pywbem package, we test the different documented methods to install the package. One of them is still "setup.py install". I do understand that it is deprecated since setuptools v58.3.0, but that still means it should be expected to work.

The issue is that the "setup.py install" command successfully installs the pywbem package, but upon import it turns out that dependent packages were not installed, e.g. "six".

The GitHub Actions test run showing that is: https://github.com/pywbem/pywbem/runs/5687824533?check_suite_focus=true

"pip install" on the package works with setuptools 61.0.0 and does install the dependent packages.

The requirements.txt file of the pywbem project does specify the dependent packages and the setup.py script loads the dependencies from requirements.txt and specifies them in the "install_requires" parameter of setup().

The "setup.py install" approach worked before setuptools 61.0.0, e.g. with 60.10.0, see [this test run](https://github.com/pywbem/pywbem/runs/5626093559?check_suite_focus=true#step:22:2157).

### Expected behavior

61.0.0 should still support "setup.py install" as before.

### How to Reproduce

1. Create a new virtual env on Python 3.10
2. `pip install setuptools==61.0.0; pip uninstall six`
3. `git clone https://github.com/pywbem/pywbem.git; cd pywbem`
4. `./setup.py install`  # should succeed
5. `pip list`  # should display:
   ```
   Package    Version
   ---------- ----------
   pip        22.0.4
   pywbem     1.5.0.dev1
   setuptools 61.0.0
   wheel      0.37.1
   ```
   and the issue is that the dependent packages are not installed.
6. `pip install .`  # should succeed
7. `pip list`  # should display the correctly installed dependencies:
   ```
   Package            Version
   ------------------ ----------
   certifi            2021.10.8
   charset-normalizer 2.0.12
   idna               3.3
   nocasedict         1.0.2
   nocaselist         1.0.4
   pip                22.0.4
   ply                3.11
   pywbem             1.5.0.dev1
   PyYAML             6.0
   requests           2.27.1
   setuptools         61.0.0
   six                1.16.0
   urllib3            1.26.9
   wheel              0.37.1
   yamlloader         1.1.0
   ```

### Output

See above


Discussion:
Probably related to #3196
I had the same problem this morning since I upgraded to setuptools 61.0.0 (no dependencies installed).
The problem is clearly coming from the last release because if we do :
`pip install --upgrade setuptools==60.10.0`
everything works fine!
Hi @andy-maier, thank you very much for reporting that.
I checked pywbem against the fix proposed for 3196, and it seems that the issue might be different. I am still investigating the problem.

Resolving patch:
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -298,7 +298,9 @@ def finalize_options(self):  # noqa: C901  # is too complex (25)  # FIXME
 
         if not self.editable:
             self.check_site_dir()
-        self.index_url = self.index_url or "https://pypi.org/simple/"
+        default_index = os.getenv("__EASYINSTALL_INDEX", "https://pypi.org/simple/")
+        # ^ Private API for testing purposes only
+        self.index_url = self.index_url or default_index
         self.shadow_path = self.all_site_dirs[:]
         for path_item in self.install_dir, normalize_path(self.script_dir):
             if path_item not in self.shadow_path:
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -91,14 +91,21 @@ def _called_from_setup(run_frame):
                 msg = "For best results, pass -X:Frames to enable call stack."
                 warnings.warn(msg)
             return True
-        res = inspect.getouterframes(run_frame)[2]
-        caller, = res[:1]
-        info = inspect.getframeinfo(caller)
-        caller_module = caller.f_globals.get('__name__', '')
-        return (
-            caller_module == 'distutils.dist'
-            and info.function == 'run_commands'
-        )
+
+        frames = inspect.getouterframes(run_frame)
+        for frame in frames[2:4]:
+            caller, = frame[:1]
+            info = inspect.getframeinfo(caller)
+            caller_module = caller.f_globals.get('__name__', '')
+
+            if caller_module == "setuptools.dist" and info.function == "run_command":
+                # Starting from v61.0.0 setuptools overwrites dist.run_command
+                continue
+
+            return (
+                caller_module == 'distutils.dist'
+                and info.function == 'run_commands'
+            )
 
     def do_egg_install(self):
 


Test patch:
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -448,6 +448,63 @@ def test_bdist_egg_available_on_distutils_pkg(self, distutils_package):
         run_setup('setup.py', ['bdist_egg'])
 
 
+class TestInstallRequires:
+    def test_setup_install_includes_dependencies(self, tmp_path, mock_index):
+        """
+        When ``python setup.py install`` is called directly, it will use easy_install
+        to fetch dependencies.
+        """
+        # TODO: Remove these tests once `setup.py install` is completely removed
+        project_root = tmp_path / "project"
+        project_root.mkdir(exist_ok=True)
+        install_root = tmp_path / "install"
+        install_root.mkdir(exist_ok=True)
+
+        self.create_project(project_root)
+        cmd = [
+            sys.executable,
+            '-c', '__import__("setuptools").setup()',
+            'install',
+            '--install-base', str(install_root),
+            '--install-lib', str(install_root),
+            '--install-headers', str(install_root),
+            '--install-scripts', str(install_root),
+            '--install-data', str(install_root),
+            '--install-purelib', str(install_root),
+            '--install-platlib', str(install_root),
+        ]
+        env = {"PYTHONPATH": str(install_root), "__EASYINSTALL_INDEX": mock_index.url}
+        with pytest.raises(subprocess.CalledProcessError) as exc_info:
+            subprocess.check_output(
+                cmd, cwd=str(project_root), env=env, stderr=subprocess.STDOUT, text=True
+            )
+        try:
+            assert '/does-not-exist/' in {r.path for r in mock_index.requests}
+            assert next(
+                line
+                for line in exc_info.value.output.splitlines()
+                if "not find suitable distribution for" in line
+                and "does-not-exist" in line
+            )
+        except Exception:
+            if "failed to get random numbers" in exc_info.value.output:
+                pytest.xfail(f"{sys.platform} failure - {exc_info.value.output}")
+            raise
+
+    def create_project(self, root):
+        config = """
+        [metadata]
+        name = project
+        version = 42
+
+        [options]
+        install_requires = does-not-exist
+        py_modules = mod
+        """
+        (root / 'setup.cfg').write_text(DALS(config), encoding="utf-8")
+        (root / 'mod.py').touch()
+
+
 class TestSetupRequires:
 
     def test_setup_requires_honors_fetch_params(self, mock_index, monkeypatch):
@@ -466,7 +523,7 @@ def test_setup_requires_honors_fetch_params(self, mock_index, monkeypatch):
                     with contexts.environment(PYTHONPATH=temp_install_dir):
                         cmd = [
                             sys.executable,
-                            '-m', 'setup',
+                            '-c', '__import__("setuptools").setup()',
                             'easy_install',
                             '--index-url', mock_index.url,
                             '--exclude-scripts',