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