Index: Doc/library/ssl.rst =================================================================== --- Doc/library/ssl.rst (Revision 86645) +++ Doc/library/ssl.rst (Arbeitskopie) @@ -152,8 +152,12 @@ raised from the underlying socket; if :const:`False`, it will raise the exceptions back to the caller. + If SSLContext.current() returns a context (either the global default one, + or the current thread's current context), most of the arguments of wrap_socket + are ignored, and their values are taken from that context instead. + .. versionchanged:: 3.2 - New optional argument *ciphers*. + New optional argument *ciphers*. SSLContext objects get considered. Random generation ^^^^^^^^^^^^^^^^^ @@ -497,6 +501,16 @@ It also manages a cache of SSL sessions for server-side sockets, in order to speed up repeated connections from the same clients. +SSLContexts can be explicitly specified per connection, or a default +"current" SSLContext might be used when an SSLSocket is created. +SSLContext objects can be used as context managers, allowing to write +code such as:: + + with SSLContext(PROTOCOL_SSLv23): + SSLContext.current().set_default_verify_path() + # code executed in this block (e.g. urllib, imaplib) + # will use the standard CAs for certificate verification + .. class:: SSLContext(protocol) Create a new SSL context. You must pass *protocol* which must be one Index: Lib/ssl.py =================================================================== --- Lib/ssl.py (Revision 86645) +++ Lib/ssl.py (Arbeitskopie) @@ -56,6 +56,7 @@ import textwrap import re +import threading import _ssl # if we can't import it, let the error propagate @@ -151,6 +152,11 @@ __slots__ = ('protocol',) + # per-thread current contexts + _contexts = threading.local() + # global default context + default = None + def __new__(cls, protocol, *args, **kwargs): return _SSLContext.__new__(cls, protocol) @@ -167,7 +173,24 @@ server_hostname=server_hostname, _context=self) + # Context manager: per-thread nestable current context + def __enter__(self): + try: + SSLContext._contexts.stack.append(self) + except AttributeError: + SSLContext._contexts.stack = [self] + def __exit__(self, type, value, traceback): + assert SSLContext._contexts.stack[-1] is self + SSLContext._contexts.stack.pop() + + @classmethod + def current(self): + try: + return self._contexts.stack[-1] + except (AttributeError, IndexError): + return self.default + class SSLSocket(socket): """This class implements a subtype of socket.socket that wraps the underlying OS socket in an SSL context when necessary, and @@ -182,6 +205,8 @@ server_hostname=None, _context=None): + if _context is None: + _context = SSLContext.current if _context: self.context = _context else: