[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