Repository navigation
Python 3.12+ breaks backwards compatibility for logging QueueHandler with some Queue classes #119819
Copy link
Copy link
Closed
Labels
stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Bug description:
Related bug: #111615
Related pull: #111638
Related codes:
Lines 789 to 804 in bd0d97c
| if 'queue' in config: | |
| from multiprocessing.queues import Queue as MPQueue | |
| qspec = config['queue'] | |
| if not isinstance(qspec, (queue.Queue, MPQueue)): | |
| if isinstance(qspec, str): | |
| q = self.resolve(qspec) | |
| if not callable(q): | |
| raise TypeError('Invalid queue specifier %r' % qspec) | |
| q = q() | |
| elif isinstance(qspec, dict): | |
| if '()' not in qspec: | |
| raise TypeError('Invalid queue specifier %r' % qspec) | |
| q = self.configure_custom(dict(qspec)) | |
| else: | |
| raise TypeError('Invalid queue specifier %r' % qspec) | |
| config['queue'] = q |
reproducible example using Python 3.12.3
import logging.config
def main(q):
config = {
'version': 1,
'handlers': {
'sink': {
'class': 'logging.handlers.QueueHandler',
'queue': q,
},
},
'root': {
'handlers': ['sink'],
},
}
logging.config.dictConfig(config)
if __name__ == '__main__':
import multiprocessing as mp
main(mp.Manager().Queue())
#import asyncio
#main(asyncio.Queue()) # broken tooerror:
Traceback (most recent call last):
File "/usr/lib/python3.12/logging/config.py", line 581, in configure
handler = self.configure_handler(handlers[name])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/logging/config.py", line 801, in configure_handler
raise TypeError('Invalid queue specifier %r' % qspec)
TypeError: Invalid queue specifier <AutoProxy[Queue] object, typeid 'Queue' at 0x7f8b07189d90>
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/xxx/logging-queue-handler-bug.py", line 33, in <module>
main(mp.Manager().Queue())
File "/home/xxx/logging-queue-handler-bug.py", line 29, in main
logging.config.dictConfig(config)
File "/usr/lib/python3.12/logging/config.py", line 914, in dictConfig
dictConfigClass(config).configure()
File "/usr/lib/python3.12/logging/config.py", line 588, in configure
raise ValueError('Unable to configure handler '
ValueError: Unable to configure handler 'sink'
Queue classes to check for logging QueueHandler
- queue.Queue(),
<queue.Queue at 0x7fb86eeef170>, works. - multiprocessing
- multiprocessing.Queue(),
<multiprocessing.queues.Queue at 0x7fb871790500>, works. - multiprocessing.Manager().Queue()
<AutoProxy[Queue] object, typeid 'Queue' at 0x7fb86ef4a840>, broken.
Its class ismultiprocessing.managers.AutoProxy[Queue], a subclass ofmultiprocessing.managers.BaseProxy.
- multiprocessing.Queue(),
- asyncio.queues.Queue()
<Queue at 0x7fb86f0be4e0 maxsize=0>, broken. - any other Queue?
discuss: how to fix
Check all Queue classes mentioned above in
Line 792 in bd0d97c
| if not isinstance(qspec, (queue.Queue, MPQueue)): |
or just focus on handling special cases: str and dict, while leaving other cases un-checked (e.g., queue.Queue, multiprocessing.queues.Queue).
CPython versions tested on:
3.12
Operating systems tested on:
Linux
Linked PRs
- gh-119819: Fix regression to allow logging configuration with multipr… #120030
- [3.12] gh-119819: Fix regression to allow logging configuration with multipr… (GH-120030) #120034
- [3.13] gh-119819: Fix regression to allow logging configuration with multipr… (GH-120030) #120035
- gh-119819: Update test to skip if _multiprocessing is unavailable. #120067
- [3.12] gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067) #120071
- [3.13] gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067) #120072
- gh-119819: Update logging configuration to support joinable multiproc… #120090
- [3.12] gh-119819: Update logging configuration to support joinable multiproc… (GH-120090) #120092
- [3.13] gh-119819: Update logging configuration to support joinable multiproc… (GH-120090) #120093
- gh-119819: Conditional skip of logging tests that require multiprocessing subprocess support #120476
- [3.13] gh-119819: Conditional skip of logging tests that require multiprocessing subprocess support (GH-120476) #120531
- [3.12] gh-119819: Conditional skip of logging tests that require multiprocessing subprocess support (GH-120476) #120532
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Projects
- StatusShow more project fieldsDone
Activity
fix gdpy3 broken logger with python 3.12.3
picnixz commentedon May 31, 2024
I'm not really sure about this but would it actually make sense to only check for protocol-based queues and not the actual types of queues? strictly speaking, we only need a queue-like interface right? If so, I can work on that one.
shmilee commentedon May 31, 2024
After reading the codes above and this pull request, I tend to not check the queue or queue-like classes, as the main purpose of the code from lines 789 to 804 is to handle the special cases of
isinstance(qspec, str)andisinstance(qspec, dict)and then updateconfig['queue']to be a valid queue instance.Other cases are considered that
config['queue']is already a queue, like an instance ofqueue.Queueor any other Queue type, which is the common use case in the previous version of Python.Perhaps we can ask @vsajip for some advice. Is there anything else we should consider?
vsajip commentedon May 31, 2024
Can you explain the use cases which require
multiprocessing.Manager().Queue()(the AutoProxy)? Also, since logging isn't async, why would you want to pass anasyncio.queues.Queue()?shmilee commentedon Jun 1, 2024
I have only used
multiprocessing.Manager().Queue(). Theasyncio.queues.Queue()mentioned is just a possible use case I imagined after searching the standard library for queues. So I can only talk about the former.Using Python 3.12 on Linux, I change
manager.Queue()(manager is a Manager instance ) tomultiprocessing.Queue(), in my codesloggingalso works. So, currently, it seems that usingmanager.Queue()is not mandatory.The reason for choosing it before is that in previous versions of Python,
multiprocessing.Queue()may cause some errors related to multiprocessing Pool, spawn, and fork things, on Linux or Windows.Sorry for leaking an example code with logging, because tests on Windows, and installing previous versions of Python, may take a lot of time. However, some information about using use a queue created with a manager to avoid some potential issues can be provided below after I searched:
RuntimeError: Queue objects should only be shared between processes through inheritance.vsajip commentedon Jun 1, 2024
Do you have an actual use case where you're running into problems? Or is it just a theoretical exercise? If an actual use case, please give details.
shmilee commentedon Jun 1, 2024
Here is an actual use case,and quickly show the problem:
Then I will get errors from more and more ForkPoolWorker-XXX, XXX is an increasing number.
shmilee commentedon Jun 1, 2024
Using
multiprocessing.Queue()can fix it. see hereI also test this patch on
logging/config.py. The error also disappears.(cannot find class
AutoProxy[Queue]to use in managers module, so just test withBaseProxy)vsajip commentedon Jun 1, 2024
No, that's not the use case I meant. What I meant is, do you have a real problem where you have to use the proxy and can't use
multiprocessing.Queue?shmilee commentedon Jun 1, 2024
No. For me, using logging with
multiprocessing.Queuealso works fine.shmilee commentedon Jun 1, 2024
And I think using the proxy "manager.Queue()" should not be excluded / droped, it was working fine before 3.12.
Even in version 3.12, there is still a way to bypass the existing restrictions (Line-792), using the much more powerful dict qspec configuration. About the
listenerpart, I haven't figured out how to set up a "manager.Queue()" together withhandleryet.Output:
vsajip commentedon Jun 2, 2024
Why should it not be excluded, since
multiprocessing.Queue ()is available to be used? You can't check the class easily asAutoProxycould be used to proxy other things, not just queues.32 remaining items
[3.13] gh-119819: Conditional skip of logging tests that require mult…
[3.12] gh-119819: Conditional skip of logging tests that require mult…
pythongh-119819: Conditional skip of logging tests that require multi…
pythongh-119819: Fix regression to allow logging configuration with m…
pythongh-119819: Update test to skip if _multiprocessing is unavailab…
pythongh-119819: Update logging configuration to support joinable mul…
pythongh-119819: Conditional skip of logging tests that require multi…
noreentry commentedon Jul 13, 2024
multiprocessing.Pool with spawn method breaks logging.handlers.QueueHandler configuration
got exception (when q = manager.Queue() or q = multiprocessing.Queue())
vsajip commentedon Jul 13, 2024
Not sure this is the same error - what exact version of 3.12 is in use? What happens with 3.13?
noreentry commentedon Jul 13, 2024
it works on 3.12.3
doesn't work on 3.12.4
can't test it on 3.13
pythongh-119819: Fix regression to allow logging configuration with m…
pythongh-119819: Update test to skip if _multiprocessing is unavailab…
pythongh-119819: Update logging configuration to support joinable mul…
pythongh-119819: Conditional skip of logging tests that require multi…
[3.12] pythongh-119819: Conditional skip of logging tests that requir…