@@ -2377,16 +2377,22 @@ def __getattr__(self, attribute):
23772377 return getattr (queue , attribute )
23782378
23792379class CustomQueueFakeProtocol (CustomQueueProtocol ):
2380- # An object implementing the Queue API (incorrect signatures).
2380+ # An object implementing the minimial Queue API for
2381+ # the logging module but with incorrect signatures.
2382+ #
23812383 # The object will be considered a valid queue class since we
23822384 # do not check the signatures (only callability of methods)
23832385 # but will NOT be usable in production since a TypeError will
2384- # be raised due to a missing argument.
2385- def empty (self , x ):
2386+ # be raised due to the extra argument in 'put_nowait' .
2387+ def put_nowait (self ):
23862388 pass
23872389
23882390class CustomQueueWrongProtocol (CustomQueueProtocol ):
2389- empty = None
2391+ put_nowait = None
2392+
2393+ class MinimalQueueProtocol :
2394+ def put_nowait (self , x ): pass
2395+ def get (self ): pass
23902396
23912397def queueMaker ():
23922398 return queue .Queue ()
@@ -3946,56 +3952,70 @@ def test_config_queue_handler(self):
39463952 msg = str (ctx .exception )
39473953 self .assertEqual (msg , "Unable to configure handler 'ah'" )
39483954
3955+ def _apply_simple_queue_listener_configuration (self , qspec ):
3956+ self .apply_config ({
3957+ "version" : 1 ,
3958+ "handlers" : {
3959+ "queue_listener" : {
3960+ "class" : "logging.handlers.QueueHandler" ,
3961+ "queue" : qspec ,
3962+ },
3963+ },
3964+ })
3965+
39493966 @threading_helper .requires_working_threading ()
39503967 @support .requires_subprocess ()
39513968 @patch ("multiprocessing.Manager" )
39523969 def test_config_queue_handler_does_not_create_multiprocessing_manager (self , manager ):
3953- # gh-120868, gh-121723
3954-
3955- from multiprocessing import Queue as MQ
3956-
3957- q1 = {"()" : "queue.Queue" , "maxsize" : - 1 }
3958- q2 = MQ ()
3959- q3 = queue .Queue ()
3960- # CustomQueueFakeProtocol passes the checks but will not be usable
3961- # since the signatures are incompatible. Checking the Queue API
3962- # without testing the type of the actual queue is a trade-off
3963- # between usability and the work we need to do in order to safely
3964- # check that the queue object correctly implements the API.
3965- q4 = CustomQueueFakeProtocol ()
3966-
3967- for qspec in (q1 , q2 , q3 , q4 ):
3968- self .apply_config (
3969- {
3970- "version" : 1 ,
3971- "handlers" : {
3972- "queue_listener" : {
3973- "class" : "logging.handlers.QueueHandler" ,
3974- "queue" : qspec ,
3975- },
3976- },
3977- }
3978- )
3979- manager .assert_not_called ()
3970+ # gh-120868, gh-121723, gh-124653
3971+
3972+ for qspec in [
3973+ {"()" : "queue.Queue" , "maxsize" : - 1 },
3974+ queue .Queue (),
3975+ # queue.SimpleQueue does not inherit from queue.Queue
3976+ queue .SimpleQueue (),
3977+ # CustomQueueFakeProtocol passes the checks but will not be usable
3978+ # since the signatures are incompatible. Checking the Queue API
3979+ # without testing the type of the actual queue is a trade-off
3980+ # between usability and the work we need to do in order to safely
3981+ # check that the queue object correctly implements the API.
3982+ CustomQueueFakeProtocol (),
3983+ MinimalQueueProtocol (),
3984+ ]:
3985+ with self .subTest (qspec = qspec ):
3986+ self ._apply_simple_queue_listener_configuration (qspec )
3987+ manager .assert_not_called ()
39803988
39813989 @patch ("multiprocessing.Manager" )
39823990 def test_config_queue_handler_invalid_config_does_not_create_multiprocessing_manager (self , manager ):
39833991 # gh-120868, gh-121723
39843992
39853993 for qspec in [object (), CustomQueueWrongProtocol ()]:
3986- with self .assertRaises (ValueError ):
3987- self .apply_config (
3988- {
3989- "version" : 1 ,
3990- "handlers" : {
3991- "queue_listener" : {
3992- "class" : "logging.handlers.QueueHandler" ,
3993- "queue" : qspec ,
3994- },
3995- },
3996- }
3997- )
3998- manager .assert_not_called ()
3994+ with self .subTest (qspec = qspec ), self .assertRaises (ValueError ):
3995+ self ._apply_simple_queue_listener_configuration (qspec )
3996+ manager .assert_not_called ()
3997+
3998+ @skip_if_tsan_fork
3999+ @support .requires_subprocess ()
4000+ @unittest .skipUnless (support .Py_DEBUG , "requires a debug build for testing"
4001+ " assertions in multiprocessing" )
4002+ def test_config_reject_simple_queue_handler_multiprocessing_context (self ):
4003+ # multiprocessing.SimpleQueue does not implement 'put_nowait'
4004+ # and thus cannot be used as a queue-like object (gh-124653)
4005+
4006+ import multiprocessing
4007+
4008+ if support .MS_WINDOWS :
4009+ start_methods = ['spawn' ]
4010+ else :
4011+ start_methods = ['spawn' , 'fork' , 'forkserver' ]
4012+
4013+ for start_method in start_methods :
4014+ with self .subTest (start_method = start_method ):
4015+ ctx = multiprocessing .get_context (start_method )
4016+ qspec = ctx .SimpleQueue ()
4017+ with self .assertRaises (ValueError ):
4018+ self ._apply_simple_queue_listener_configuration (qspec )
39994019
40004020 @skip_if_tsan_fork
40014021 @support .requires_subprocess ()
0 commit comments