give urllib.urlopen a context parameter (closes #22927)
diff --git a/Lib/httplib.py b/Lib/httplib.py
index 6d2e38d..48fbcb6 100644
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -1238,14 +1238,15 @@
_connection_class = HTTPSConnection
def __init__(self, host='', port=None, key_file=None, cert_file=None,
- strict=None):
+ strict=None, context=None):
# provide a default host, pass the X509 cert info
# urf. compensate for bad input.
if port == 0:
port = None
self._setup(self._connection_class(host, port, key_file,
- cert_file, strict))
+ cert_file, strict,
+ context=context))
# we never actually use these for anything, but we keep them
# here for compatibility with post-1.5.2 CVS.
diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py
index 9f24b7a..7c4b1f7 100644
--- a/Lib/test/test_urllibnet.py
+++ b/Lib/test/test_urllibnet.py
@@ -7,6 +7,15 @@
import os
import time
+try:
+ import ssl
+except ImportError:
+ ssl = None
+
+here = os.path.dirname(__file__)
+# Self-signed cert file for self-signed.pythontest.net
+CERT_selfsigned_pythontestdotnet = os.path.join(here, 'selfsigned_pythontestdotnet.pem')
+
mimetools = test_support.import_module("mimetools", deprecated=True)
@@ -195,6 +204,14 @@
self.fail('Date value not in %r format', dateformat)
+@unittest.skipIf(ssl is None, "requires ssl")
+class urlopen_HttpsTests(unittest.TestCase):
+
+ def test_context_argument(self):
+ context = ssl.create_default_context(cafile=CERT_selfsigned_pythontestdotnet)
+ response = urllib.urlopen("https://self-signed.pythontest.net", context=context)
+ self.assertIn("Python", response.read())
+
def test_main():
test_support.requires('network')
@@ -202,7 +219,8 @@
("urllib.urlopen.. has been removed", DeprecationWarning)):
test_support.run_unittest(URLTimeoutTest,
urlopenNetworkTests,
- urlretrieveNetworkTests)
+ urlretrieveNetworkTests,
+ urlopen_HttpsTests)
if __name__ == "__main__":
test_main()
diff --git a/Lib/urllib.py b/Lib/urllib.py
index ac5d797..2408cb8 100644
--- a/Lib/urllib.py
+++ b/Lib/urllib.py
@@ -69,15 +69,15 @@
# Shortcut for basic usage
_urlopener = None
-def urlopen(url, data=None, proxies=None):
+def urlopen(url, data=None, proxies=None, context=None):
"""Create a file-like object for the specified URL to read from."""
from warnings import warnpy3k
warnpy3k("urllib.urlopen() has been removed in Python 3.0 in "
"favor of urllib2.urlopen()", stacklevel=2)
global _urlopener
- if proxies is not None:
- opener = FancyURLopener(proxies=proxies)
+ if proxies is not None or context is not None:
+ opener = FancyURLopener(proxies=proxies, context=context)
elif not _urlopener:
opener = FancyURLopener()
_urlopener = opener
@@ -87,11 +87,15 @@
return opener.open(url)
else:
return opener.open(url, data)
-def urlretrieve(url, filename=None, reporthook=None, data=None):
+def urlretrieve(url, filename=None, reporthook=None, data=None, context=None):
global _urlopener
- if not _urlopener:
- _urlopener = FancyURLopener()
- return _urlopener.retrieve(url, filename, reporthook, data)
+ if context is not None:
+ opener = FancyURLopener(context=context)
+ elif not _urlopener:
+ _urlopener = opener = FancyURLopener()
+ else:
+ opener = _urlopener
+ return opener.retrieve(url, filename, reporthook, data)
def urlcleanup():
if _urlopener:
_urlopener.cleanup()
@@ -126,13 +130,14 @@
version = "Python-urllib/%s" % __version__
# Constructor
- def __init__(self, proxies=None, **x509):
+ def __init__(self, proxies=None, context=None, **x509):
if proxies is None:
proxies = getproxies()
assert hasattr(proxies, 'has_key'), "proxies must be a mapping"
self.proxies = proxies
self.key_file = x509.get('key_file')
self.cert_file = x509.get('cert_file')
+ self.context = context
self.addheaders = [('User-Agent', self.version)]
self.__tempfiles = []
self.__unlink = os.unlink # See cleanup()
@@ -422,7 +427,8 @@
auth = None
h = httplib.HTTPS(host, 0,
key_file=self.key_file,
- cert_file=self.cert_file)
+ cert_file=self.cert_file,
+ context=self.context)
if data is not None:
h.putrequest('POST', selector)
h.putheader('Content-Type',