[3.13] gh-121723: Relax constraints on queue objects for logging.handlers.QueueHandler. (GH-122154) (GH-122603)

(cherry picked from commit fb864c76cd)
This commit is contained in:
Miss Islington (bot) 2024-08-02 14:07:27 +02:00 committed by GitHub
parent 9f488f9358
commit 56435a88c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 123 additions and 49 deletions

View file

@ -497,6 +497,33 @@ class BaseConfigurator(object):
value = tuple(value)
return value
def _is_queue_like_object(obj):
"""Check that *obj* implements the Queue API."""
if isinstance(obj, queue.Queue):
return True
# defer importing multiprocessing as much as possible
from multiprocessing.queues import Queue as MPQueue
if isinstance(obj, MPQueue):
return True
# Depending on the multiprocessing start context, we cannot create
# a multiprocessing.managers.BaseManager instance 'mm' to get the
# runtime type of mm.Queue() or mm.JoinableQueue() (see gh-119819).
#
# Since we only need an object implementing the Queue API, we only
# do a protocol check, but we do not use typing.runtime_checkable()
# and typing.Protocol to reduce import time (see gh-121723).
#
# Ideally, we would have wanted to simply use strict type checking
# instead of a protocol-based type checking since the latter does
# not check the method signatures.
queue_interface = [
'empty', 'full', 'get', 'get_nowait',
'put', 'put_nowait', 'join', 'qsize',
'task_done',
]
return all(callable(getattr(obj, method, None))
for method in queue_interface)
class DictConfigurator(BaseConfigurator):
"""
Configure logging using a dictionary-like object to describe the
@ -791,32 +818,8 @@ class DictConfigurator(BaseConfigurator):
if '()' not in qspec:
raise TypeError('Invalid queue specifier %r' % qspec)
config['queue'] = self.configure_custom(dict(qspec))
else:
from multiprocessing.queues import Queue as MPQueue
if not isinstance(qspec, (queue.Queue, MPQueue)):
# Safely check if 'qspec' is an instance of Manager.Queue
# / Manager.JoinableQueue
from multiprocessing import Manager as MM
from multiprocessing.managers import BaseProxy
# if it's not an instance of BaseProxy, it also can't be
# an instance of Manager.Queue / Manager.JoinableQueue
if isinstance(qspec, BaseProxy):
# Sometimes manager or queue creation might fail
# (e.g. see issue gh-120868). In that case, any
# exception during the creation of these queues will
# propagate up to the caller and be wrapped in a
# `ValueError`, whose cause will indicate the details of
# the failure.
mm = MM()
proxy_queue = mm.Queue()
proxy_joinable_queue = mm.JoinableQueue()
if not isinstance(qspec, (type(proxy_queue), type(proxy_joinable_queue))):
raise TypeError('Invalid queue specifier %r' % qspec)
else:
raise TypeError('Invalid queue specifier %r' % qspec)
elif not _is_queue_like_object(qspec):
raise TypeError('Invalid queue specifier %r' % qspec)
if 'listener' in config:
lspec = config['listener']