Close #20053: ignore default pip config settings

ensurepip now sets PIP_CONFIG_FILE to os.devnull before
import pip from the wheel file. This also ensures venv
ignores the default settings when bootstrapping pip.
This commit is contained in:
Nick Coghlan 2014-02-04 23:02:36 +10:00
parent a9b15241c6
commit 6edd82a1d2
4 changed files with 53 additions and 12 deletions

View File

@ -47,13 +47,16 @@ def version():
""" """
return _PIP_VERSION return _PIP_VERSION
def _clear_pip_environment_variables(): def _disable_pip_configuration_settings():
# We deliberately ignore all pip environment variables # We deliberately ignore all pip environment variables
# when invoking pip # when invoking pip
# See http://bugs.python.org/issue19734 for details # See http://bugs.python.org/issue19734 for details
keys_to_remove = [k for k in os.environ if k.startswith("PIP_")] keys_to_remove = [k for k in os.environ if k.startswith("PIP_")]
for k in keys_to_remove: for k in keys_to_remove:
del os.environ[k] del os.environ[k]
# We also ignore the settings in the default pip configuration file
# See http://bugs.python.org/issue20053 for details
os.environ['PIP_CONFIG_FILE'] = os.devnull
def bootstrap(*, root=None, upgrade=False, user=False, def bootstrap(*, root=None, upgrade=False, user=False,
@ -69,7 +72,7 @@ def bootstrap(*, root=None, upgrade=False, user=False,
raise ValueError("Cannot use altinstall and default_pip together") raise ValueError("Cannot use altinstall and default_pip together")
_require_ssl_for_pip() _require_ssl_for_pip()
_clear_pip_environment_variables() _disable_pip_configuration_settings()
# By default, installing pip and setuptools installs all of the # By default, installing pip and setuptools installs all of the
# following scripts (X.Y == running Python version): # following scripts (X.Y == running Python version):
@ -130,7 +133,7 @@ def _uninstall_helper(*, verbosity=0):
raise RuntimeError(msg.format(pip.__version__, _PIP_VERSION)) raise RuntimeError(msg.format(pip.__version__, _PIP_VERSION))
_require_ssl_for_pip() _require_ssl_for_pip()
_clear_pip_environment_variables() _disable_pip_configuration_settings()
# Construct the arguments to be passed to the pip command # Construct the arguments to be passed to the pip command
args = ["uninstall", "-y"] args = ["uninstall", "-y"]

View File

@ -36,9 +36,11 @@ class EnsurepipMixin:
self.addCleanup(run_pip_patch.stop) self.addCleanup(run_pip_patch.stop)
# Avoid side effects on the actual os module # Avoid side effects on the actual os module
real_devnull = os.devnull
os_patch = unittest.mock.patch("ensurepip.os") os_patch = unittest.mock.patch("ensurepip.os")
patched_os = os_patch.start() patched_os = os_patch.start()
self.addCleanup(os_patch.stop) self.addCleanup(os_patch.stop)
patched_os.devnull = real_devnull
patched_os.path = os.path patched_os.path = os.path
self.os_environ = patched_os.environ = os.environ.copy() self.os_environ = patched_os.environ = os.environ.copy()
@ -161,6 +163,12 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase):
ensurepip.bootstrap() ensurepip.bootstrap()
self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ) self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
@requires_usable_pip
def test_pip_config_file_disabled(self):
# ensurepip deliberately ignores the pip config file
# See http://bugs.python.org/issue20053 for details
ensurepip.bootstrap()
self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
@contextlib.contextmanager @contextlib.contextmanager
def fake_pip(version=ensurepip._PIP_VERSION): def fake_pip(version=ensurepip._PIP_VERSION):
@ -240,6 +248,14 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase):
ensurepip._uninstall_helper() ensurepip._uninstall_helper()
self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ) self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
@requires_usable_pip
def test_pip_config_file_disabled(self):
# ensurepip deliberately ignores the pip config file
# See http://bugs.python.org/issue20053 for details
with fake_pip():
ensurepip._uninstall_helper()
self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull)
class TestMissingSSL(EnsurepipMixin, unittest.TestCase): class TestMissingSSL(EnsurepipMixin, unittest.TestCase):

View File

@ -301,16 +301,35 @@ class EnsurePipTest(BaseTest):
# that we want to ensure it ignores the normal pip environment # that we want to ensure it ignores the normal pip environment
# variable settings. We set PIP_NO_INSTALL here specifically # variable settings. We set PIP_NO_INSTALL here specifically
# to check that ensurepip (and hence venv) ignores it. # to check that ensurepip (and hence venv) ignores it.
# See http://bugs.python.org/issue19734 for details # See http://bugs.python.org/issue19734
envvars["PIP_NO_INSTALL"] = "1" envvars["PIP_NO_INSTALL"] = "1"
try: # Also check that we ignore the pip configuration file
self.run_with_capture(venv.create, self.env_dir, with_pip=True) # See http://bugs.python.org/issue20053
except subprocess.CalledProcessError as exc: with tempfile.TemporaryDirectory() as home_dir:
# The output this produces can be a little hard to read, but envvars["HOME"] = home_dir
# least it has all the details bad_config = "[global]\nno-install=1"
details = exc.output.decode(errors="replace") # Write to both config file names on all platforms to reduce
msg = "{}\n\n**Subprocess Output**\n{}".format(exc, details) # cross-platform variation in test code behaviour
self.fail(msg) win_location = ("pip", "pip.ini")
posix_location = (".pip", "pip.conf")
for dirname, fname in (win_location, posix_location):
dirpath = os.path.join(home_dir, dirname)
os.mkdir(dirpath)
fpath = os.path.join(dirpath, fname)
with open(fpath, 'w') as f:
f.write(bad_config)
# Actually run the create command with all that unhelpful
# config in place to ensure we ignore it
try:
self.run_with_capture(venv.create, self.env_dir,
with_pip=True)
except subprocess.CalledProcessError as exc:
# The output this produces can be a little hard to read,
# but at least it has all the details
details = exc.output.decode(errors="replace")
msg = "{}\n\n**Subprocess Output**\n{}"
self.fail(msg.format(exc, details))
# Ensure pip is available in the virtual environment # Ensure pip is available in the virtual environment
envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
cmd = [envpy, '-Im', 'pip', '--version'] cmd = [envpy, '-Im', 'pip', '--version']

View File

@ -24,6 +24,9 @@ Core and Builtins
Library Library
------- -------
- Issue #20053: ensurepip (and hence venv) are no longer affected by the
settings in the default pip configuration file.
- Issue #20426: When passing the re.DEBUG flag, re.compile() displays the - Issue #20426: When passing the re.DEBUG flag, re.compile() displays the
debug output every time it is called, regardless of the compilation cache. debug output every time it is called, regardless of the compilation cache.