将 LineReciever 与 Twisted Process 协议结合使用
Using LineReciever with Twisted Process protocols
我正在尝试使用 twisted 来处理由二进制文件生成的数据(它会无限期地将行转储到标准输出)。由于 by data 本质上是行分隔的,所以我试图使用 LineReciever 而不是尝试解析数据。以下是似乎引起麻烦的代码的相关部分:
class ProtocolBareQDAL41xB(ProcessProtocol, LineReceiver):
...
def outReceived(self, data):
print "Got Data:" + repr(data)
self.dataReceived(data)
def lineReceived(self, line):
print "Got Line: " + line
self._process_line(line)
...
此 'works' 用于输出中两行中的第一行。我还不知道它是否只适用于一行,或者它是否适用于除最后一行以外的所有行。结果输出类似于:
$ python BareQDAL41xB.py
Made Connection
<Process pid=16486 status=-1>
Got Data:'No device found!\nMultiple devices found! Please connect only one.\n'
Got Line: No device found!
Got Serial Number : found!
Unhandled Error
Traceback (most recent call last):
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/log.py", line 101, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/log.py", line 84, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/context.py", line 118, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/context.py", line 81, in callWithContext
return func(*args,**kw)
--- <exception caught here> ---
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/posixbase.py", line 597, in _doReadOrWrite
why = selectable.doRead()
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/process.py", line 274, in doRead
return fdesc.readFromFD(self.fd, self.dataReceived)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/fdesc.py", line 94, in readFromFD
callback(output)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/process.py", line 277, in dataReceived
self.proc.childDataReceived(self.name, data)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/process.py", line 931, in childDataReceived
self.proto.childDataReceived(name, data)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/protocol.py", line 604, in childDataReceived
self.outReceived(data)
File "BareQDAL41xB.py", line 104, in outReceived
self.dataReceived(data)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 573, in dataReceived
self.transport.disconnecting):
exceptions.AttributeError: 'Process' object has no attribute 'disconnecting'
processExited, status 0
processEnded, status 0
LineReciever 似乎期待传输实现 disconnecting
。
是否可以将 twisted 的 LineReciever 与 twisted 的 ProcessProtocol 一起使用,或者我应该在我的协议中实现行解析器?
LineReceiver
已经是一个 Protocol
,它实现了与 IProcessProtocol
不同的接口。
幸运的是,最新版本的 Twisted 已经包含了一个适配器,可以满足您的需求——将子进程视为字节流。不要直接调用 spawnProcess
,而是使用 ProcessEndpoint
,并且您可以传递一个常规的 ProtocolFactory
,不涉及 ProcessProtocol
。
但是,正如评论者已经指出的那样,there's a bug here,其中 disconnecting
属性不是 ITransport
的正式部分,而是 LineReceiver
(和 LineOnlyReceiver
) 无论如何都依赖于它,并且因为它不是接口的一部分,所以 ProcessEndpoint
没有实现它。这绝对应该得到解决,但与此同时,我们需要解决它。
幸运的是,Twisted 对包装协议的内置支持 WrappingFactory
已经支持 disconnecting
属性,特别是因为接口规范理论之间存在这种丑陋的差异以及最流行的 ITransport
实现的现实。所以即使是什么都不做的包装器也可以解决这个问题。您可以这样实现:
from zope.interface import implementer
from twisted.internet.interfaces import IStreamClientEndpoint
from twisted.protocols.policies import WrappingFactory
@implementer(IStreamClientEndpoint)
class DisconnectingWorkaroundEndpoint(object):
def __init__(self, endpoint):
self._endpoint = endpoint
def connect(self, protocolFactory):
return self._endpoint.connect(WrappingFactory(protocolFactory))
然后当您构建 ProcessEndpoint
时,执行:
endpoint = DisconnectingWorkaroundEndpoint(ProcessEndpoint(...))
抱歉回复晚了;虽然您可能已经找到了自己的解决方法,但我希望这对有相同问题的其他人有用!
我正在尝试使用 twisted 来处理由二进制文件生成的数据(它会无限期地将行转储到标准输出)。由于 by data 本质上是行分隔的,所以我试图使用 LineReciever 而不是尝试解析数据。以下是似乎引起麻烦的代码的相关部分:
class ProtocolBareQDAL41xB(ProcessProtocol, LineReceiver):
...
def outReceived(self, data):
print "Got Data:" + repr(data)
self.dataReceived(data)
def lineReceived(self, line):
print "Got Line: " + line
self._process_line(line)
...
此 'works' 用于输出中两行中的第一行。我还不知道它是否只适用于一行,或者它是否适用于除最后一行以外的所有行。结果输出类似于:
$ python BareQDAL41xB.py
Made Connection
<Process pid=16486 status=-1>
Got Data:'No device found!\nMultiple devices found! Please connect only one.\n'
Got Line: No device found!
Got Serial Number : found!
Unhandled Error
Traceback (most recent call last):
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/log.py", line 101, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/log.py", line 84, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/context.py", line 118, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/python/context.py", line 81, in callWithContext
return func(*args,**kw)
--- <exception caught here> ---
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/posixbase.py", line 597, in _doReadOrWrite
why = selectable.doRead()
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/process.py", line 274, in doRead
return fdesc.readFromFD(self.fd, self.dataReceived)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/fdesc.py", line 94, in readFromFD
callback(output)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/process.py", line 277, in dataReceived
self.proc.childDataReceived(self.name, data)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/process.py", line 931, in childDataReceived
self.proto.childDataReceived(name, data)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/internet/protocol.py", line 604, in childDataReceived
self.outReceived(data)
File "BareQDAL41xB.py", line 104, in outReceived
self.dataReceived(data)
File "/media/ldata/code/virtualenvs/tendril/local/lib/python2.7/site-packages/twisted/protocols/basic.py", line 573, in dataReceived
self.transport.disconnecting):
exceptions.AttributeError: 'Process' object has no attribute 'disconnecting'
processExited, status 0
processEnded, status 0
LineReciever 似乎期待传输实现 disconnecting
。
是否可以将 twisted 的 LineReciever 与 twisted 的 ProcessProtocol 一起使用,或者我应该在我的协议中实现行解析器?
LineReceiver
已经是一个 Protocol
,它实现了与 IProcessProtocol
不同的接口。
幸运的是,最新版本的 Twisted 已经包含了一个适配器,可以满足您的需求——将子进程视为字节流。不要直接调用 spawnProcess
,而是使用 ProcessEndpoint
,并且您可以传递一个常规的 ProtocolFactory
,不涉及 ProcessProtocol
。
但是,正如评论者已经指出的那样,there's a bug here,其中 disconnecting
属性不是 ITransport
的正式部分,而是 LineReceiver
(和 LineOnlyReceiver
) 无论如何都依赖于它,并且因为它不是接口的一部分,所以 ProcessEndpoint
没有实现它。这绝对应该得到解决,但与此同时,我们需要解决它。
幸运的是,Twisted 对包装协议的内置支持 WrappingFactory
已经支持 disconnecting
属性,特别是因为接口规范理论之间存在这种丑陋的差异以及最流行的 ITransport
实现的现实。所以即使是什么都不做的包装器也可以解决这个问题。您可以这样实现:
from zope.interface import implementer
from twisted.internet.interfaces import IStreamClientEndpoint
from twisted.protocols.policies import WrappingFactory
@implementer(IStreamClientEndpoint)
class DisconnectingWorkaroundEndpoint(object):
def __init__(self, endpoint):
self._endpoint = endpoint
def connect(self, protocolFactory):
return self._endpoint.connect(WrappingFactory(protocolFactory))
然后当您构建 ProcessEndpoint
时,执行:
endpoint = DisconnectingWorkaroundEndpoint(ProcessEndpoint(...))
抱歉回复晚了;虽然您可能已经找到了自己的解决方法,但我希望这对有相同问题的其他人有用!