Skip to content

Commit b31f7e2

Browse files
[3.12] gh-120868: Fix breaking change in logging.config when using QueueHandler (GH-120872) (GH-121077)
(cherry picked from commit 7d9c685)
1 parent 8ea6cc1 commit b31f7e2

File tree

3 files changed

+82
-17
lines changed

3 files changed

+82
-17
lines changed

Lib/logging/config.py

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -787,25 +787,44 @@ def configure_handler(self, config):
787787
# if 'handlers' not in config:
788788
# raise ValueError('No handlers specified for a QueueHandler')
789789
if 'queue' in config:
790-
from multiprocessing.queues import Queue as MPQueue
791-
from multiprocessing import Manager as MM
792-
proxy_queue = MM().Queue()
793-
proxy_joinable_queue = MM().JoinableQueue()
794790
qspec = config['queue']
795-
if not isinstance(qspec, (queue.Queue, MPQueue,
796-
type(proxy_queue), type(proxy_joinable_queue))):
797-
if isinstance(qspec, str):
798-
q = self.resolve(qspec)
799-
if not callable(q):
800-
raise TypeError('Invalid queue specifier %r' % qspec)
801-
q = q()
802-
elif isinstance(qspec, dict):
803-
if '()' not in qspec:
804-
raise TypeError('Invalid queue specifier %r' % qspec)
805-
q = self.configure_custom(dict(qspec))
806-
else:
791+
792+
if isinstance(qspec, str):
793+
q = self.resolve(qspec)
794+
if not callable(q):
807795
raise TypeError('Invalid queue specifier %r' % qspec)
808-
config['queue'] = q
796+
config['queue'] = q()
797+
elif isinstance(qspec, dict):
798+
if '()' not in qspec:
799+
raise TypeError('Invalid queue specifier %r' % qspec)
800+
config['queue'] = self.configure_custom(dict(qspec))
801+
else:
802+
from multiprocessing.queues import Queue as MPQueue
803+
804+
if not isinstance(qspec, (queue.Queue, MPQueue)):
805+
# Safely check if 'qspec' is an instance of Manager.Queue
806+
# / Manager.JoinableQueue
807+
808+
from multiprocessing import Manager as MM
809+
from multiprocessing.managers import BaseProxy
810+
811+
# if it's not an instance of BaseProxy, it also can't be
812+
# an instance of Manager.Queue / Manager.JoinableQueue
813+
if isinstance(qspec, BaseProxy):
814+
# Sometimes manager or queue creation might fail
815+
# (e.g. see issue gh-120868). In that case, any
816+
# exception during the creation of these queues will
817+
# propagate up to the caller and be wrapped in a
818+
# `ValueError`, whose cause will indicate the details of
819+
# the failure.
820+
mm = MM()
821+
proxy_queue = mm.Queue()
822+
proxy_joinable_queue = mm.JoinableQueue()
823+
if not isinstance(qspec, (type(proxy_queue), type(proxy_joinable_queue))):
824+
raise TypeError('Invalid queue specifier %r' % qspec)
825+
else:
826+
raise TypeError('Invalid queue specifier %r' % qspec)
827+
809828
if 'listener' in config:
810829
lspec = config['listener']
811830
if isinstance(lspec, type):

Lib/test/test_logging.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import weakref
6161

6262
from http.server import HTTPServer, BaseHTTPRequestHandler
63+
from unittest.mock import patch
6364
from urllib.parse import urlparse, parse_qs
6465
from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
6566
ThreadingTCPServer, StreamRequestHandler)
@@ -3895,6 +3896,50 @@ def test_config_queue_handler(self):
38953896
msg = str(ctx.exception)
38963897
self.assertEqual(msg, "Unable to configure handler 'ah'")
38973898

3899+
@threading_helper.requires_working_threading()
3900+
@support.requires_subprocess()
3901+
@patch("multiprocessing.Manager")
3902+
def test_config_queue_handler_does_not_create_multiprocessing_manager(self, manager):
3903+
# gh-120868
3904+
3905+
from multiprocessing import Queue as MQ
3906+
3907+
q1 = {"()": "queue.Queue", "maxsize": -1}
3908+
q2 = MQ()
3909+
q3 = queue.Queue()
3910+
3911+
for qspec in (q1, q2, q3):
3912+
self.apply_config(
3913+
{
3914+
"version": 1,
3915+
"handlers": {
3916+
"queue_listener": {
3917+
"class": "logging.handlers.QueueHandler",
3918+
"queue": qspec,
3919+
},
3920+
},
3921+
}
3922+
)
3923+
manager.assert_not_called()
3924+
3925+
@patch("multiprocessing.Manager")
3926+
def test_config_queue_handler_invalid_config_does_not_create_multiprocessing_manager(self, manager):
3927+
# gh-120868
3928+
3929+
with self.assertRaises(ValueError):
3930+
self.apply_config(
3931+
{
3932+
"version": 1,
3933+
"handlers": {
3934+
"queue_listener": {
3935+
"class": "logging.handlers.QueueHandler",
3936+
"queue": object(),
3937+
},
3938+
},
3939+
}
3940+
)
3941+
manager.assert_not_called()
3942+
38983943
@support.requires_subprocess()
38993944
def test_multiprocessing_queues(self):
39003945
# See gh-119819

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,7 @@ Hrvoje Nikšić
13041304
Gregory Nofi
13051305
Jesse Noller
13061306
Bill Noon
1307+
Janek Nouvertné
13071308
Stefan Norberg
13081309
Tim Northover
13091310
Joe Norton

0 commit comments

Comments
 (0)