从其他线程触发转换时出现 TypeError
TypeError while triggering transition from other thread
我已经 Controller
在主线程中实例化,产生它自己的工作线程,它正在处理来自其他控制器的事件。 Controller
实例化 ProductionMachine
,这是主要的状态机,它有嵌套的机器 PrepareMachine
和 FlashMachine
.
PrepareMachine
向某些设备发送连接请求并等待通过 Controller
的工作线程收到的响应。当所有设备都连接好后,它将控制权交给FlashMachine
。
到现在为止似乎没问题,但是当我尝试触发转换时 event_data.model.to_done()
我从 self.machine.to_connected()
得到了 TypeError
我应该完成了。你知道我做错了什么吗?
我在 Raspberry Pi.
上使用转换 0.8.9、python 3.7.3
代码:
from transitions.extensions import LockedHierarchicalMachine
from threading import Thread
from time import sleep
import logging as log
class Controller:
def __init__(self):
self.machine = ProductionMachine()
self.worker_thread = Thread(target=self.worker, name="controller")
self.worker_thread.start()
def worker(self):
for i in range(3):
sleep(0.2)
self.machine.to_connected()
class ProductionMachine(LockedHierarchicalMachine):
def __init__(self):
prep = PrepareMachine()
flash = FlashMachine()
states = [
{"name": "prepare", "children": prep, "remap": {"done": "flash"}},
{"name": "flash", "children": flash},
]
super().__init__(states=states, queued=True, send_event=True)
class PrepareMachine(LockedHierarchicalMachine):
def __init__(self):
self.counter = 3
states = [
{"name": "connected", "on_enter": self.entry_connected},
{"name": "done"},
]
super().__init__(states=states, queued=True, send_event=True)
def entry_connected(self, event_data):
self.counter -= 1
if self.counter == 0:
event_data.model.to_done()
class FlashMachine(LockedHierarchicalMachine):
def __init__(self):
states = [
{"name": "initial", "on_enter": self.entry_initial},
{"name": "flashing"},
]
super().__init__(states=states, queued=True, send_event=True)
def entry_initial(self, event_data):
event_data.model.to_flashing()
log.basicConfig(level=log.INFO)
controller = Controller()
controller.machine.to_prepare()
controller.worker_thread.join()
输出:
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "/usr/lib/python3.8/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/examples/hfsm_locked.py", line 16, in worker
self.machine.to_connected()
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/locking.py", line 196, in _locked_method
return func(*args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 854, in trigger_event
res = self._trigger_event(_model, _trigger, None, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 1050, in _trigger_event
tmp = self._trigger_event(_model, _trigger, value, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 1054, in _trigger_event
tmp = self.events[_trigger].trigger(_model, self, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 118, in trigger
return _machine._process(func)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/core.py", line 1200, in _process
self._transition_queue[0]()
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 136, in _trigger
return self._trigger_scoped(_model, _machine, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 153, in _trigger_scoped
state_tree = reduce(dict.get, _machine.get_global_name(join=False), state_tree)
TypeError: descriptor 'get' for 'dict' objects doesn't apply to a 'NoneType' object
您的代码看起来没问题。这显然是 transitions
0.8.9 及之前的错误。这应该在 0.8.10
中修复。不过,我确实对您的机器初始化有一些评论:当您不传递模型参数时,机器会将自己添加为模型。考虑到您的示例,您不需要 PrepareMachine
和 FlashMachine
来执行此操作。您可以使用 FlashMachine(model=None, states=...)
初始化两者,因为您只将 ProductionMachine
用作有状态对象:
from transitions.extensions import LockedHierarchicalMachine
from threading import Thread
from time import sleep
import logging as log
class Controller:
def __init__(self):
self.machine = ProductionMachine()
self.worker_thread = Thread(target=self.worker, name="controller")
self.worker_thread.start()
def worker(self):
for i in range(3):
sleep(0.2)
self.machine.to_connected()
class ProductionMachine(LockedHierarchicalMachine):
def __init__(self):
prep = PrepareMachine()
flash = FlashMachine()
states = [
{"name": "prepare", "children": prep, "remap": {"done": "flash"}},
{"name": "flash", "children": flash},
]
super().__init__(states=states, queued=True, send_event=True)
class PrepareMachine(LockedHierarchicalMachine):
def __init__(self):
self.counter = 3
states = [
{"name": "connected", "on_enter": self.entry_connected},
{"name": "done"},
]
super().__init__(model=None, states=states, queued=True, send_event=True)
def entry_connected(self, event_data):
self.counter -= 1
if self.counter == 0:
event_data.model.to_done()
class FlashMachine(LockedHierarchicalMachine):
def __init__(self):
states = [
{"name": "initial", "on_enter": self.entry_initial},
{"name": "flashing"},
]
super().__init__(model=None, states=states, queued=True, send_event=True)
def entry_initial(self, event_data):
event_data.model.to_flashing()
log.basicConfig(level=log.INFO)
controller = Controller()
controller.machine.to_prepare()
controller.worker_thread.join()
assert controller.machine.is_flash_flashing()
我已经 Controller
在主线程中实例化,产生它自己的工作线程,它正在处理来自其他控制器的事件。 Controller
实例化 ProductionMachine
,这是主要的状态机,它有嵌套的机器 PrepareMachine
和 FlashMachine
.
PrepareMachine
向某些设备发送连接请求并等待通过 Controller
的工作线程收到的响应。当所有设备都连接好后,它将控制权交给FlashMachine
。
到现在为止似乎没问题,但是当我尝试触发转换时 event_data.model.to_done()
我从 self.machine.to_connected()
得到了 TypeError
我应该完成了。你知道我做错了什么吗?
我在 Raspberry Pi.
上使用转换 0.8.9、python 3.7.3代码:
from transitions.extensions import LockedHierarchicalMachine
from threading import Thread
from time import sleep
import logging as log
class Controller:
def __init__(self):
self.machine = ProductionMachine()
self.worker_thread = Thread(target=self.worker, name="controller")
self.worker_thread.start()
def worker(self):
for i in range(3):
sleep(0.2)
self.machine.to_connected()
class ProductionMachine(LockedHierarchicalMachine):
def __init__(self):
prep = PrepareMachine()
flash = FlashMachine()
states = [
{"name": "prepare", "children": prep, "remap": {"done": "flash"}},
{"name": "flash", "children": flash},
]
super().__init__(states=states, queued=True, send_event=True)
class PrepareMachine(LockedHierarchicalMachine):
def __init__(self):
self.counter = 3
states = [
{"name": "connected", "on_enter": self.entry_connected},
{"name": "done"},
]
super().__init__(states=states, queued=True, send_event=True)
def entry_connected(self, event_data):
self.counter -= 1
if self.counter == 0:
event_data.model.to_done()
class FlashMachine(LockedHierarchicalMachine):
def __init__(self):
states = [
{"name": "initial", "on_enter": self.entry_initial},
{"name": "flashing"},
]
super().__init__(states=states, queued=True, send_event=True)
def entry_initial(self, event_data):
event_data.model.to_flashing()
log.basicConfig(level=log.INFO)
controller = Controller()
controller.machine.to_prepare()
controller.worker_thread.join()
输出:
Traceback (most recent call last):
File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
self.run()
File "/usr/lib/python3.8/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/examples/hfsm_locked.py", line 16, in worker
self.machine.to_connected()
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/locking.py", line 196, in _locked_method
return func(*args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 854, in trigger_event
res = self._trigger_event(_model, _trigger, None, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 1050, in _trigger_event
tmp = self._trigger_event(_model, _trigger, value, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 1054, in _trigger_event
tmp = self.events[_trigger].trigger(_model, self, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 118, in trigger
return _machine._process(func)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/core.py", line 1200, in _process
self._transition_queue[0]()
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 136, in _trigger
return self._trigger_scoped(_model, _machine, *args, **kwargs)
File "/home/jankrejci/Drive/projekty/210430_pdx_calib/.venv/lib/python3.8/site-packages/transitions/extensions/nesting.py", line 153, in _trigger_scoped
state_tree = reduce(dict.get, _machine.get_global_name(join=False), state_tree)
TypeError: descriptor 'get' for 'dict' objects doesn't apply to a 'NoneType' object
您的代码看起来没问题。这显然是 transitions
0.8.9 及之前的错误。这应该在 0.8.10
中修复。不过,我确实对您的机器初始化有一些评论:当您不传递模型参数时,机器会将自己添加为模型。考虑到您的示例,您不需要 PrepareMachine
和 FlashMachine
来执行此操作。您可以使用 FlashMachine(model=None, states=...)
初始化两者,因为您只将 ProductionMachine
用作有状态对象:
from transitions.extensions import LockedHierarchicalMachine
from threading import Thread
from time import sleep
import logging as log
class Controller:
def __init__(self):
self.machine = ProductionMachine()
self.worker_thread = Thread(target=self.worker, name="controller")
self.worker_thread.start()
def worker(self):
for i in range(3):
sleep(0.2)
self.machine.to_connected()
class ProductionMachine(LockedHierarchicalMachine):
def __init__(self):
prep = PrepareMachine()
flash = FlashMachine()
states = [
{"name": "prepare", "children": prep, "remap": {"done": "flash"}},
{"name": "flash", "children": flash},
]
super().__init__(states=states, queued=True, send_event=True)
class PrepareMachine(LockedHierarchicalMachine):
def __init__(self):
self.counter = 3
states = [
{"name": "connected", "on_enter": self.entry_connected},
{"name": "done"},
]
super().__init__(model=None, states=states, queued=True, send_event=True)
def entry_connected(self, event_data):
self.counter -= 1
if self.counter == 0:
event_data.model.to_done()
class FlashMachine(LockedHierarchicalMachine):
def __init__(self):
states = [
{"name": "initial", "on_enter": self.entry_initial},
{"name": "flashing"},
]
super().__init__(model=None, states=states, queued=True, send_event=True)
def entry_initial(self, event_data):
event_data.model.to_flashing()
log.basicConfig(level=log.INFO)
controller = Controller()
controller.machine.to_prepare()
controller.worker_thread.join()
assert controller.machine.is_flash_flashing()