Repository: pypa/setuptools

Issue:
[BUG] `import setuptools` raises a DeprecationWarning on Windows
### setuptools version

setuptools==65.0.2

### Python version

Python 3.9

### OS

Windows

### Additional environment information

_No response_

### Description

On Windows, `import setuptools` raises a warning added in https://github.com/pypa/distutils/commit/c802880.

This issue does not occur in setuptools 65.0.1.

### Expected behavior

No warnings.

### How to Reproduce

```console
python -Werror -c "import setuptools"
```

### Output

```console
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\__init__.py", line 247, in <module>
    monkey.patch_all()
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\monkey.py", line 99, in patch_all
    patch_for_msvc_specialized_compiler()
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\monkey.py", line 136, in patch_for_msvc_specialized_compiler
    msvc = import_module('setuptools.msvc')
  File "C:\Program Files\Python39\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\msvc.py", line 61, in <module>
    from distutils.msvc9compiler import Reg
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\_distutils\msvc9compiler.py", line 34, in <module>
    warnings.warn(
DeprecationWarning: msvc9compiler is deprecated and slated to be removed in the future. Please discontinue use or file an issue with pypa/distutils describing your use case.
```
[BUG] `import setuptools` raises a DeprecationWarning on Windows
### setuptools version

setuptools==65.0.2

### Python version

Python 3.9

### OS

Windows

### Additional environment information

_No response_

### Description

On Windows, `import setuptools` raises a warning added in https://github.com/pypa/distutils/commit/c802880.

This issue does not occur in setuptools 65.0.1.

### Expected behavior

No warnings.

### How to Reproduce

```console
python -Werror -c "import setuptools"
```

### Output

```console
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\__init__.py", line 247, in <module>
    monkey.patch_all()
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\monkey.py", line 99, in patch_all
    patch_for_msvc_specialized_compiler()
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\monkey.py", line 136, in patch_for_msvc_specialized_compiler
    msvc = import_module('setuptools.msvc')
  File "C:\Program Files\Python39\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\msvc.py", line 61, in <module>
    from distutils.msvc9compiler import Reg
  File "C:\Users\maehashi\.venv-py39\lib\site-packages\setuptools\_distutils\msvc9compiler.py", line 34, in <module>
    warnings.warn(
DeprecationWarning: msvc9compiler is deprecated and slated to be removed in the future. Please discontinue use or file an issue with pypa/distutils describing your use case.
```


Discussion:
Is it possible to have the setuptools tests fail if setuptools itself produces warnings? And to run the integration tests on Windows too?

It would be nice if the CI tests could help avoid the excitement that we're experiencing in future.
Setuptools does fail on warnings, but intentionally suppressed this DeprecationWarning because it gets triggered when those modules get tested. I did not realize that Setuptools actually imported this module. That's slightly surprising given that I removed the module and the tests continued to pass. But now that I look at it, it's not all that surprising with all the `except ImportError` wrapped around it.

The choice to deprecate these modules came a little rushed when backing out the removal of those modules.

I definitely don't want these deprecation warnings to emit when using Setuptools.
Is it possible to have the setuptools tests fail if setuptools itself produces warnings? And to run the integration tests on Windows too?

It would be nice if the CI tests could help avoid the excitement that we're experiencing in future.
Setuptools does fail on warnings, but intentionally suppressed this DeprecationWarning because it gets triggered when those modules get tested. I did not realize that Setuptools actually imported this module. That's slightly surprising given that I removed the module and the tests continued to pass. But now that I look at it, it's not all that surprising with all the `except ImportError` wrapped around it.

The choice to deprecate these modules came a little rushed when backing out the removal of those modules.

I definitely don't want these deprecation warnings to emit when using Setuptools.

Resolving patch:
diff --git a/setuptools/monkey.py b/setuptools/monkey.py
--- a/setuptools/monkey.py
+++ b/setuptools/monkey.py
@@ -143,7 +143,7 @@ def patch_params(mod_name, func_name):
         """
         Prepare the parameters for patch_func to patch indicated function.
         """
-        repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_'
+        repl_prefix = 'msvc14_'
         repl_name = repl_prefix + func_name.lstrip('_')
         repl = getattr(msvc, repl_name)
         mod = import_module(mod_name)
@@ -151,19 +151,9 @@ def patch_params(mod_name, func_name):
             raise ImportError(func_name)
         return repl, mod, func_name
 
-    # Python 2.7 to 3.4
-    msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler')
-
     # Python 3.5+
     msvc14 = functools.partial(patch_params, 'distutils._msvccompiler')
 
-    try:
-        # Patch distutils.msvc9compiler
-        patch_func(*msvc9('find_vcvarsall'))
-        patch_func(*msvc9('query_vcvarsall'))
-    except ImportError:
-        pass
-
     try:
         # Patch distutils._msvccompiler._get_vc_env
         patch_func(*msvc14('_get_vc_env'))
diff --git a/setuptools/msvc.py b/setuptools/msvc.py
--- a/setuptools/msvc.py
+++ b/setuptools/msvc.py
@@ -3,14 +3,6 @@
 
 Known supported compilers:
 --------------------------
-Microsoft Visual C++ 9.0:
-    Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
-    Microsoft Windows SDK 6.1 (x86, x64, ia64)
-    Microsoft Windows SDK 7.0 (x86, x64, ia64)
-
-Microsoft Visual C++ 10.0:
-    Microsoft Windows SDK 7.1 (x86, x64, ia64)
-
 Microsoft Visual C++ 14.X:
     Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
     Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
@@ -48,100 +40,6 @@ class winreg:
 
     environ = dict()
 
-_msvc9_suppress_errors = (
-    # msvc9compiler isn't available on some platforms
-    ImportError,
-
-    # msvc9compiler raises DistutilsPlatformError in some
-    # environments. See #1118.
-    distutils.errors.DistutilsPlatformError,
-)
-
-try:
-    from distutils.msvc9compiler import Reg
-except _msvc9_suppress_errors:
-    pass
-
-
-def msvc9_find_vcvarsall(version):
-    """
-    Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone
-    compiler build for Python
-    (VCForPython / Microsoft Visual C++ Compiler for Python 2.7).
-
-    Fall back to original behavior when the standalone compiler is not
-    available.
-
-    Redirect the path of "vcvarsall.bat".
-
-    Parameters
-    ----------
-    version: float
-        Required Microsoft Visual C++ version.
-
-    Return
-    ------
-    str
-        vcvarsall.bat path
-    """
-    vc_base = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
-    key = vc_base % ('', version)
-    try:
-        # Per-user installs register the compiler path here
-        productdir = Reg.get_value(key, "installdir")
-    except KeyError:
-        try:
-            # All-user installs on a 64-bit system register here
-            key = vc_base % ('Wow6432Node\\', version)
-            productdir = Reg.get_value(key, "installdir")
-        except KeyError:
-            productdir = None
-
-    if productdir:
-        vcvarsall = join(productdir, "vcvarsall.bat")
-        if isfile(vcvarsall):
-            return vcvarsall
-
-    return get_unpatched(msvc9_find_vcvarsall)(version)
-
-
-def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
-    """
-    Patched "distutils.msvc9compiler.query_vcvarsall" for support extra
-    Microsoft Visual C++ 9.0 and 10.0 compilers.
-
-    Set environment without use of "vcvarsall.bat".
-
-    Parameters
-    ----------
-    ver: float
-        Required Microsoft Visual C++ version.
-    arch: str
-        Target architecture.
-
-    Return
-    ------
-    dict
-        environment
-    """
-    # Try to get environment from vcvarsall.bat (Classical way)
-    try:
-        orig = get_unpatched(msvc9_query_vcvarsall)
-        return orig(ver, arch, *args, **kwargs)
-    except distutils.errors.DistutilsPlatformError:
-        # Pass error if Vcvarsall.bat is missing
-        pass
-    except ValueError:
-        # Pass error if environment not set after executing vcvarsall.bat
-        pass
-
-    # If error, try to set environment directly
-    try:
-        return EnvironmentInfo(arch, ver).return_env()
-    except distutils.errors.DistutilsPlatformError as exc:
-        _augment_exception(exc, ver, arch)
-        raise
-
 
 def _msvc14_find_vc2015():
     """Python 3.8 "distutils/_msvccompiler.py" backport"""


Test patch:
diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py
deleted file mode 100644
--- a/setuptools/tests/test_msvc.py
+++ /dev/null
@@ -1,179 +0,0 @@
-"""
-Tests for msvc support module.
-"""
-
-import os
-import contextlib
-import distutils.errors
-import mock
-
-import pytest
-
-from . import contexts
-
-# importing only setuptools should apply the patch
-__import__('setuptools')
-
-pytest.importorskip("distutils.msvc9compiler")
-
-
-def mock_reg(hkcu=None, hklm=None):
-    """
-    Return a mock for distutils.msvc9compiler.Reg, patched
-    to mock out the functions that access the registry.
-    """
-
-    _winreg = getattr(distutils.msvc9compiler, '_winreg', None)
-    winreg = getattr(distutils.msvc9compiler, 'winreg', _winreg)
-
-    hives = {
-        winreg.HKEY_CURRENT_USER: hkcu or {},
-        winreg.HKEY_LOCAL_MACHINE: hklm or {},
-    }
-
-    @classmethod
-    def read_keys(cls, base, key):
-        """Return list of registry keys."""
-        hive = hives.get(base, {})
-        return [
-            k.rpartition('\\')[2]
-            for k in hive if k.startswith(key.lower())
-        ]
-
-    @classmethod
-    def read_values(cls, base, key):
-        """Return dict of registry keys and values."""
-        hive = hives.get(base, {})
-        return dict(
-            (k.rpartition('\\')[2], hive[k])
-            for k in hive if k.startswith(key.lower())
-        )
-
-    return mock.patch.multiple(
-        distutils.msvc9compiler.Reg,
-        read_keys=read_keys, read_values=read_values)
-
-
-class TestModulePatch:
-    """
-    Ensure that importing setuptools is sufficient to replace
-    the standard find_vcvarsall function with a version that
-    recognizes the "Visual C++ for Python" package.
-    """
-
-    key_32 = r'software\microsoft\devdiv\vcforpython\9.0\installdir'
-    key_64 = key_32.replace(r'\microsoft', r'\wow6432node\microsoft')
-
-    def test_patched(self):
-        "Test the module is actually patched"
-        mod_name = distutils.msvc9compiler.find_vcvarsall.__module__
-        assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched"
-
-    def test_no_registry_entries_means_nothing_found(self):
-        """
-        No registry entries or environment variable should lead to an error
-        directing the user to download vcpython27.
-        """
-        find_vcvarsall = distutils.msvc9compiler.find_vcvarsall
-        query_vcvarsall = distutils.msvc9compiler.query_vcvarsall
-
-        with contexts.environment(VS90COMNTOOLS=None):
-            with mock_reg():
-                assert find_vcvarsall(9.0) is None
-
-                try:
-                    query_vcvarsall(9.0)
-                except Exception as exc:
-                    expected = distutils.errors.DistutilsPlatformError
-                    assert isinstance(exc, expected)
-                    assert 'aka.ms/vcpython27' in str(exc)
-
-    @pytest.fixture
-    def user_preferred_setting(self):
-        """
-        Set up environment with different install dirs for user vs. system
-        and yield the user_install_dir for the expected result.
-        """
-        with self.mock_install_dir() as user_install_dir:
-            with self.mock_install_dir() as system_install_dir:
-                reg = mock_reg(
-                    hkcu={
-                        self.key_32: user_install_dir,
-                    },
-                    hklm={
-                        self.key_32: system_install_dir,
-                        self.key_64: system_install_dir,
-                    },
-                )
-                with reg:
-                    yield user_install_dir
-
-    def test_prefer_current_user(self, user_preferred_setting):
-        """
-        Ensure user's settings are preferred.
-        """
-        result = distutils.msvc9compiler.find_vcvarsall(9.0)
-        expected = os.path.join(user_preferred_setting, 'vcvarsall.bat')
-        assert expected == result
-
-    @pytest.fixture
-    def local_machine_setting(self):
-        """
-        Set up environment with only the system environment configured.
-        """
-        with self.mock_install_dir() as system_install_dir:
-            reg = mock_reg(
-                hklm={
-                    self.key_32: system_install_dir,
-                },
-            )
-            with reg:
-                yield system_install_dir
-
-    def test_local_machine_recognized(self, local_machine_setting):
-        """
-        Ensure machine setting is honored if user settings are not present.
-        """
-        result = distutils.msvc9compiler.find_vcvarsall(9.0)
-        expected = os.path.join(local_machine_setting, 'vcvarsall.bat')
-        assert expected == result
-
-    @pytest.fixture
-    def x64_preferred_setting(self):
-        """
-        Set up environment with 64-bit and 32-bit system settings configured
-        and yield the canonical location.
-        """
-        with self.mock_install_dir() as x32_dir:
-            with self.mock_install_dir() as x64_dir:
-                reg = mock_reg(
-                    hklm={
-                        # This *should* only exist on 32-bit machines
-                        self.key_32: x32_dir,
-                        # This *should* only exist on 64-bit machines
-                        self.key_64: x64_dir,
-                    },
-                )
-                with reg:
-                    yield x32_dir
-
-    def test_ensure_64_bit_preferred(self, x64_preferred_setting):
-        """
-        Ensure 64-bit system key is preferred.
-        """
-        result = distutils.msvc9compiler.find_vcvarsall(9.0)
-        expected = os.path.join(x64_preferred_setting, 'vcvarsall.bat')
-        assert expected == result
-
-    @staticmethod
-    @contextlib.contextmanager
-    def mock_install_dir():
-        """
-        Make a mock install dir in a unique location so that tests can
-        distinguish which dir was detected in a given scenario.
-        """
-        with contexts.tempdir() as result:
-            vcvarsall = os.path.join(result, 'vcvarsall.bat')
-            with open(vcvarsall, 'w'):
-                pass
-            yield result