diff options
author | Bénédikt Tran <10796600+picnixz@users.noreply.github.com> | 2024-08-02 11:16:32 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-02 11:16:32 (GMT) |
commit | fb864c76cd5e450e789a7b4095832e118cc49a39 (patch) | |
tree | df02db9e2ebdae479fb26f4f728e4110504d2d2a /Lib/logging | |
parent | addbb73927f55855dfcc62fd47b0018de8a814ed (diff) | |
download | cpython-fb864c76cd5e450e789a7b4095832e118cc49a39.zip cpython-fb864c76cd5e450e789a7b4095832e118cc49a39.tar.gz cpython-fb864c76cd5e450e789a7b4095832e118cc49a39.tar.bz2 |
gh-121723: Relax constraints on queue objects for `logging.handlers.QueueHandler`. (GH-122154)
Diffstat (limited to 'Lib/logging')
-rw-r--r-- | Lib/logging/config.py | 55 |
1 files changed, 29 insertions, 26 deletions
diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 95e129a..3781cb1 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -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'] |