事件处理的扭曲模式
Twisted Patterns for Event Handling
(也欢迎以更好的形式提供一些协助)
我正在寻找 twisted 来实现一个与 Internet 完全无关的异步软件。我还获得了 Twisted Network Programming Essentials 的副本,这相当令人失望。它,以及几乎所有其他教程,似乎只将 twisted 视为网络客户端,使用内置事件处理程序并默默地使用难以概括的内置胶水代码。
就上下文而言,我的应用程序希望使用串行/pyserial 与硬件设备通信,并使用 Qt 提供图形用户界面。我什至还没有接近开始担心 Qt 反应器(它看起来是又一个蠕虫罐头的承诺)或移植到 windows.
的地步
首先,我使用了一个扭曲的 select 反应器,我在其中添加了一个协议 + FileDescriptor 来处理 udev 事件。到目前为止,我所做的工作是因为 udev 事件触发了协议中的一个函数 (eventReceived)。
以下是协议及其添加到反应堆的方式:
class UdevMonitorListener(Protocol):
def __init__(self, _reactor=None):
if _reactor is not None:
self._reactor = _reactor
else:
self._reactor = reactor
self.subsystem = 'tty'
self.monitor = None
def startListening(self):
logger.info("Starting UdevMonitorListener")
self.monitor = UdevMonitor(self._reactor, self, self.subsystem)
self.monitor.startReading()
def eventReceived(self, action, device):
if device in connected_devices.udev_ports:
if action == u'remove':
connected_devices.remove_by_udev_port(device)
if action == u'add':
if is_device_supported_from_udev_port(device):
if device not in connected_devices.udev_ports:
connected_devices.append_by_udev_port(device)
def init(_reactor=None):
monitor_protocol = UdevMonitorListener(_reactor)
monitor_protocol.startListening()
函数 init()
被 reactor.callWhenRunning()
在 reactor.run()
之前调用。 eventReceived 函数按 FileDescriptor 的预期调用。如果有帮助,我也可以在此处添加该代码。
我想要的是 eventRecieved 在反应器中触发某种事件,其他东西可以对此做出反应。这段代码不应该关心谁在消费它,而那段代码不应该关心是谁在生成它。这些事件相距甚远,而且我似乎找不到能够干净利落地完成此操作的界面。预计事件发生的频率相对较低,但绝不会 'end'。如果要使用延迟,它必须有某种方式 'refreshing' 本身来等待下一个事件。处理此类事件的常用模式是什么?
编辑:
为了后代和其他人的利益,代码的其余部分:
(来自 https://gist.github.com/dpnova/a7830b34e7c465baace7)
class UdevMonitor(FileDescriptor):
"""
File Descriptor for pyudev.Monitor.
@see: U{http://packages.python.org/pyudev/api/monitor.html}.
"""
def __init__(self, _reactor, protocol, subsystem=None):
FileDescriptor.__init__(self, _reactor)
# Set up monitor
context = pyudev.Context()
self.monitor = pyudev.Monitor.from_netlink(context)
if subsystem:
self.monitor.filter_by(subsystem=subsystem)
# Connect protocol
assert isinstance(protocol, UdevMonitorListener)
self.protocol = protocol
self.protocol.makeConnection(self)
def fileno(self):
"""
Return monitor's file descriptor.
"""
return self.monitor.fileno()
def startReading(self):
"""
Start waiting for read availability.
"""
logger.debug("starting udev monitor fd")
self.monitor.start()
FileDescriptor.startReading(self)
def doRead(self):
"""
An event is ready, decode it through Monitor and call our protocol.
"""
logger.debug("udev reports event available")
event = self.monitor.receive_device()
if event:
action, device = event
self.protocol.eventReceived(action, device)
def writeSomeData(self, data):
raise IOError("You can't write to a udev Monitor")
或者包含 connected_devices 和搜索设备的完整模块在 https://gist.github.com/chintal/2511459c02a9767deb5d
What I would like is for eventRecieved to trigger some sort of event in the reactor which something else can react to. This code shouldn't care who's consuming this, and that code shouldn't care who's generating it.
在 Twisted 和使用 Twisted 的应用程序中广泛使用的执行此操作的模式是“进行函数调用”(可能是方法调用)。
reactor 本身并不能构成一个好的单进程消息总线。没有什么动力去尝试把它变成一个,因为函数调用本身在一般情况下工作得很好,当你遇到不那么常见的特殊情况时,它们实际上很难做出好的东西(而且一旦你这样做了,你仍然只为 Twisted 的一小部分可能的观众提供服务。
您的代码已经包含了我正在谈论的那种事情的示例。 UdevMonitor
调用 UdevMonitorListener.eventReceived
当(我猜)udev 系统出现某种事件时。
再重复一遍。为您的 UdevMonitorListener
提供对另一个对象的引用,并在适当的时候对该对象调用一些方法。例如:
class UdevMonitorListener(object):
def __init__(self, device_listener, _reactor=None):
...
self.device_listener = device_listener
def eventReceived(self, ...)
...
self.device_listener.some_device_thing_happened(...)
另请注意,我将基数 class 更改为 object
。 Protocol
对于 IProtocol
实现来说是一个有点方便的基础 class - 但那不是 UdevMonitorListener
是什么,所以 Protocol
不是基础 class.
(也欢迎以更好的形式提供一些协助)
我正在寻找 twisted 来实现一个与 Internet 完全无关的异步软件。我还获得了 Twisted Network Programming Essentials 的副本,这相当令人失望。它,以及几乎所有其他教程,似乎只将 twisted 视为网络客户端,使用内置事件处理程序并默默地使用难以概括的内置胶水代码。
就上下文而言,我的应用程序希望使用串行/pyserial 与硬件设备通信,并使用 Qt 提供图形用户界面。我什至还没有接近开始担心 Qt 反应器(它看起来是又一个蠕虫罐头的承诺)或移植到 windows.
的地步首先,我使用了一个扭曲的 select 反应器,我在其中添加了一个协议 + FileDescriptor 来处理 udev 事件。到目前为止,我所做的工作是因为 udev 事件触发了协议中的一个函数 (eventReceived)。 以下是协议及其添加到反应堆的方式:
class UdevMonitorListener(Protocol):
def __init__(self, _reactor=None):
if _reactor is not None:
self._reactor = _reactor
else:
self._reactor = reactor
self.subsystem = 'tty'
self.monitor = None
def startListening(self):
logger.info("Starting UdevMonitorListener")
self.monitor = UdevMonitor(self._reactor, self, self.subsystem)
self.monitor.startReading()
def eventReceived(self, action, device):
if device in connected_devices.udev_ports:
if action == u'remove':
connected_devices.remove_by_udev_port(device)
if action == u'add':
if is_device_supported_from_udev_port(device):
if device not in connected_devices.udev_ports:
connected_devices.append_by_udev_port(device)
def init(_reactor=None):
monitor_protocol = UdevMonitorListener(_reactor)
monitor_protocol.startListening()
函数 init()
被 reactor.callWhenRunning()
在 reactor.run()
之前调用。 eventReceived 函数按 FileDescriptor 的预期调用。如果有帮助,我也可以在此处添加该代码。
我想要的是 eventRecieved 在反应器中触发某种事件,其他东西可以对此做出反应。这段代码不应该关心谁在消费它,而那段代码不应该关心是谁在生成它。这些事件相距甚远,而且我似乎找不到能够干净利落地完成此操作的界面。预计事件发生的频率相对较低,但绝不会 'end'。如果要使用延迟,它必须有某种方式 'refreshing' 本身来等待下一个事件。处理此类事件的常用模式是什么?
编辑:
为了后代和其他人的利益,代码的其余部分:
(来自 https://gist.github.com/dpnova/a7830b34e7c465baace7)
class UdevMonitor(FileDescriptor):
"""
File Descriptor for pyudev.Monitor.
@see: U{http://packages.python.org/pyudev/api/monitor.html}.
"""
def __init__(self, _reactor, protocol, subsystem=None):
FileDescriptor.__init__(self, _reactor)
# Set up monitor
context = pyudev.Context()
self.monitor = pyudev.Monitor.from_netlink(context)
if subsystem:
self.monitor.filter_by(subsystem=subsystem)
# Connect protocol
assert isinstance(protocol, UdevMonitorListener)
self.protocol = protocol
self.protocol.makeConnection(self)
def fileno(self):
"""
Return monitor's file descriptor.
"""
return self.monitor.fileno()
def startReading(self):
"""
Start waiting for read availability.
"""
logger.debug("starting udev monitor fd")
self.monitor.start()
FileDescriptor.startReading(self)
def doRead(self):
"""
An event is ready, decode it through Monitor and call our protocol.
"""
logger.debug("udev reports event available")
event = self.monitor.receive_device()
if event:
action, device = event
self.protocol.eventReceived(action, device)
def writeSomeData(self, data):
raise IOError("You can't write to a udev Monitor")
或者包含 connected_devices 和搜索设备的完整模块在 https://gist.github.com/chintal/2511459c02a9767deb5d
What I would like is for eventRecieved to trigger some sort of event in the reactor which something else can react to. This code shouldn't care who's consuming this, and that code shouldn't care who's generating it.
在 Twisted 和使用 Twisted 的应用程序中广泛使用的执行此操作的模式是“进行函数调用”(可能是方法调用)。
reactor 本身并不能构成一个好的单进程消息总线。没有什么动力去尝试把它变成一个,因为函数调用本身在一般情况下工作得很好,当你遇到不那么常见的特殊情况时,它们实际上很难做出好的东西(而且一旦你这样做了,你仍然只为 Twisted 的一小部分可能的观众提供服务。
您的代码已经包含了我正在谈论的那种事情的示例。 UdevMonitor
调用 UdevMonitorListener.eventReceived
当(我猜)udev 系统出现某种事件时。
再重复一遍。为您的 UdevMonitorListener
提供对另一个对象的引用,并在适当的时候对该对象调用一些方法。例如:
class UdevMonitorListener(object):
def __init__(self, device_listener, _reactor=None):
...
self.device_listener = device_listener
def eventReceived(self, ...)
...
self.device_listener.some_device_thing_happened(...)
另请注意,我将基数 class 更改为 object
。 Protocol
对于 IProtocol
实现来说是一个有点方便的基础 class - 但那不是 UdevMonitorListener
是什么,所以 Protocol
不是基础 class.