Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-urllib3_1 for openSUSE:Factory checked in at 2023-05-25 23:52:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-urllib3_1 (Old) and /work/SRC/openSUSE:Factory/.python-urllib3_1.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-urllib3_1" Thu May 25 23:52:08 2023 rev:3 rq:1088727 version:1.26.16 Changes: -------- --- /work/SRC/openSUSE:Factory/python-urllib3_1/python-urllib3_1.changes 2023-05-23 14:53:34.642140196 +0200 +++ /work/SRC/openSUSE:Factory/.python-urllib3_1.new.1533/python-urllib3_1.changes 2023-05-25 23:52:12.923497797 +0200 @@ -1,0 +2,9 @@ +Tue May 23 20:02:08 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 1.26.16: + * Fixed thread-safety issue where accessing a ``PoolManager`` + with many distinct origins would cause connection pools to + be closed while requests are in progress +- drop support-fixed-ssl-shared_ciphers.patch (obsolete) + +------------------------------------------------------------------- Old: ---- support-fixed-ssl-shared_ciphers.patch urllib3-1.26.15.tar.gz New: ---- urllib3-1.26.16.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-urllib3_1.spec ++++++ --- /var/tmp/diff_new_pack.ZN1Gwg/_old 2023-05-25 23:52:13.435500803 +0200 +++ /var/tmp/diff_new_pack.ZN1Gwg/_new 2023-05-25 23:52:13.439500827 +0200 @@ -26,7 +26,7 @@ %endif %{?sle15_python_module_pythons} Name: python-urllib3_1%{psuffix} -Version: 1.26.15 +Version: 1.26.16 Release: 0 Summary: HTTP library with thread-safe connection pooling, file post, and more License: MIT @@ -36,8 +36,6 @@ # PATCH-FIX-UPSTREAM remove_mock.patch gh#urllib3/urllib3#2108 mc...@suse.com # remove dependency on the external module mock Patch0: remove_mock.patch -# PATCH-FIX-OPENSUSE New Python versions fixed behaviour of ssl.shared_ciphers -Patch1: support-fixed-ssl-shared_ciphers.patch BuildRequires: %{python_module base >= 3.7} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module six} ++++++ remove_mock.patch ++++++ --- /var/tmp/diff_new_pack.ZN1Gwg/_old 2023-05-25 23:52:13.483501085 +0200 +++ /var/tmp/diff_new_pack.ZN1Gwg/_new 2023-05-25 23:52:13.487501108 +0200 @@ -1,7 +1,7 @@ -Index: urllib3-1.26.10/docs/conf.py +Index: urllib3-1.26.16/docs/conf.py =================================================================== ---- urllib3-1.26.10.orig/docs/conf.py -+++ urllib3-1.26.10/docs/conf.py +--- urllib3-1.26.16.orig/docs/conf.py ++++ urllib3-1.26.16/docs/conf.py @@ -14,7 +14,10 @@ sys.path.insert(0, root_path) # Mock some expensive/platform-specific modules so build will work. # (https://read-the-docs.readthedocs.io/en/latest/faq.html#\ @@ -14,10 +14,10 @@ class MockModule(mock.Mock): -Index: urllib3-1.26.10/test/contrib/test_pyopenssl.py +Index: urllib3-1.26.16/test/contrib/test_pyopenssl.py =================================================================== ---- urllib3-1.26.10.orig/test/contrib/test_pyopenssl.py -+++ urllib3-1.26.10/test/contrib/test_pyopenssl.py +--- urllib3-1.26.16.orig/test/contrib/test_pyopenssl.py ++++ urllib3-1.26.16/test/contrib/test_pyopenssl.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- import os @@ -30,10 +30,10 @@ import pytest try: -Index: urllib3-1.26.10/test/contrib/test_pyopenssl_dependencies.py +Index: urllib3-1.26.16/test/contrib/test_pyopenssl_dependencies.py =================================================================== ---- urllib3-1.26.10.orig/test/contrib/test_pyopenssl_dependencies.py -+++ urllib3-1.26.10/test/contrib/test_pyopenssl_dependencies.py +--- urllib3-1.26.16.orig/test/contrib/test_pyopenssl_dependencies.py ++++ urllib3-1.26.16/test/contrib/test_pyopenssl_dependencies.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- import pytest @@ -45,10 +45,10 @@ try: from urllib3.contrib.pyopenssl import extract_from_urllib3, inject_into_urllib3 -Index: urllib3-1.26.10/test/test_connection.py +Index: urllib3-1.26.16/test/test_connection.py =================================================================== ---- urllib3-1.26.10.orig/test/test_connection.py -+++ urllib3-1.26.10/test/test_connection.py +--- urllib3-1.26.16.orig/test/test_connection.py ++++ urllib3-1.26.16/test/test_connection.py @@ -1,6 +1,9 @@ import datetime @@ -60,10 +60,10 @@ import pytest from urllib3.connection import RECENT_DATE, CertificateError, _match_hostname -Index: urllib3-1.26.10/test/test_connectionpool.py +Index: urllib3-1.26.16/test/test_connectionpool.py =================================================================== ---- urllib3-1.26.10.orig/test/test_connectionpool.py -+++ urllib3-1.26.10/test/test_connectionpool.py +--- urllib3-1.26.16.orig/test/test_connectionpool.py ++++ urllib3-1.26.16/test/test_connectionpool.py @@ -6,7 +6,10 @@ from ssl import SSLError as BaseSSLError from test import SHORT_TIMEOUT @@ -76,10 +76,10 @@ from dummyserver.server import DEFAULT_CA from urllib3._collections import HTTPHeaderDict -Index: urllib3-1.26.10/test/test_queue_monkeypatch.py +Index: urllib3-1.26.16/test/test_queue_monkeypatch.py =================================================================== ---- urllib3-1.26.10.orig/test/test_queue_monkeypatch.py -+++ urllib3-1.26.10/test/test_queue_monkeypatch.py +--- urllib3-1.26.16.orig/test/test_queue_monkeypatch.py ++++ urllib3-1.26.16/test/test_queue_monkeypatch.py @@ -1,6 +1,9 @@ from __future__ import absolute_import @@ -91,10 +91,10 @@ import pytest from urllib3 import HTTPConnectionPool -Index: urllib3-1.26.10/test/test_response.py +Index: urllib3-1.26.16/test/test_response.py =================================================================== ---- urllib3-1.26.10.orig/test/test_response.py -+++ urllib3-1.26.10/test/test_response.py +--- urllib3-1.26.16.orig/test/test_response.py ++++ urllib3-1.26.16/test/test_response.py @@ -9,7 +9,10 @@ from base64 import b64decode from io import BufferedReader, BytesIO, TextIOWrapper from test import onlyBrotlipy @@ -107,10 +107,10 @@ import pytest import six -Index: urllib3-1.26.10/test/test_retry.py +Index: urllib3-1.26.16/test/test_retry.py =================================================================== ---- urllib3-1.26.10.orig/test/test_retry.py -+++ urllib3-1.26.10/test/test_retry.py +--- urllib3-1.26.16.orig/test/test_retry.py ++++ urllib3-1.26.16/test/test_retry.py @@ -1,6 +1,9 @@ import warnings @@ -122,10 +122,10 @@ import pytest from urllib3.exceptions import ( -Index: urllib3-1.26.10/test/test_retry_deprecated.py +Index: urllib3-1.26.16/test/test_retry_deprecated.py =================================================================== ---- urllib3-1.26.10.orig/test/test_retry_deprecated.py -+++ urllib3-1.26.10/test/test_retry_deprecated.py +--- urllib3-1.26.16.orig/test/test_retry_deprecated.py ++++ urllib3-1.26.16/test/test_retry_deprecated.py @@ -1,7 +1,10 @@ # This is a copy-paste of test_retry.py with extra asserts about deprecated options. It will be removed for v2. import warnings @@ -138,10 +138,10 @@ import pytest from urllib3.exceptions import ( -Index: urllib3-1.26.10/test/test_ssl.py +Index: urllib3-1.26.16/test/test_ssl.py =================================================================== ---- urllib3-1.26.10.orig/test/test_ssl.py -+++ urllib3-1.26.10/test/test_ssl.py +--- urllib3-1.26.16.orig/test/test_ssl.py ++++ urllib3-1.26.16/test/test_ssl.py @@ -1,6 +1,9 @@ from test import notPyPy2 @@ -153,10 +153,10 @@ import pytest from urllib3.exceptions import SNIMissingWarning -Index: urllib3-1.26.10/test/test_ssltransport.py +Index: urllib3-1.26.16/test/test_ssltransport.py =================================================================== ---- urllib3-1.26.10.orig/test/test_ssltransport.py -+++ urllib3-1.26.10/test/test_ssltransport.py +--- urllib3-1.26.16.orig/test/test_ssltransport.py ++++ urllib3-1.26.16/test/test_ssltransport.py @@ -4,7 +4,10 @@ import socket import ssl import sys @@ -169,10 +169,10 @@ import pytest from dummyserver.server import DEFAULT_CA, DEFAULT_CERTS -Index: urllib3-1.26.10/test/test_util.py +Index: urllib3-1.26.16/test/test_util.py =================================================================== ---- urllib3-1.26.10.orig/test/test_util.py -+++ urllib3-1.26.10/test/test_util.py +--- urllib3-1.26.16.orig/test/test_util.py ++++ urllib3-1.26.16/test/test_util.py @@ -9,7 +9,10 @@ from itertools import chain from test import notBrotlipy, onlyBrotlipy, onlyPy2, onlyPy3 @@ -185,10 +185,10 @@ from urllib3 import add_stderr_logger, disable_warnings, util from urllib3.exceptions import ( -Index: urllib3-1.26.10/test/with_dummyserver/test_connectionpool.py +Index: urllib3-1.26.16/test/with_dummyserver/test_connectionpool.py =================================================================== ---- urllib3-1.26.10.orig/test/with_dummyserver/test_connectionpool.py -+++ urllib3-1.26.10/test/with_dummyserver/test_connectionpool.py +--- urllib3-1.26.16.orig/test/with_dummyserver/test_connectionpool.py ++++ urllib3-1.26.16/test/with_dummyserver/test_connectionpool.py @@ -12,7 +12,10 @@ import warnings from test import LONG_TIMEOUT, SHORT_TIMEOUT, onlyPy2 from threading import Event @@ -201,10 +201,10 @@ import pytest import six -Index: urllib3-1.26.10/test/with_dummyserver/test_https.py +Index: urllib3-1.26.16/test/with_dummyserver/test_https.py =================================================================== ---- urllib3-1.26.10.orig/test/with_dummyserver/test_https.py -+++ urllib3-1.26.10/test/with_dummyserver/test_https.py +--- urllib3-1.26.16.orig/test/with_dummyserver/test_https.py ++++ urllib3-1.26.16/test/with_dummyserver/test_https.py @@ -18,7 +18,10 @@ from test import ( resolvesLocalhostFQDN, ) @@ -217,11 +217,11 @@ import pytest import trustme -Index: urllib3-1.26.10/test/with_dummyserver/test_socketlevel.py +Index: urllib3-1.26.16/test/with_dummyserver/test_socketlevel.py =================================================================== ---- urllib3-1.26.10.orig/test/with_dummyserver/test_socketlevel.py -+++ urllib3-1.26.10/test/with_dummyserver/test_socketlevel.py -@@ -53,7 +53,10 @@ from test import ( +--- urllib3-1.26.16.orig/test/with_dummyserver/test_socketlevel.py ++++ urllib3-1.26.16/test/with_dummyserver/test_socketlevel.py +@@ -54,7 +54,10 @@ from test import ( ) from threading import Event @@ -233,11 +233,11 @@ import pytest import trustme -Index: urllib3-1.26.10/test/test_poolmanager.py +Index: urllib3-1.26.16/test/test_poolmanager.py =================================================================== ---- urllib3-1.26.10.orig/test/test_poolmanager.py -+++ urllib3-1.26.10/test/test_poolmanager.py -@@ -2,7 +2,11 @@ import socket +--- urllib3-1.26.16.orig/test/test_poolmanager.py ++++ urllib3-1.26.16/test/test_poolmanager.py +@@ -3,7 +3,11 @@ import socket from test import resolvesLocalhostFQDN import pytest @@ -249,8 +249,8 @@ + import mock from urllib3 import connection_from_url - from urllib3.exceptions import ClosedPoolError, LocationValueError -@@ -383,7 +387,7 @@ class TestPoolManager(object): + from urllib3.exceptions import LocationValueError +@@ -361,7 +365,7 @@ class TestPoolManager(object): "http://[a::b%25zone]", ], ) ++++++ urllib3-1.26.15.tar.gz -> urllib3-1.26.16.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/CHANGES.rst new/urllib3-1.26.16/CHANGES.rst --- old/urllib3-1.26.15/CHANGES.rst 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/CHANGES.rst 2023-05-23 12:51:05.000000000 +0200 @@ -1,6 +1,13 @@ Changes ======= +1.26.16 (2023-05-23) +-------------------- + +* Fixed thread-safety issue where accessing a ``PoolManager`` with many distinct origins + would cause connection pools to be closed while requests are in progress (`#2954 <https://github.com/urllib3/urllib3/pull/2954>`_) + + 1.26.15 (2023-03-10) -------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/PKG-INFO new/urllib3-1.26.16/PKG-INFO --- old/urllib3-1.26.15/PKG-INFO 2023-03-11 00:57:54.565305700 +0100 +++ new/urllib3-1.26.16/PKG-INFO 2023-05-23 12:51:13.747491400 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: urllib3 -Version: 1.26.15 +Version: 1.26.16 Summary: HTTP library with thread-safe connection pooling, file post, and more. Home-page: https://urllib3.readthedocs.io/ Author: Andrey Petrov @@ -144,6 +144,13 @@ Changes ======= +1.26.16 (2023-05-23) +-------------------- + +* Fixed thread-safety issue where accessing a ``PoolManager`` with many distinct origins + would cause connection pools to be closed while requests are in progress (`#2954 <https://github.com/urllib3/urllib3/pull/2954>`_) + + 1.26.15 (2023-03-10) -------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/docs/v2-roadmap.rst new/urllib3-1.26.16/docs/v2-roadmap.rst --- old/urllib3-1.26.15/docs/v2-roadmap.rst 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/docs/v2-roadmap.rst 2023-05-23 12:51:05.000000000 +0200 @@ -80,15 +80,15 @@ available. -**⨠Optimized for Python 3.6+** +**⨠Optimized for Python 3.7+** -------------------------------- In v2.0 we'll be specifically be targeting -CPython 3.6+ and PyPy 7.0+ (compatible with CPython 3.6) -and dropping support Python versions 2.7 and 3.5. +CPython 3.7+ and PyPy 7.3.10+ (compatible with CPython 3.8) +and dropping support Python versions 2.7 and 3.6. By dropping end-of-life Python versions we're able to optimize -the codebase for Python 3.6+ by using new features to improve +the codebase for Python 3.7+ by using new features to improve performance and reduce the amount of code that needs to be executed in order to support legacy versions. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/src/urllib3/_version.py new/urllib3-1.26.16/src/urllib3/_version.py --- old/urllib3-1.26.15/src/urllib3/_version.py 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/src/urllib3/_version.py 2023-05-23 12:51:05.000000000 +0200 @@ -1,2 +1,2 @@ # This file is protected via CODEOWNERS -__version__ = "1.26.15" +__version__ = "1.26.16" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/src/urllib3/connectionpool.py new/urllib3-1.26.16/src/urllib3/connectionpool.py --- old/urllib3-1.26.15/src/urllib3/connectionpool.py 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/src/urllib3/connectionpool.py 2023-05-23 12:51:05.000000000 +0200 @@ -50,6 +50,13 @@ from .util.url import _normalize_host as normalize_host from .util.url import get_host, parse_url +try: # Platform-specific: Python 3 + import weakref + + weakref_finalize = weakref.finalize +except AttributeError: # Platform-specific: Python 2 + from .packages.backports.weakref_finalize import weakref_finalize + xrange = six.moves.xrange log = logging.getLogger(__name__) @@ -220,6 +227,16 @@ self.conn_kw["proxy"] = self.proxy self.conn_kw["proxy_config"] = self.proxy_config + # Do not pass 'self' as callback to 'finalize'. + # Then the 'finalize' would keep an endless living (leak) to self. + # By just passing a reference to the pool allows the garbage collector + # to free self if nobody else has a reference to it. + pool = self.pool + + # Close all the HTTPConnections in the pool before the + # HTTPConnectionPool object is garbage collected. + weakref_finalize(self, _close_pool_connections, pool) + def _new_conn(self): """ Return a fresh :class:`HTTPConnection`. @@ -489,14 +506,8 @@ # Disable access to the pool old_pool, self.pool = self.pool, None - try: - while True: - conn = old_pool.get(block=False) - if conn: - conn.close() - - except queue.Empty: - pass # Done. + # Close all the HTTPConnections in the pool. + _close_pool_connections(old_pool) def is_same_host(self, url): """ @@ -1108,3 +1119,14 @@ if host.startswith("[") and host.endswith("]"): host = host[1:-1] return host + + +def _close_pool_connections(pool): + """Drains a queue of connections and closes each one.""" + try: + while True: + conn = pool.get(block=False) + if conn: + conn.close() + except queue.Empty: + pass # Done. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/src/urllib3/packages/backports/weakref_finalize.py new/urllib3-1.26.16/src/urllib3/packages/backports/weakref_finalize.py --- old/urllib3-1.26.15/src/urllib3/packages/backports/weakref_finalize.py 1970-01-01 01:00:00.000000000 +0100 +++ new/urllib3-1.26.16/src/urllib3/packages/backports/weakref_finalize.py 2023-05-23 12:51:05.000000000 +0200 @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +""" +backports.weakref_finalize +~~~~~~~~~~~~~~~~~~ + +Backports the Python 3 ``weakref.finalize`` method. +""" +from __future__ import absolute_import + +import itertools +import sys +from weakref import ref + +__all__ = ["weakref_finalize"] + + +class weakref_finalize(object): + """Class for finalization of weakrefable objects + finalize(obj, func, *args, **kwargs) returns a callable finalizer + object which will be called when obj is garbage collected. The + first time the finalizer is called it evaluates func(*arg, **kwargs) + and returns the result. After this the finalizer is dead, and + calling it just returns None. + When the program exits any remaining finalizers for which the + atexit attribute is true will be run in reverse order of creation. + By default atexit is true. + """ + + # Finalizer objects don't have any state of their own. They are + # just used as keys to lookup _Info objects in the registry. This + # ensures that they cannot be part of a ref-cycle. + + __slots__ = () + _registry = {} + _shutdown = False + _index_iter = itertools.count() + _dirty = False + _registered_with_atexit = False + + class _Info(object): + __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index") + + def __init__(self, obj, func, *args, **kwargs): + if not self._registered_with_atexit: + # We may register the exit function more than once because + # of a thread race, but that is harmless + import atexit + + atexit.register(self._exitfunc) + weakref_finalize._registered_with_atexit = True + info = self._Info() + info.weakref = ref(obj, self) + info.func = func + info.args = args + info.kwargs = kwargs or None + info.atexit = True + info.index = next(self._index_iter) + self._registry[self] = info + weakref_finalize._dirty = True + + def __call__(self, _=None): + """If alive then mark as dead and return func(*args, **kwargs); + otherwise return None""" + info = self._registry.pop(self, None) + if info and not self._shutdown: + return info.func(*info.args, **(info.kwargs or {})) + + def detach(self): + """If alive then mark as dead and return (obj, func, args, kwargs); + otherwise return None""" + info = self._registry.get(self) + obj = info and info.weakref() + if obj is not None and self._registry.pop(self, None): + return (obj, info.func, info.args, info.kwargs or {}) + + def peek(self): + """If alive then return (obj, func, args, kwargs); + otherwise return None""" + info = self._registry.get(self) + obj = info and info.weakref() + if obj is not None: + return (obj, info.func, info.args, info.kwargs or {}) + + @property + def alive(self): + """Whether finalizer is alive""" + return self in self._registry + + @property + def atexit(self): + """Whether finalizer should be called at exit""" + info = self._registry.get(self) + return bool(info) and info.atexit + + @atexit.setter + def atexit(self, value): + info = self._registry.get(self) + if info: + info.atexit = bool(value) + + def __repr__(self): + info = self._registry.get(self) + obj = info and info.weakref() + if obj is None: + return "<%s object at %#x; dead>" % (type(self).__name__, id(self)) + else: + return "<%s object at %#x; for %r at %#x>" % ( + type(self).__name__, + id(self), + type(obj).__name__, + id(obj), + ) + + @classmethod + def _select_for_exit(cls): + # Return live finalizers marked for exit, oldest first + L = [(f, i) for (f, i) in cls._registry.items() if i.atexit] + L.sort(key=lambda item: item[1].index) + return [f for (f, i) in L] + + @classmethod + def _exitfunc(cls): + # At shutdown invoke finalizers for which atexit is true. + # This is called once all other non-daemonic threads have been + # joined. + reenable_gc = False + try: + if cls._registry: + import gc + + if gc.isenabled(): + reenable_gc = True + gc.disable() + pending = None + while True: + if pending is None or weakref_finalize._dirty: + pending = cls._select_for_exit() + weakref_finalize._dirty = False + if not pending: + break + f = pending.pop() + try: + # gc is disabled, so (assuming no daemonic + # threads) the following is the only line in + # this function which might trigger creation + # of a new finalizer + f() + except Exception: + sys.excepthook(*sys.exc_info()) + assert f not in cls._registry + finally: + # prevent any more finalizers from executing during shutdown + weakref_finalize._shutdown = True + if reenable_gc: + gc.enable() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/src/urllib3/poolmanager.py new/urllib3-1.26.16/src/urllib3/poolmanager.py --- old/urllib3-1.26.15/src/urllib3/poolmanager.py 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/src/urllib3/poolmanager.py 2023-05-23 12:51:05.000000000 +0200 @@ -171,7 +171,7 @@ def __init__(self, num_pools=10, headers=None, **connection_pool_kw): RequestMethods.__init__(self, headers) self.connection_pool_kw = connection_pool_kw - self.pools = RecentlyUsedContainer(num_pools, dispose_func=lambda p: p.close()) + self.pools = RecentlyUsedContainer(num_pools) # Locally set the pool classes and keys so other PoolManagers can # override them. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/src/urllib3.egg-info/PKG-INFO new/urllib3-1.26.16/src/urllib3.egg-info/PKG-INFO --- old/urllib3-1.26.15/src/urllib3.egg-info/PKG-INFO 2023-03-11 00:57:54.000000000 +0100 +++ new/urllib3-1.26.16/src/urllib3.egg-info/PKG-INFO 2023-05-23 12:51:13.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: urllib3 -Version: 1.26.15 +Version: 1.26.16 Summary: HTTP library with thread-safe connection pooling, file post, and more. Home-page: https://urllib3.readthedocs.io/ Author: Andrey Petrov @@ -144,6 +144,13 @@ Changes ======= +1.26.16 (2023-05-23) +-------------------- + +* Fixed thread-safety issue where accessing a ``PoolManager`` with many distinct origins + would cause connection pools to be closed while requests are in progress (`#2954 <https://github.com/urllib3/urllib3/pull/2954>`_) + + 1.26.15 (2023-03-10) -------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/src/urllib3.egg-info/SOURCES.txt new/urllib3-1.26.16/src/urllib3.egg-info/SOURCES.txt --- old/urllib3-1.26.15/src/urllib3.egg-info/SOURCES.txt 2023-03-11 00:57:54.000000000 +0100 +++ new/urllib3-1.26.16/src/urllib3.egg-info/SOURCES.txt 2023-05-23 12:51:13.000000000 +0200 @@ -77,6 +77,7 @@ src/urllib3/packages/six.py src/urllib3/packages/backports/__init__.py src/urllib3/packages/backports/makefile.py +src/urllib3/packages/backports/weakref_finalize.py src/urllib3/util/__init__.py src/urllib3/util/connection.py src/urllib3/util/proxy.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/test/test_poolmanager.py new/urllib3-1.26.16/test/test_poolmanager.py --- old/urllib3-1.26.15/test/test_poolmanager.py 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/test/test_poolmanager.py 2023-05-23 12:51:05.000000000 +0200 @@ -1,3 +1,4 @@ +import gc import socket from test import resolvesLocalhostFQDN @@ -5,7 +6,7 @@ from mock import patch from urllib3 import connection_from_url -from urllib3.exceptions import ClosedPoolError, LocationValueError +from urllib3.exceptions import LocationValueError from urllib3.poolmanager import PoolKey, PoolManager, key_fn_by_scheme from urllib3.util import retry, timeout @@ -60,24 +61,12 @@ def test_manager_clear(self): p = PoolManager(5) - conn_pool = p.connection_from_url("http://google.com") + p.connection_from_url("http://google.com") assert len(p.pools) == 1 - conn = conn_pool._get_conn() - p.clear() assert len(p.pools) == 0 - with pytest.raises(ClosedPoolError): - conn_pool._get_conn() - - conn_pool._put_conn(conn) - - with pytest.raises(ClosedPoolError): - conn_pool._get_conn() - - assert len(p.pools) == 0 - @pytest.mark.parametrize("url", ["http://@", None]) def test_nohost(self, url): p = PoolManager(5) @@ -86,19 +75,8 @@ def test_contextmanager(self): with PoolManager(1) as p: - conn_pool = p.connection_from_url("http://google.com") + p.connection_from_url("http://google.com") assert len(p.pools) == 1 - conn = conn_pool._get_conn() - - assert len(p.pools) == 0 - - with pytest.raises(ClosedPoolError): - conn_pool._get_conn() - - conn_pool._put_conn(conn) - - with pytest.raises(ClosedPoolError): - conn_pool._get_conn() assert len(p.pools) == 0 @@ -397,3 +375,30 @@ conn.connect() assert create_connection.call_args[0][0] == ("a::b%zone", 80) + + def test_thread_safty(self): + pool_manager = PoolManager(num_pools=2) + + # thread 1 gets a pool for host x + pool_1 = pool_manager.connection_from_url("http://host_x:80/") + + # thread 2 gets a pool for host y + pool_2 = pool_manager.connection_from_url("http://host_y:80/") + + # thread 3 gets a pool for host z + pool_3 = pool_manager.connection_from_url("http://host_z:80") + + # None of the pools should be closed, since all of them are referenced. + assert pool_1.pool is not None + assert pool_2.pool is not None + assert pool_3.pool is not None + + conn_queue = pool_1.pool + assert conn_queue.qsize() > 0 + + # thread 1 stops. + del pool_1 + gc.collect() + + # Connection should be closed, because reference to pool_1 is gone. + assert conn_queue.qsize() == 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/test/test_ssltransport.py new/urllib3-1.26.16/test/test_ssltransport.py --- old/urllib3-1.26.15/test/test_ssltransport.py 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/test/test_ssltransport.py 2023-05-23 12:51:05.000000000 +0200 @@ -202,8 +202,11 @@ assert ssock.selected_npn_protocol() is None shared_ciphers = ssock.shared_ciphers() - assert type(shared_ciphers) == list - assert len(shared_ciphers) > 0 + # SSLContext.shared_ciphers() changed behavior completely in a patch version. + # See: https://github.com/python/cpython/issues/96931 + assert shared_ciphers is None or ( + type(shared_ciphers) is list and len(shared_ciphers) > 0 + ) assert ssock.compression() is None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/urllib3-1.26.15/test/with_dummyserver/test_socketlevel.py new/urllib3-1.26.16/test/with_dummyserver/test_socketlevel.py --- old/urllib3-1.26.15/test/with_dummyserver/test_socketlevel.py 2023-03-11 00:57:45.000000000 +0100 +++ new/urllib3-1.26.16/test/with_dummyserver/test_socketlevel.py 2023-05-23 12:51:05.000000000 +0200 @@ -1221,10 +1221,11 @@ ssl_sock.close() self._start_server(socket_handler) - with HTTPSConnectionPool(self.host, self.port) as pool: - with pytest.raises(MaxRetryError) as cm: - pool.request("GET", "/", retries=0) - assert isinstance(cm.value.reason, SSLError) + with HTTPSConnectionPool(self.host, self.port, ca_certs=DEFAULT_CA) as pool: + with pytest.raises( + SSLError, match=r"(wrong version number|record overflow)" + ): + pool.request("GET", "/", retries=False) @notSecureTransport def test_ssl_read_timeout(self):