diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index b588dad..07928ed 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -16,7 +16,7 @@ authentication, redirections, cookies and more. The :mod:`urllib.request` module defines the following functions: -.. function:: urlopen(url, data=None[, timeout], *, cafile=None, capath=None, cadefault=False) +.. function:: urlopen(url, data=None[, timeout], *, cafile=None, capath=None, cadefault=False, context=None) Open the URL *url*, which can be either a string or a :class:`Request` object. @@ -47,6 +47,10 @@ The :mod:`urllib.request` module defines the following functions: the global default timeout setting will be used). This actually only works for HTTP, HTTPS and FTP connections. + If *context* is specified, it must be a :class:`ssl.SSLContext` instance + describing the various SSL options. See + :class:`~http.client.HTTPSConnection` for more details. + The optional *cafile* and *capath* parameters specify a set of trusted CA certificates for HTTPS requests. *cafile* should point to a single file containing a bundle of CA certificates, whereas *capath* should @@ -111,6 +115,9 @@ The :mod:`urllib.request` module defines the following functions: .. versionchanged:: 3.3 *cadefault* was added. + .. versionchanged:: 3.5 + *context* was added. + .. function:: install_opener(opener) Install an :class:`OpenerDirector` instance as the default global opener. diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 5478837..962ef73 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -10,6 +10,7 @@ import unittest from unittest.mock import patch from test import support import os +import ssl import sys import tempfile from nturl2path import url2pathname, pathname2url @@ -379,6 +380,13 @@ Content-Type: text/html; charset=iso-8859-1 with support.check_warnings(('',DeprecationWarning)): urllib.request.URLopener() + def test_cafile_and_context(self): + context = ssl.create_default_context() + with self.assertRaises(ValueError): + urllib.request.urlopen( + "https://localhost", cafile="/nonexistent/path", context=context + ) + class urlopen_DataTests(unittest.TestCase): """Test urlopen() opening a data URL.""" diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 67c7566..e0c8116 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -136,9 +136,14 @@ __version__ = sys.version[:3] _opener = None def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - *, cafile=None, capath=None, cadefault=False): + *, cafile=None, capath=None, cadefault=False, context=None): global _opener if cafile or capath or cadefault: + if context is not None: + raise ValueError( + "You can't pass both context and any of cafile, capath, and " + "cadefault" + ) if not _have_ssl: raise ValueError('SSL support not available') context = ssl._create_stdlib_context(cert_reqs=ssl.CERT_REQUIRED, @@ -146,6 +151,9 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, capath=capath) https_handler = HTTPSHandler(context=context, check_hostname=True) opener = build_opener(https_handler) + elif context: + https_handler = HTTPSHandler(context=context) + opener = build_opener(https_handler) elif _opener is None: _opener = opener = build_opener() else: