現象
Windows の Visual Studio Code の拡張機能を使って Python を F5 キーでデバッグ実行した時、 ProcessPoolExecutor を使っていると、プログラム終了時に以下のようなエラーが出る。
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.2544.0_x64__*************\Lib\concurrent\futures\process.py", line 310, in weakref_cb
AttributeError: 'NoneType' object has no attribute 'debug'
(*************
のところは、おそらく固有の番号なので隠した。)
ProcessPoolExecutor をwith ProcessPoolExecutor as executor:
といった使い方をしていても出るし、executor.shutdown()
を明示的に呼んでも出る。
考察
AIに聞いても、通常は問題ないとだけ返ってきて釈然としないので、ちょっと考えてみた。
エラーの出た所のソースコードを見に行ったところ、こんな感じになっている。
class _ExecutorManagerThread(threading.Thread):
"""Manages the communication between this process and the worker processes.
The manager is run in a local thread.
Args:
executor: A reference to the ProcessPoolExecutor that owns
this thread. A weakref will be own by the manager as well as
references to internal objects used to introspect the state of
the executor.
"""
def __init__(self, executor):
# Store references to necessary internals of the executor.
# A _ThreadWakeup to allow waking up the queue_manager_thread from the
# main Thread and avoid deadlocks caused by permanently locked queues.
self.thread_wakeup = executor._executor_manager_thread_wakeup
self.shutdown_lock = executor._shutdown_lock
# A weakref.ref to the ProcessPoolExecutor that owns this thread. Used
# to determine if the ProcessPoolExecutor has been garbage collected
# and that the manager can exit.
# When the executor gets garbage collected, the weakref callback
# will wake up the queue management thread so that it can terminate
# if there is no pending work item.
def weakref_cb(_,
thread_wakeup=self.thread_wakeup):
mp.util.debug('Executor collected: triggering callback for'
' QueueManager wakeup')
thread_wakeup.wakeup()
self.executor_reference = weakref.ref(executor, weakref_cb)
# 以下省略
多分ですが、mp.util.debug()
を呼んでいるので、multiprocessing
のユーティリティ関数群のデバッグ関数っぽいので、こんな感じではないかと。
-
VS Codeから実行したときに何らかの 'debug' という属性か何かがセットされ実行される
-
それにより、通常は実行されることのないデバッグ用関数
mp.util.debug()
が実行される。または、インスタンスがある状態で呼びだされるべきものが、ない状態で呼び出される -
意図しない実行により、結果的に、意図しない例外が発生してしまう
なお、mp
は、multiprocessing
の別名のようです。
multiprocessing.util.debug()
はこんな感じ
def debug(msg, *args):
if _logger:
_logger.log(DEBUG, msg, *args, stacklevel=2)
ということは、おそらく、mp.util
のインスタンスが無いかNone
になっているような状態か?