Skip to content

Commit

Permalink
gh-95299: Stop installing setuptools as a part of ensurepip and venv (#…
Browse files Browse the repository at this point in the history
…101039)

Remove the bundled setuptools wheel from ensurepip, and stop installing setuptools in environments created by venv.

Co-Authored-by: Hugo van Kemenade <[email protected]>
Co-authored-by: C.A.M. Gerlach <[email protected]>
Co-authored-by: Oleg Iarygin <[email protected]>
  • Loading branch information
4 people authored Apr 18, 2023
1 parent f39e00f commit ece20db
Show file tree
Hide file tree
Showing 13 changed files with 57 additions and 48 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/verify-ensurepip-wheels.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Verify bundled pip and setuptools
name: Verify bundled wheels

on:
workflow_dispatch:
Expand Down Expand Up @@ -29,5 +29,5 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: '3'
- name: Compare checksums of bundled pip and setuptools to ones published on PyPI
- name: Compare checksum of bundled wheels to the ones published on PyPI
run: ./Tools/build/verify_ensurepip_wheels.py
7 changes: 5 additions & 2 deletions Doc/library/venv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,14 @@ creation according to their needs, the :class:`EnvBuilder` class.

.. method:: upgrade_dependencies(context)

Upgrades the core venv dependency packages (currently ``pip`` and
``setuptools``) in the environment. This is done by shelling out to the
Upgrades the core venv dependency packages (currently ``pip``)
in the environment. This is done by shelling out to the
``pip`` executable in the environment.

.. versionadded:: 3.9
.. versionchanged:: 3.12

``setuptools`` is no longer a core venv dependency.

.. method:: post_setup(context)

Expand Down
7 changes: 5 additions & 2 deletions Doc/using/venv-create.inc
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ The command, if run with ``-h``, will show the available options::
environment (pip is bootstrapped by default)
--prompt PROMPT Provides an alternative prompt prefix for this
environment.
--upgrade-deps Upgrade core dependencies: pip setuptools to the
--upgrade-deps Upgrade core dependencies (pip) to the
latest version in PyPI
Once an environment has been created, you may wish to activate it, e.g. by
sourcing an activate script in its bin directory.
.. versionchanged:: 3.12
``setuptools`` is no longer a core venv dependency.
.. versionchanged:: 3.9
Add ``--upgrade-deps`` option to upgrade pip + setuptools to the latest on PyPI
Expand Down Expand Up @@ -104,4 +108,3 @@ invoked to bootstrap ``pip`` into the virtual environment.
Multiple paths can be given to ``venv``, in which case an identical virtual
environment will be created, according to the given options, at each provided
path.
18 changes: 18 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,24 @@ Removed
project can be installed: it still provides ``distutils``.
(Contributed by Victor Stinner in :gh:`92584`.)

* Remove the bundled setuptools wheel from :mod:`ensurepip`,
and stop installing setuptools in environments created by :mod:`venv`.

``pip (>= 22.1)`` does not require setuptools to be installed in the
environment. ``setuptools``-based (and ``distutils``-based) packages
can still be used with ``pip install``, since pip will provide
``setuptools`` in the build environment it uses for building a
package.

``easy_install``, ``pkg_resources``, ``setuptools`` and ``distutils``
are no longer provided by default in environments created with
``venv`` or bootstrapped with ``ensurepip``, since they are part of
the ``setuptools`` package. For projects relying on these at runtime,
the ``setuptools`` project should be declared as a dependency and
installed separately (typically, using pip).

(Contributed by Pradyun Gedam in :gh:`95299`.)

* Removed many old deprecated :mod:`unittest` features:

- A number of :class:`~unittest.TestCase` method aliases:
Expand Down
16 changes: 7 additions & 9 deletions Lib/ensurepip/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@


__all__ = ["version", "bootstrap"]
_PACKAGE_NAMES = ('setuptools', 'pip')
_SETUPTOOLS_VERSION = "65.5.0"
_PACKAGE_NAMES = ('pip',)
_PIP_VERSION = "23.0.1"
_PROJECTS = [
("setuptools", _SETUPTOOLS_VERSION, "py3"),
("pip", _PIP_VERSION, "py3"),
]

Expand Down Expand Up @@ -153,17 +151,17 @@ def _bootstrap(*, root=None, upgrade=False, user=False,

_disable_pip_configuration_settings()

# By default, installing pip and setuptools installs all of the
# By default, installing pip installs all of the
# following scripts (X.Y == running Python version):
#
# pip, pipX, pipX.Y, easy_install, easy_install-X.Y
# pip, pipX, pipX.Y
#
# pip 1.5+ allows ensurepip to request that some of those be left out
if altinstall:
# omit pip, pipX and easy_install
# omit pip, pipX
os.environ["ENSUREPIP_OPTIONS"] = "altinstall"
elif not default_pip:
# omit pip and easy_install
# omit pip
os.environ["ENSUREPIP_OPTIONS"] = "install"

with tempfile.TemporaryDirectory() as tmpdir:
Expand Down Expand Up @@ -271,14 +269,14 @@ def _main(argv=None):
action="store_true",
default=False,
help=("Make an alternate install, installing only the X.Y versioned "
"scripts (Default: pipX, pipX.Y, easy_install-X.Y)."),
"scripts (Default: pipX, pipX.Y)."),
)
parser.add_argument(
"--default-pip",
action="store_true",
default=False,
help=("Make a default pip install, installing the unqualified pip "
"and easy_install in addition to the versioned scripts."),
"in addition to the versioned scripts."),
)

args = parser.parse_args(argv)
Expand Down
Binary file not shown.
35 changes: 12 additions & 23 deletions Lib/test/test_ensurepip.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ def test_version(self):
# Test version()
with tempfile.TemporaryDirectory() as tmpdir:
self.touch(tmpdir, "pip-1.2.3b1-py2.py3-none-any.whl")
self.touch(tmpdir, "setuptools-49.1.3-py3-none-any.whl")
with (unittest.mock.patch.object(ensurepip, '_PACKAGES', None),
unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', tmpdir)):
self.assertEqual(ensurepip.version(), '1.2.3b1')
Expand All @@ -36,15 +35,12 @@ def test_get_packages_no_dir(self):

# use bundled wheel packages
self.assertIsNotNone(packages['pip'].wheel_name)
self.assertIsNotNone(packages['setuptools'].wheel_name)

def test_get_packages_with_dir(self):
# Test _get_packages() with a wheel package directory
setuptools_filename = "setuptools-49.1.3-py3-none-any.whl"
pip_filename = "pip-20.2.2-py2.py3-none-any.whl"

with tempfile.TemporaryDirectory() as tmpdir:
self.touch(tmpdir, setuptools_filename)
self.touch(tmpdir, pip_filename)
# not used, make sure that it's ignored
self.touch(tmpdir, "wheel-0.34.2-py2.py3-none-any.whl")
Expand All @@ -53,15 +49,12 @@ def test_get_packages_with_dir(self):
unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', tmpdir)):
packages = ensurepip._get_packages()

self.assertEqual(packages['setuptools'].version, '49.1.3')
self.assertEqual(packages['setuptools'].wheel_path,
os.path.join(tmpdir, setuptools_filename))
self.assertEqual(packages['pip'].version, '20.2.2')
self.assertEqual(packages['pip'].wheel_path,
os.path.join(tmpdir, pip_filename))

# wheel package is ignored
self.assertEqual(sorted(packages), ['pip', 'setuptools'])
self.assertEqual(sorted(packages), ['pip'])


class EnsurepipMixin:
Expand Down Expand Up @@ -92,13 +85,13 @@ def test_basic_bootstrapping(self):
self.run_pip.assert_called_once_with(
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "setuptools", "pip",
unittest.mock.ANY, "pip",
],
unittest.mock.ANY,
)

additional_paths = self.run_pip.call_args[0][1]
self.assertEqual(len(additional_paths), 2)
self.assertEqual(len(additional_paths), 1)

def test_bootstrapping_with_root(self):
ensurepip.bootstrap(root="/foo/bar/")
Expand All @@ -107,7 +100,7 @@ def test_bootstrapping_with_root(self):
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "--root", "/foo/bar/",
"setuptools", "pip",
"pip",
],
unittest.mock.ANY,
)
Expand All @@ -118,7 +111,7 @@ def test_bootstrapping_with_user(self):
self.run_pip.assert_called_once_with(
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "--user", "setuptools", "pip",
unittest.mock.ANY, "--user", "pip",
],
unittest.mock.ANY,
)
Expand All @@ -129,7 +122,7 @@ def test_bootstrapping_with_upgrade(self):
self.run_pip.assert_called_once_with(
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "--upgrade", "setuptools", "pip",
unittest.mock.ANY, "--upgrade", "pip",
],
unittest.mock.ANY,
)
Expand All @@ -140,7 +133,7 @@ def test_bootstrapping_with_verbosity_1(self):
self.run_pip.assert_called_once_with(
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "-v", "setuptools", "pip",
unittest.mock.ANY, "-v", "pip",
],
unittest.mock.ANY,
)
Expand All @@ -151,7 +144,7 @@ def test_bootstrapping_with_verbosity_2(self):
self.run_pip.assert_called_once_with(
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "-vv", "setuptools", "pip",
unittest.mock.ANY, "-vv", "pip",
],
unittest.mock.ANY,
)
Expand All @@ -162,7 +155,7 @@ def test_bootstrapping_with_verbosity_3(self):
self.run_pip.assert_called_once_with(
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "-vvv", "setuptools", "pip",
unittest.mock.ANY, "-vvv", "pip",
],
unittest.mock.ANY,
)
Expand Down Expand Up @@ -239,7 +232,6 @@ def test_uninstall(self):
self.run_pip.assert_called_once_with(
[
"uninstall", "-y", "--disable-pip-version-check", "pip",
"setuptools",
]
)

Expand All @@ -250,7 +242,6 @@ def test_uninstall_with_verbosity_1(self):
self.run_pip.assert_called_once_with(
[
"uninstall", "-y", "--disable-pip-version-check", "-v", "pip",
"setuptools",
]
)

Expand All @@ -261,7 +252,6 @@ def test_uninstall_with_verbosity_2(self):
self.run_pip.assert_called_once_with(
[
"uninstall", "-y", "--disable-pip-version-check", "-vv", "pip",
"setuptools",
]
)

Expand All @@ -272,7 +262,7 @@ def test_uninstall_with_verbosity_3(self):
self.run_pip.assert_called_once_with(
[
"uninstall", "-y", "--disable-pip-version-check", "-vvv",
"pip", "setuptools",
"pip"
]
)

Expand Down Expand Up @@ -312,13 +302,13 @@ def test_basic_bootstrapping(self):
self.run_pip.assert_called_once_with(
[
"install", "--no-cache-dir", "--no-index", "--find-links",
unittest.mock.ANY, "setuptools", "pip",
unittest.mock.ANY, "pip",
],
unittest.mock.ANY,
)

additional_paths = self.run_pip.call_args[0][1]
self.assertEqual(len(additional_paths), 2)
self.assertEqual(len(additional_paths), 1)
self.assertEqual(exit_code, 0)

def test_bootstrapping_error_code(self):
Expand All @@ -344,7 +334,6 @@ def test_basic_uninstall(self):
self.run_pip.assert_called_once_with(
[
"uninstall", "-y", "--disable-pip-version-check", "pip",
"setuptools",
]
)

Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@ def pip_cmd_checker(cmd, **kwargs):
'install',
'--upgrade',
'pip',
'setuptools'
]
)

Expand Down Expand Up @@ -745,7 +744,6 @@ def do_test_with_pip(self, system_site_packages):
# future pip versions, this test can likely be relaxed further.
out = out.decode("latin-1") # Force to text, prevent decoding errors
self.assertIn("Successfully uninstalled pip", out)
self.assertIn("Successfully uninstalled setuptools", out)
# Check pip is now gone from the virtual environment. This only
# applies in the system_site_packages=False case, because in the
# other case, pip may still be available in the system site-packages
Expand Down
4 changes: 2 additions & 2 deletions Lib/venv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import types


CORE_VENV_DEPS = ('pip', 'setuptools')
CORE_VENV_DEPS = ('pip',)
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -523,7 +523,7 @@ def main(args=None):
'this environment.')
parser.add_argument('--upgrade-deps', default=False, action='store_true',
dest='upgrade_deps',
help=f'Upgrade core dependencies: {", ".join(CORE_VENV_DEPS)} '
help=f'Upgrade core dependencies ({", ".join(CORE_VENV_DEPS)}) '
'to the latest version in PyPI')
options = parser.parse_args(args)
if options.upgrade and options.clear:
Expand Down
8 changes: 4 additions & 4 deletions Mac/BuildScript/scripts/postflight.ensurepip
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,19 @@ if [ -d /usr/local/bin ] ; then

cd /usr/local/bin

# Create pipx.y and easy_install-x.y links if /usr/local/bin/pythonx.y
# Create pipx.y links if /usr/local/bin/pythonx.y
# is linked to this framework version
install_links_if_our_fw "python${PYVER}" \
"pip${PYVER}" "easy_install-${PYVER}"
"pip${PYVER}"

# Create pipx link if /usr/local/bin/pythonx is linked to this version
install_links_if_our_fw "python${PYMAJOR}" \
"pip${PYMAJOR}"

# Create pip and easy_install link if /usr/local/bin/python
# Create pip link if /usr/local/bin/python
# is linked to this version
install_links_if_our_fw "python" \
"pip" "easy_install"
"pip"
)
fi
exit 0
1 change: 0 additions & 1 deletion Mac/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ altinstallunixtools:
-if test "x$(ENSUREPIP)" != "xno" ; then \
cd "$(DESTDIR)$(FRAMEWORKUNIXTOOLSPREFIX)/bin" && \
for fn in \
easy_install-$(VERSION) \
pip$(VERSION) \
; \
do \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove the bundled setuptools wheel from ``ensurepip``, and stop installing setuptools in environments created by ``venv``.
2 changes: 1 addition & 1 deletion Tools/build/verify_ensurepip_wheels.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pathlib import Path
from urllib.request import urlopen

PACKAGE_NAMES = ("pip", "setuptools")
PACKAGE_NAMES = ("pip",)
ENSURE_PIP_ROOT = Path(__file__).parent.parent.parent / "Lib/ensurepip"
WHEEL_DIR = ENSURE_PIP_ROOT / "_bundled"
ENSURE_PIP_INIT_PY_TEXT = (ENSURE_PIP_ROOT / "__init__.py").read_text(encoding="utf-8")
Expand Down

0 comments on commit ece20db

Please sign in to comment.