summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2014-11-24 02:55:24 (GMT)
committerBenjamin Peterson <benjamin@python.org>2014-11-24 02:55:24 (GMT)
commitb206473ef8a7abe9abf5ab8776ea3bcb90adc747 (patch)
tree94fd8b54f90de94e6e608070871f1c529ba74118
parent79828343d8b31b0ccd738490d24c14bafe365099 (diff)
downloadcpython-b206473ef8a7abe9abf5ab8776ea3bcb90adc747.zip
cpython-b206473ef8a7abe9abf5ab8776ea3bcb90adc747.tar.gz
cpython-b206473ef8a7abe9abf5ab8776ea3bcb90adc747.tar.bz2
give urllib.urlopen a context parameter (closes #22927)
-rw-r--r--Doc/library/urllib.rst18
-rw-r--r--Lib/httplib.py5
-rw-r--r--Lib/test/test_urllibnet.py20
-rw-r--r--Lib/urllib.py24
-rw-r--r--Misc/NEWS3
5 files changed, 54 insertions, 16 deletions
diff --git a/Doc/library/urllib.rst b/Doc/library/urllib.rst
index 62f198f..b2a817d 100644
--- a/Doc/library/urllib.rst
+++ b/Doc/library/urllib.rst
@@ -31,7 +31,7 @@ reading, and no seek operations are available.
High-level interface
--------------------
-.. function:: urlopen(url[, data[, proxies]])
+.. function:: urlopen(url[, data[, proxies[, context]]])
Open a network object denoted by a URL for reading. If the URL does not
have a scheme identifier, or if it has :file:`file:` as its scheme
@@ -122,8 +122,12 @@ High-level interface
filehandle = urllib.urlopen(some_url, proxies=None)
filehandle = urllib.urlopen(some_url)
- Proxies which require authentication for use are not currently supported; this
- is considered an implementation limitation.
+ Proxies which require authentication for use are not currently supported;
+ this is considered an implementation limitation.
+
+ The *context* parameter may be set to a :class:`ssl.SSLContext` instance to
+ configure the SSL settings that are used if :func:`urlopen` makes a HTTPS
+ connection.
.. versionchanged:: 2.3
Added the *proxies* support.
@@ -132,6 +136,9 @@ High-level interface
Added :meth:`getcode` to returned object and support for the
:envvar:`no_proxy` environment variable.
+ .. versionchanged:: 2.7.9
+ The *context* parameter was added.
+
.. deprecated:: 2.6
The :func:`urlopen` function has been removed in Python 3 in favor
of :func:`urllib2.urlopen`.
@@ -292,7 +299,7 @@ Utility functions
URL Opener objects
------------------
-.. class:: URLopener([proxies[, **x509]])
+.. class:: URLopener([proxies[, context[, **x509]]])
Base class for opening and reading URLs. Unless you need to support opening
objects using schemes other than :file:`http:`, :file:`ftp:`, or :file:`file:`,
@@ -309,6 +316,9 @@ URL Opener objects
value is ``None``, in which case environmental proxy settings will be used if
present, as discussed in the definition of :func:`urlopen`, above.
+ The *context* parameter may be a :class:`ssl.SSLContext` instance. If given,
+ it defines the SSL settings the opener uses to make HTTPS connections.
+
Additional keyword parameters, collected in *x509*, may be used for
authentication of the client when using the :file:`https:` scheme. The keywords
*key_file* and *cert_file* are supported to provide an SSL key and certificate;
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 @@ else:
_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 9f24b7ad..7c4b1f7 100644
--- a/Lib/test/test_urllibnet.py
+++ b/Lib/test/test_urllibnet.py
@@ -7,6 +7,15 @@ import sys
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 @@ class urlretrieveNetworkTests(unittest.TestCase):
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 @@ def test_main():
("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 @@ else:
# 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 @@ def urlopen(url, data=None, proxies=None):
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 @@ class URLopener:
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 @@ class URLopener:
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',
diff --git a/Misc/NEWS b/Misc/NEWS
index 7e4a0b0..3ad4862 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -42,6 +42,9 @@ Core and Builtins
Library
-------
+- Issue #22927: Allow urllib.urlopen to take a *context* parameter to control
+ SSL settings for HTTPS connections.
+
- Issue #22921: Allow SSLContext to take the *hostname* parameter even if
OpenSSL doesn't support SNI.