Imported Upstream version 52.0.0 upstream/52.0.0
authorJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:31 +0000 (17:02 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:31 +0000 (17:02 +0900)
16 files changed:
.bumpversion.cfg
CHANGES.rst
easy_install.py [deleted file]
pyproject.toml
setup.cfg
setup.py
setuptools/command/bdist_egg.py
setuptools/command/easy_install.py
setuptools/installer.py
setuptools/tests/fixtures.py
setuptools/tests/test_bdist_egg.py
setuptools/tests/test_build_meta.py
setuptools/tests/test_distutils_adoption.py
setuptools/tests/test_easy_install.py
setuptools/tests/test_namespaces.py
setuptools/tests/test_virtualenv.py

index debcfeeb6426784f6ff5bbe3f58c27540f2199bb..c3bdafab955c35d612fd13fff3b51ddbbd3b141d 100644 (file)
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 51.3.3
+current_version = 52.0.0
 commit = True
 tag = True
 
index c094960f9aaf0e42ec9e25af6c87e2d633cf9658..79941d8efe989aa4ddfaec0aef0195e6948641a4 100644 (file)
@@ -1,3 +1,18 @@
+v52.0.0
+-------
+
+
+Breaking Changes
+^^^^^^^^^^^^^^^^
+* #2537: Remove fallback support for fetch_build_eggs using easy_install. Now pip is required for setup_requires to succeed.
+* #2544: Removed 'easy_install' top-level model (runpy entry point) and 'easy_install' console script.
+* #2545: Removed support for eggsecutables.
+
+Changes
+^^^^^^^
+* #2459: Tests now run in parallel via pytest-xdist, completing in about half the time. Special thanks to :user:`webknjaz` for hard work implementing test isolation. To run without parallelization, disable the plugin with ``tox -- -p no:xdist``.
+
+
 v51.3.3
 -------
 
diff --git a/easy_install.py b/easy_install.py
deleted file mode 100644 (file)
index d87e984..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-"""Run the EasyInstall command"""
-
-if __name__ == '__main__':
-    from setuptools.command.easy_install import main
-    main()
index 0bc2a46f4fbac51eb2e884866fc7b7109bfbf813..4e80bdc1a7e4deaeb7c3652a64b8dfda7894ad5e 100644 (file)
@@ -24,6 +24,9 @@ addopts = "--flake8"
 [pytest.enabler.cov]
 addopts = "--cov"
 
+[pytest.enabler.xdist]
+addopts = "-n auto"
+
 [tool.towncrier]
     package = "setuptools"
     package_dir = "setuptools"
index 536ec70fabea2b4e3d368e9336198d2e4da71abd..e0c4edc2e6adbe89dbd008a6ceb6a2cbbeb7306d 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,7 +2,7 @@
 license_files =
     LICENSE
 name = setuptools
-version = 51.3.3
+version = 52.0.0
 author = Python Packaging Authority
 author_email = distutils-sig@python.org
 description = Easily download, build, install, upgrade, and uninstall Python packages
@@ -24,7 +24,6 @@ project_urls =
 
 [options]
 packages = find_namespace:
-py_modules = easy_install
 # disabled as it causes tests to be included #2505
 # include_package_data = true
 python_requires = >=3.6
@@ -47,7 +46,7 @@ testing =
        pytest-black >= 0.3.7; python_implementation != "PyPy"
        pytest-cov
        pytest-mypy; python_implementation != "PyPy"
-       pytest-enabler
+       pytest-enabler >= 1.0.1
 
        # local
     mock
@@ -58,6 +57,7 @@ testing =
     paver
     pip>=19.1 # For proper file:// URLs support.
     jaraco.envs
+    pytest-xdist
 
 docs =
     # Keep these in sync with docs/requirements.txt
index 28d3dada34f10b47b6f851900a704f9fbca89e20..31eda0fbb06cfc78e447b8443f35fb699f1ec2fc 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -30,22 +30,6 @@ def read_commands():
     return command_ns['__all__']
 
 
-def _gen_console_scripts():
-    yield "easy_install = setuptools.command.easy_install:main"
-
-    # Gentoo distributions manage the python-version-specific scripts
-    # themselves, so those platforms define an environment variable to
-    # suppress the creation of the version-specific scripts.
-    var_names = (
-        'SETUPTOOLS_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
-        'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
-    )
-    if any(os.environ.get(var) not in (None, "", "0") for var in var_names):
-        return
-    tmpl = "easy_install-{shortver} = setuptools.command.easy_install:main"
-    yield tmpl.format(shortver='{}.{}'.format(*sys.version_info))
-
-
 package_data = dict(
     setuptools=['script (dev).tmpl', 'script.tmpl', 'site-patch.py'],
 )
@@ -170,9 +154,6 @@ setup_params = dict(
             "depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
             "dependency_links.txt = setuptools.command.egg_info:overwrite_arg",
         ],
-        "console_scripts": list(_gen_console_scripts()),
-        "setuptools.installation":
-            ['eggsecutable = setuptools.command.easy_install:bootstrap'],
     },
     dependency_links=[
         pypi_link(
index 206f2419ba1f3c96d8329378fe623058d7d4e11f..e6b1609f7babcd4439376c0d826978f7a66dfa3f 100644 (file)
@@ -2,7 +2,6 @@
 
 Build .egg distributions"""
 
-from distutils.errors import DistutilsSetupError
 from distutils.dir_util import remove_tree, mkpath
 from distutils import log
 from types import CodeType
@@ -11,12 +10,10 @@ import os
 import re
 import textwrap
 import marshal
-import warnings
 
 from pkg_resources import get_build_platform, Distribution, ensure_directory
-from pkg_resources import EntryPoint
 from setuptools.extension import Library
-from setuptools import Command, SetuptoolsDeprecationWarning
+from setuptools import Command
 
 from sysconfig import get_path, get_python_version
 
@@ -268,49 +265,7 @@ class bdist_egg(Command):
         return analyze_egg(self.bdist_dir, self.stubs)
 
     def gen_header(self):
-        epm = EntryPoint.parse_map(self.distribution.entry_points or '')
-        ep = epm.get('setuptools.installation', {}).get('eggsecutable')
-        if ep is None:
-            return 'w'  # not an eggsecutable, do it the usual way.
-
-        warnings.warn(
-            "Eggsecutables are deprecated and will be removed in a future "
-            "version.",
-            SetuptoolsDeprecationWarning
-        )
-
-        if not ep.attrs or ep.extras:
-            raise DistutilsSetupError(
-                "eggsecutable entry point (%r) cannot have 'extras' "
-                "or refer to a module" % (ep,)
-            )
-
-        pyver = '{}.{}'.format(*sys.version_info)
-        pkg = ep.module_name
-        full = '.'.join(ep.attrs)
-        base = ep.attrs[0]
-        basename = os.path.basename(self.egg_output)
-
-        header = (
-            "#!/bin/sh\n"
-            'if [ `basename $0` = "%(basename)s" ]\n'
-            'then exec python%(pyver)s -c "'
-            "import sys, os; sys.path.insert(0, os.path.abspath('$0')); "
-            "from %(pkg)s import %(base)s; sys.exit(%(full)s())"
-            '" "$@"\n'
-            'else\n'
-            '  echo $0 is not the correct name for this egg file.\n'
-            '  echo Please rename it back to %(basename)s and try again.\n'
-            '  exec false\n'
-            'fi\n'
-        ) % locals()
-
-        if not self.dry_run:
-            mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run)
-            f = open(self.egg_output, 'w')
-            f.write(header)
-            f.close()
-        return 'a'
+        return 'w'
 
     def copy_metadata_to(self, target_dir):
         "Copy metadata (egg info) to the target_dir"
index f1e487d4d21e9e706b3d97d77fcbf09207af2ed7..eeb21b5083a636f36484c801590d5dc9244ea73c 100644 (file)
@@ -67,7 +67,7 @@ warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)
 
 __all__ = [
     'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
-    'main', 'get_exe_prefixes',
+    'get_exe_prefixes',
 ]
 
 
@@ -2284,60 +2284,6 @@ def current_umask():
     return tmp
 
 
-def bootstrap():
-    # This function is called when setuptools*.egg is run using /bin/sh
-    import setuptools
-
-    argv0 = os.path.dirname(setuptools.__path__[0])
-    sys.argv[0] = argv0
-    sys.argv.append(argv0)
-    main()
-
-
-def main(argv=None, **kw):
-    from setuptools import setup
-    from setuptools.dist import Distribution
-
-    class DistributionWithoutHelpCommands(Distribution):
-        common_usage = ""
-
-        def _show_help(self, *args, **kw):
-            with _patch_usage():
-                Distribution._show_help(self, *args, **kw)
-
-    if argv is None:
-        argv = sys.argv[1:]
-
-    with _patch_usage():
-        setup(
-            script_args=['-q', 'easy_install', '-v'] + argv,
-            script_name=sys.argv[0] or 'easy_install',
-            distclass=DistributionWithoutHelpCommands,
-            **kw
-        )
-
-
-@contextlib.contextmanager
-def _patch_usage():
-    import distutils.core
-    USAGE = textwrap.dedent("""
-        usage: %(script)s [options] requirement_or_url ...
-           or: %(script)s --help
-        """).lstrip()
-
-    def gen_usage(script_name):
-        return USAGE % dict(
-            script=os.path.basename(script_name),
-        )
-
-    saved = distutils.core.gen_usage
-    distutils.core.gen_usage = gen_usage
-    try:
-        yield
-    finally:
-        distutils.core.gen_usage = saved
-
-
 class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning):
     """
     Warning for EasyInstall deprecations, bypassing suppression.
index c5822a31f4d0bba506dc5df46dd787341fea3577..57e2b587aae05167540abdd2b53c7b5bcac298f0 100644 (file)
@@ -7,7 +7,6 @@ from distutils import log
 from distutils.errors import DistutilsError
 
 import pkg_resources
-from setuptools.command.easy_install import easy_install
 from setuptools.wheel import Wheel
 
 
@@ -19,54 +18,11 @@ def _fixup_find_links(find_links):
     return find_links
 
 
-def _legacy_fetch_build_egg(dist, req):
-    """Fetch an egg needed for building.
-
-    Legacy path using EasyInstall.
-    """
-    tmp_dist = dist.__class__({'script_args': ['easy_install']})
-    opts = tmp_dist.get_option_dict('easy_install')
-    opts.clear()
-    opts.update(
-        (k, v)
-        for k, v in dist.get_option_dict('easy_install').items()
-        if k in (
-            # don't use any other settings
-            'find_links', 'site_dirs', 'index_url',
-            'optimize', 'site_dirs', 'allow_hosts',
-        ))
-    if dist.dependency_links:
-        links = dist.dependency_links[:]
-        if 'find_links' in opts:
-            links = _fixup_find_links(opts['find_links'][1]) + links
-        opts['find_links'] = ('setup', links)
-    install_dir = dist.get_egg_cache_dir()
-    cmd = easy_install(
-        tmp_dist, args=["x"], install_dir=install_dir,
-        exclude_scripts=True,
-        always_copy=False, build_directory=None, editable=False,
-        upgrade=False, multi_version=True, no_report=True, user=False
-    )
-    cmd.ensure_finalized()
-    return cmd.easy_install(req)
-
-
 def fetch_build_egg(dist, req):  # noqa: C901  # is too complex (16)  # FIXME
     """Fetch an egg needed for building.
 
     Use pip/wheel to fetch/build a wheel."""
-    # Check pip is available.
-    try:
-        pkg_resources.get_distribution('pip')
-    except pkg_resources.DistributionNotFound:
-        dist.announce(
-            'WARNING: The pip package is not available, falling back '
-            'to EasyInstall for handling setup_requires/test_requires; '
-            'this is deprecated and will be removed in a future version.',
-            log.WARN
-        )
-        return _legacy_fetch_build_egg(dist, req)
-    # Warn if wheel is not.
+    # Warn if wheel is not available
     try:
         pkg_resources.get_distribution('wheel')
     except pkg_resources.DistributionNotFound:
index e8cb7f523762ce9f1f65491403a9cde99ae0830f..d74b5f031ac6285dca898d825febecd694825fda 100644 (file)
@@ -1,3 +1,7 @@
+import contextlib
+import sys
+import shutil
+
 import pytest
 
 from . import contexts
@@ -21,3 +25,36 @@ def user_override(monkeypatch):
 def tmpdir_cwd(tmpdir):
     with tmpdir.as_cwd() as orig:
         yield orig
+
+
+@pytest.fixture
+def tmp_src(request, tmp_path):
+    """Make a copy of the source dir under `$tmp/src`.
+
+    This fixture is useful whenever it's necessary to run `setup.py`
+    or `pip install` against the source directory when there's no
+    control over the number of simultaneous invocations. Such
+    concurrent runs create and delete directories with the same names
+    under the target directory and so they influence each other's runs
+    when they are not being executed sequentially.
+    """
+    tmp_src_path = tmp_path / 'src'
+    shutil.copytree(request.config.rootdir, tmp_src_path)
+    return tmp_src_path
+
+
+@pytest.fixture(autouse=True, scope="session")
+def workaround_xdist_376(request):
+    """
+    Workaround pytest-dev/pytest-xdist#376
+
+    ``pytest-xdist`` tends to inject '' into ``sys.path``,
+    which may break certain isolation expectations.
+    Remove the entry so the import
+    machinery behaves the same irrespective of xdist.
+    """
+    if not request.config.pluginmanager.has_plugin('xdist'):
+        return
+
+    with contextlib.suppress(ValueError):
+        sys.path.remove('')
index 8760ea304c46489c4483e1b2166b33cd59f6b799..fb5b90b1a3846aa90ff9a11e5feb8b817faba5b8 100644 (file)
@@ -7,7 +7,6 @@ import zipfile
 import pytest
 
 from setuptools.dist import Distribution
-from setuptools import SetuptoolsDeprecationWarning
 
 from . import contexts
 
@@ -65,17 +64,3 @@ class Test:
         names = list(zi.filename for zi in zip.filelist)
         assert 'hi.pyc' in names
         assert 'hi.py' not in names
-
-    def test_eggsecutable_warning(self, setup_context, user_override):
-        dist = Distribution(dict(
-            script_name='setup.py',
-            script_args=['bdist_egg'],
-            name='foo',
-            py_modules=['hi'],
-            entry_points={
-                'setuptools.installation':
-                    ['eggsecutable = my_package.some_module:main_func']},
-        ))
-        dist.parse_command_line()
-        with pytest.warns(SetuptoolsDeprecationWarning):
-            dist.run_commands()
index 6d3a997ee062d0a5700051d60d6180d7caa0e4fe..e117d8e6295c675e77df8a5c7b82727f6e4b5d8a 100644 (file)
@@ -11,7 +11,7 @@ from .textwrap import DALS
 
 
 class BuildBackendBase:
-    def __init__(self, cwd=None, env={}, backend_name='setuptools.build_meta'):
+    def __init__(self, cwd='.', env={}, backend_name='setuptools.build_meta'):
         self.cwd = cwd
         self.env = env
         self.backend_name = backend_name
@@ -126,7 +126,7 @@ class TestBuildMetaBackend:
     backend_name = 'setuptools.build_meta'
 
     def get_build_backend(self):
-        return BuildBackend(cwd='.', backend_name=self.backend_name)
+        return BuildBackend(backend_name=self.backend_name)
 
     @pytest.fixture(params=defns)
     def build_backend(self, tmpdir, request):
@@ -337,7 +337,7 @@ class TestBuildMetaBackend:
     def test_build_sdist_relative_path_import(self, tmpdir_cwd):
         build_files(self._relative_path_import_files)
         build_backend = self.get_build_backend()
-        with pytest.raises(ImportError):
+        with pytest.raises(ImportError, match="^No module named 'hello'$"):
             build_backend.build_sdist("temp")
 
     @pytest.mark.parametrize('setup_literal, requirements', [
index a53773df8cb36ced584f116f20f95f20c7305c04..0e89921c90439ee429caaeead65a36551ae4b496 100644 (file)
@@ -21,10 +21,10 @@ class VirtualEnv(jaraco.envs.VirtualEnv):
 
 
 @pytest.fixture
-def venv(tmpdir):
+def venv(tmp_path, tmp_src):
     env = VirtualEnv()
-    env.root = path.Path(tmpdir)
-    env.req = os.getcwd()
+    env.root = path.Path(tmp_path / 'venv')
+    env.req = str(tmp_src)
     return env.create()
 
 
index 6aa17e9414192fbd36cdb8df8ef4ef6fb372d617..66598066d74a676744be2c315946d999011d8551 100644 (file)
@@ -15,6 +15,7 @@ import zipfile
 import mock
 import time
 import re
+import subprocess
 
 import pytest
 
@@ -25,7 +26,6 @@ from setuptools.command.easy_install import (
     EasyInstallDeprecationWarning, ScriptWriter, PthDistributions,
     WindowsScriptWriter,
 )
-from setuptools.command import easy_install as easy_install_pkg
 from setuptools.dist import Distribution
 from pkg_resources import normalize_path, working_set
 from pkg_resources import Distribution as PRDistribution
@@ -461,17 +461,16 @@ class TestSetupRequires:
             with TestSetupRequires.create_sdist() as dist_file:
                 with contexts.tempdir() as temp_install_dir:
                     with contexts.environment(PYTHONPATH=temp_install_dir):
-                        ei_params = [
+                        cmd = [
+                            sys.executable,
+                            '-m', 'setup',
+                            'easy_install',
                             '--index-url', mock_index.url,
                             '--exclude-scripts',
                             '--install-dir', temp_install_dir,
                             dist_file,
                         ]
-                        with sandbox.save_argv(['easy_install']):
-                            # attempt to install the dist. It should
-                            # fail because it doesn't exist.
-                            with pytest.raises(SystemExit):
-                                easy_install_pkg.main(ei_params)
+                        subprocess.Popen(cmd).wait()
         # there should have been one requests to the server
         assert [r.path for r in mock_index.requests] == ['/does-not-exist/']
 
@@ -742,10 +741,10 @@ class TestSetupRequires:
         assert eggs == ['dep 1.0']
 
     @pytest.mark.parametrize(
-        'use_legacy_installer,with_dependency_links_in_setup_py',
-        itertools.product((False, True), (False, True)))
+        'with_dependency_links_in_setup_py',
+        (False, True))
     def test_setup_requires_with_find_links_in_setup_cfg(
-            self, monkeypatch, use_legacy_installer,
+            self, monkeypatch,
             with_dependency_links_in_setup_py):
         monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
         monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
@@ -767,11 +766,9 @@ class TestSetupRequires:
                     fp.write(DALS(
                         '''
                         from setuptools import installer, setup
-                        if {use_legacy_installer}:
-                            installer.fetch_build_egg = installer._legacy_fetch_build_egg
                         setup(setup_requires='python-xlib==42',
                         dependency_links={dependency_links!r})
-                        ''').format(use_legacy_installer=use_legacy_installer,  # noqa
+                        ''').format(
                                     dependency_links=dependency_links))
                 with open(test_setup_cfg, 'w') as fp:
                     fp.write(DALS(
index 6c8c522dc07e916824e9d6df633a2c124743fd82..270f90c98b0b2a992661d2677542b4689b8cdb92 100644 (file)
@@ -62,8 +62,9 @@ class TestNamespaces:
         target.mkdir()
         install_cmd = [
             sys.executable,
-            '-m', 'easy_install',
-            '-d', str(target),
+            '-m', 'pip',
+            'install',
+            '-t', str(target),
             str(pkg),
         ]
         with test.test.paths_on_pythonpath([str(target)]):
index 5a942d84c5438039635e79d69786e58e054734d3..fcd5da5d45f4331a32afd020386e1ffe1e729405 100644 (file)
@@ -40,14 +40,11 @@ def bare_virtualenv():
         yield venv
 
 
-SOURCE_DIR = os.path.join(os.path.dirname(__file__), '../..')
-
-
-def test_clean_env_install(bare_virtualenv):
+def test_clean_env_install(bare_virtualenv, tmp_src):
     """
     Check setuptools can be installed in a clean environment.
     """
-    bare_virtualenv.run(['python', 'setup.py', 'install'], cd=SOURCE_DIR)
+    bare_virtualenv.run(['python', 'setup.py', 'install'], cd=tmp_src)
 
 
 def _get_pip_versions():
@@ -85,7 +82,7 @@ def _get_pip_versions():
 
 
 @pytest.mark.parametrize('pip_version', _get_pip_versions())
-def test_pip_upgrade_from_source(pip_version, virtualenv):
+def test_pip_upgrade_from_source(pip_version, tmp_src, virtualenv):
     """
     Check pip can upgrade setuptools from source.
     """
@@ -104,7 +101,7 @@ def test_pip_upgrade_from_source(pip_version, virtualenv):
     virtualenv.run(' && '.join((
         'python setup.py -q sdist -d {dist}',
         'python setup.py -q bdist_wheel -d {dist}',
-    )).format(dist=dist_dir), cd=SOURCE_DIR)
+    )).format(dist=dist_dir), cd=tmp_src)
     sdist = glob.glob(os.path.join(dist_dir, '*.zip'))[0]
     wheel = glob.glob(os.path.join(dist_dir, '*.whl'))[0]
     # Then update from wheel.
@@ -113,12 +110,12 @@ def test_pip_upgrade_from_source(pip_version, virtualenv):
     virtualenv.run('pip install --no-cache-dir --upgrade ' + sdist)
 
 
-def _check_test_command_install_requirements(virtualenv, tmpdir):
+def _check_test_command_install_requirements(virtualenv, tmpdir, cwd):
     """
     Check the test command will install all required dependencies.
     """
     # Install setuptools.
-    virtualenv.run('python setup.py develop', cd=SOURCE_DIR)
+    virtualenv.run('python setup.py develop', cd=cwd)
 
     def sdist(distname, version):
         dist_path = tmpdir.join('%s-%s.tar.gz' % (distname, version))
@@ -175,7 +172,7 @@ def _check_test_command_install_requirements(virtualenv, tmpdir):
     assert tmpdir.join('success').check()
 
 
-def test_test_command_install_requirements(virtualenv, tmpdir):
+def test_test_command_install_requirements(virtualenv, tmpdir, request):
     # Ensure pip/wheel packages are installed.
     virtualenv.run(
         "python -c \"__import__('pkg_resources').require(['pip', 'wheel'])\"")
@@ -183,18 +180,13 @@ def test_test_command_install_requirements(virtualenv, tmpdir):
     virtualenv.run("python -m pip uninstall -y setuptools")
     # disable index URL so bits and bobs aren't requested from PyPI
     virtualenv.env['PIP_NO_INDEX'] = '1'
-    _check_test_command_install_requirements(virtualenv, tmpdir)
-
-
-def test_test_command_install_requirements_when_using_easy_install(
-        bare_virtualenv, tmpdir):
-    _check_test_command_install_requirements(bare_virtualenv, tmpdir)
+    _check_test_command_install_requirements(virtualenv, tmpdir, request.config.rootdir)
 
 
-def test_no_missing_dependencies(bare_virtualenv):
+def test_no_missing_dependencies(bare_virtualenv, request):
     """
     Quick and dirty test to ensure all external dependencies are vendored.
     """
     for command in ('upload',):  # sorted(distutils.command.__all__):
-        bare_virtualenv.run(
-            ['python', 'setup.py', command, '-h'], cd=SOURCE_DIR)
+        cmd = ['python', 'setup.py', command, '-h']
+        bare_virtualenv.run(cmd, cd=request.config.rootdir)