Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ae2ee96

Browse files
committed
Issue #19744: improve ensurepip error when ssl is missing
1 parent f71cae0 commit ae2ee96

4 files changed

Lines changed: 81 additions & 2 deletions

File tree

Lib/ensurepip/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@
1414

1515
_PIP_VERSION = "1.5rc2"
1616

17+
# pip currently requires ssl support, so we try to provide a nicer
18+
# error message when that is missing (http://bugs.python.org/issue19744)
19+
_MISSING_SSL_MESSAGE = ("pip {} requires SSL/TLS".format(_PIP_VERSION))
20+
try:
21+
import ssl
22+
except ImportError:
23+
ssl = None
24+
def _require_ssl_for_pip():
25+
raise RuntimeError(_MISSING_SSL_MESSAGE)
26+
else:
27+
def _require_ssl_for_pip():
28+
pass
29+
1730
_PROJECTS = [
1831
("setuptools", _SETUPTOOLS_VERSION),
1932
("pip", _PIP_VERSION),
@@ -57,6 +70,7 @@ def bootstrap(*, root=None, upgrade=False, user=False,
5770
if altinstall and default_pip:
5871
raise ValueError("Cannot use altinstall and default_pip together")
5972

73+
_require_ssl_for_pip()
6074
_clear_pip_environment_variables()
6175

6276
# By default, installing pip and setuptools installs all of the
@@ -121,6 +135,7 @@ def _uninstall_helper(*, verbosity=0):
121135
"({!r} installed, {!r} bundled)")
122136
raise RuntimeError(msg.format(pip.__version__, _PIP_VERSION))
123137

138+
_require_ssl_for_pip()
124139
_clear_pip_environment_variables()
125140

126141
# Construct the arguments to be passed to the pip command

Lib/test/test_ensurepip.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@
99
import ensurepip
1010
import ensurepip._uninstall
1111

12+
# pip currently requires ssl support, so we ensure we handle
13+
# it being missing (http://bugs.python.org/issue19744)
14+
ensurepip_no_ssl = test.support.import_fresh_module("ensurepip",
15+
blocked=["ssl"])
16+
try:
17+
import ssl
18+
except ImportError:
19+
def requires_usable_pip(f):
20+
deco = unittest.skip(ensurepip._MISSING_SSL_MESSAGE)
21+
return deco(f)
22+
else:
23+
def requires_usable_pip(f):
24+
return f
25+
1226
class TestEnsurePipVersion(unittest.TestCase):
1327

1428
def test_returns_version(self):
@@ -28,8 +42,10 @@ def setUp(self):
2842
patched_os.path = os.path
2943
self.os_environ = patched_os.environ = os.environ.copy()
3044

45+
3146
class TestBootstrap(EnsurepipMixin, unittest.TestCase):
3247

48+
@requires_usable_pip
3349
def test_basic_bootstrapping(self):
3450
ensurepip.bootstrap()
3551

@@ -44,6 +60,7 @@ def test_basic_bootstrapping(self):
4460
additional_paths = self.run_pip.call_args[0][1]
4561
self.assertEqual(len(additional_paths), 2)
4662

63+
@requires_usable_pip
4764
def test_bootstrapping_with_root(self):
4865
ensurepip.bootstrap(root="/foo/bar/")
4966

@@ -56,6 +73,7 @@ def test_bootstrapping_with_root(self):
5673
unittest.mock.ANY,
5774
)
5875

76+
@requires_usable_pip
5977
def test_bootstrapping_with_user(self):
6078
ensurepip.bootstrap(user=True)
6179

@@ -67,6 +85,7 @@ def test_bootstrapping_with_user(self):
6785
unittest.mock.ANY,
6886
)
6987

88+
@requires_usable_pip
7089
def test_bootstrapping_with_upgrade(self):
7190
ensurepip.bootstrap(upgrade=True)
7291

@@ -78,6 +97,7 @@ def test_bootstrapping_with_upgrade(self):
7897
unittest.mock.ANY,
7998
)
8099

100+
@requires_usable_pip
81101
def test_bootstrapping_with_verbosity_1(self):
82102
ensurepip.bootstrap(verbosity=1)
83103

@@ -89,6 +109,7 @@ def test_bootstrapping_with_verbosity_1(self):
89109
unittest.mock.ANY,
90110
)
91111

112+
@requires_usable_pip
92113
def test_bootstrapping_with_verbosity_2(self):
93114
ensurepip.bootstrap(verbosity=2)
94115

@@ -100,6 +121,7 @@ def test_bootstrapping_with_verbosity_2(self):
100121
unittest.mock.ANY,
101122
)
102123

124+
@requires_usable_pip
103125
def test_bootstrapping_with_verbosity_3(self):
104126
ensurepip.bootstrap(verbosity=3)
105127

@@ -111,14 +133,17 @@ def test_bootstrapping_with_verbosity_3(self):
111133
unittest.mock.ANY,
112134
)
113135

136+
@requires_usable_pip
114137
def test_bootstrapping_with_regular_install(self):
115138
ensurepip.bootstrap()
116139
self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "install")
117140

141+
@requires_usable_pip
118142
def test_bootstrapping_with_alt_install(self):
119143
ensurepip.bootstrap(altinstall=True)
120144
self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "altinstall")
121145

146+
@requires_usable_pip
122147
def test_bootstrapping_with_default_pip(self):
123148
ensurepip.bootstrap(default_pip=True)
124149
self.assertNotIn("ENSUREPIP_OPTIONS", self.os_environ)
@@ -128,6 +153,7 @@ def test_altinstall_default_pip_conflict(self):
128153
ensurepip.bootstrap(altinstall=True, default_pip=True)
129154
self.run_pip.assert_not_called()
130155

156+
@requires_usable_pip
131157
def test_pip_environment_variables_removed(self):
132158
# ensurepip deliberately ignores all pip environment variables
133159
# See http://bugs.python.org/issue19734 for details
@@ -169,6 +195,7 @@ def test_uninstall_fails_with_wrong_version(self):
169195
self.run_pip.assert_not_called()
170196

171197

198+
@requires_usable_pip
172199
def test_uninstall(self):
173200
with fake_pip():
174201
ensurepip._uninstall_helper()
@@ -177,6 +204,7 @@ def test_uninstall(self):
177204
["uninstall", "-y", "pip", "setuptools"]
178205
)
179206

207+
@requires_usable_pip
180208
def test_uninstall_with_verbosity_1(self):
181209
with fake_pip():
182210
ensurepip._uninstall_helper(verbosity=1)
@@ -185,6 +213,7 @@ def test_uninstall_with_verbosity_1(self):
185213
["uninstall", "-y", "-v", "pip", "setuptools"]
186214
)
187215

216+
@requires_usable_pip
188217
def test_uninstall_with_verbosity_2(self):
189218
with fake_pip():
190219
ensurepip._uninstall_helper(verbosity=2)
@@ -193,6 +222,7 @@ def test_uninstall_with_verbosity_2(self):
193222
["uninstall", "-y", "-vv", "pip", "setuptools"]
194223
)
195224

225+
@requires_usable_pip
196226
def test_uninstall_with_verbosity_3(self):
197227
with fake_pip():
198228
ensurepip._uninstall_helper(verbosity=3)
@@ -201,6 +231,7 @@ def test_uninstall_with_verbosity_3(self):
201231
["uninstall", "-y", "-vvv", "pip", "setuptools"]
202232
)
203233

234+
@requires_usable_pip
204235
def test_pip_environment_variables_removed(self):
205236
# ensurepip deliberately ignores all pip environment variables
206237
# See http://bugs.python.org/issue19734 for details
@@ -210,6 +241,30 @@ def test_pip_environment_variables_removed(self):
210241
self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ)
211242

212243

244+
class TestMissingSSL(EnsurepipMixin, unittest.TestCase):
245+
246+
def setUp(self):
247+
sys.modules["ensurepip"] = ensurepip_no_ssl
248+
@self.addCleanup
249+
def restore_module():
250+
sys.modules["ensurepip"] = ensurepip
251+
super().setUp()
252+
253+
def test_bootstrap_requires_ssl(self):
254+
self.os_environ["PIP_THIS_SHOULD_STAY"] = "test fodder"
255+
with self.assertRaisesRegex(RuntimeError, "requires SSL/TLS"):
256+
ensurepip_no_ssl.bootstrap()
257+
self.run_pip.assert_not_called()
258+
self.assertIn("PIP_THIS_SHOULD_STAY", self.os_environ)
259+
260+
def test_uninstall_requires_ssl(self):
261+
self.os_environ["PIP_THIS_SHOULD_STAY"] = "test fodder"
262+
with self.assertRaisesRegex(RuntimeError, "requires SSL/TLS"):
263+
with fake_pip():
264+
ensurepip_no_ssl._uninstall_helper()
265+
self.run_pip.assert_not_called()
266+
self.assertIn("PIP_THIS_SHOULD_STAY", self.os_environ)
267+
213268
# Basic testing of the main functions and their argument parsing
214269

215270
EXPECTED_VERSION_OUTPUT = "pip " + ensurepip._PIP_VERSION
@@ -224,6 +279,7 @@ def test_bootstrap_version(self):
224279
self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
225280
self.run_pip.assert_not_called()
226281

282+
@requires_usable_pip
227283
def test_basic_bootstrapping(self):
228284
ensurepip._main([])
229285

@@ -248,6 +304,7 @@ def test_uninstall_version(self):
248304
self.assertEqual(result, EXPECTED_VERSION_OUTPUT)
249305
self.run_pip.assert_not_called()
250306

307+
@requires_usable_pip
251308
def test_basic_uninstall(self):
252309
with fake_pip():
253310
ensurepip._uninstall._main([])

Lib/test/test_venv.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import textwrap
1818
import unittest
1919
import venv
20+
21+
# pip currently requires ssl support, so we ensure we handle
22+
# it being missing (http://bugs.python.org/issue19744)
2023
try:
2124
import ssl
2225
except ImportError:
@@ -285,8 +288,8 @@ def test_explicit_no_pip(self):
285288
self.run_with_capture(venv.create, self.env_dir, with_pip=False)
286289
self.assert_pip_not_installed()
287290

288-
# Temporary skip for http://bugs.python.org/issue19744
289-
@unittest.skipIf(ssl is None, 'pip needs SSL support')
291+
# Requesting pip fails without SSL (http://bugs.python.org/issue19744)
292+
@unittest.skipIf(ssl is None, ensurepip._MISSING_SSL_MESSAGE)
290293
def test_with_pip(self):
291294
shutil.rmtree(self.env_dir)
292295
with EnvironmentVarGuard() as envvars:

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ Core and Builtins
4444
Library
4545
-------
4646

47+
- Issue #19744: ensurepip now provides a better error message when Python is
48+
built without SSL/TLS support (pip currently requires that support to run,
49+
even if only operating with local wheel files)
50+
4751
- Issue #19734: ensurepip now ignores all pip environment variables to avoid
4852
odd behaviour based on user configuration settings
4953

0 commit comments

Comments
 (0)