[Python-checkins] cpython (2.7): give urllib.urlopen a context parameter (closes #22927)
benjamin.peterson
python-checkins at python.org
Mon Nov 24 03:55:41 CET 2014
https://hg.python.org/cpython/rev/c84f36a5f556
changeset: 93569:c84f36a5f556
branch: 2.7
parent: 93563:40f9e91f3626
user: Benjamin Peterson <benjamin at python.org>
date: Sun Nov 23 20:55:24 2014 -0600
summary:
give urllib.urlopen a context parameter (closes #22927)
files:
Doc/library/urllib.rst | 18 ++++++++++++++----
Lib/httplib.py | 5 +++--
Lib/test/test_urllibnet.py | 20 +++++++++++++++++++-
Lib/urllib.py | 24 +++++++++++++++---------
Misc/NEWS | 3 +++
5 files changed, 54 insertions(+), 16 deletions(-)
diff --git a/Doc/library/urllib.rst b/Doc/library/urllib.rst
--- a/Doc/library/urllib.rst
+++ b/Doc/library/urllib.rst
@@ -31,7 +31,7 @@
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 @@
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 @@
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 @@
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 @@
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
--- 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
--- 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)
+ at 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
--- 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',
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -42,6 +42,9 @@
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.
--
Repository URL: https://hg.python.org/cpython
More information about the Python-checkins
mailing list