我正在尝试使用 dbus-python 将信号动态添加到 D-Bus 服务。它为此提供了一个装饰器,如果信号名称在模块加载时已知,则可以正常工作;但是,直到运行时我才知道要导出到 D-Bus 的名称。
为了说明这个问题,我想做的是道德上的等价物:
import dbus
import dbus.service
import gobject
from dbus.mainloop.glib import DBusGMainLoop
class Event(dbus.service.Object):
def __init__(self, name):
self.name = name
self.busName = dbus.service.BusName('com.acme.EventManager',
bus=dbus.SessionBus())
dbus.service.Object.__init__(self,
self.busName,
'/com/acme/EventManager/' +
self.name)
self.signame = 'com.acme.EventManager.' + self.name
# THIS DOES NOT WORK: this decorator is parsed before the Event
# class, and 'self' wouldn't exist here, anyway...
@dbus.service.signal(dbus_interface=self.signame, signature='v')
def emit(self, data):
print "In %s event, got: %s " % (self.name, data)
if __name__ == "__main__":
DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
loop = gobject.MainLoop()
connect = Event('Connect')
disconnect = Event('Disconnect')
loop.run()
毫不奇怪,这会产生:
@dbus.service.signal(dbus_interface=self.signame, signature='v')
NameError: name 'self' is not defined
我想我可以省去装饰运算符提供的语法糖,@
并在定义类之后手动修补 Event,如下所示:
import dbus
import dbus.service
import gobject
from dbus.mainloop.glib import DBusGMainLoop
class Event(dbus.service.Object):
def __init__(self, name):
self.name = name
self.busName = dbus.service.BusName('com.acme.EventManager',
bus=dbus.SessionBus())
dbus.service.Object.__init__(self,
self.busName,
'/com/acme/EventManager/' +
self.name)
self.signame = 'com.acme.EventManager.' + self.name
def emit(self, data):
print "In %s event, got: %s " % (self.name, data)
if __name__ == "__main__":
DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
loop = gobject.MainLoop()
e1 = Event('Connect')
e1.emit = dbus.service.signal(dbus_interface=e1.signame,
signature='v')(e1.emit)
loop.run()
这运行没有错误,但它无法将信号导出到 D-Bus。当我运行 D-Feet 时,我看到了对象路径/com/acme/EventManager/Connect
,但除了Introspect()
.
为了了解我是否可以了解正在发生的事情,我dbus.service.signal
在调试器中检查了传递给装饰器的函数的值。对于这个典型的用例:
@dbus.service.signal(dbus_interface='com.acme.foo', signature='v')
def emit(self, data):
pass
传入装饰器的函数(在变量中func
)如下所示:
>>> func
>>> <function emit at 0x99fbed4>
但是当我手动调用装饰器函数时(如e1.emit =
上面第二个示例中的赋值),我看到:
>>> func
>>> <bound method Event.emit of <__main__.Event at /com/acme/EventManager/Connect at 0x9b3348c>>
所以......似乎,对于正常的用例,dbus.service.signal
期望接收一个自由函数——不是一个未绑定的函数,而是一个函数,从所有意图和目的来看,它看起来像是使用以下定义的:
def emit():
pass
这种行为对我来说完全是神秘的。我已经阅读了很多关于装饰器的教程,并且认为我对它们非常了解,但是我已经为此困扰了好几个小时。如果这个装饰器希望用“原始”函数调用,我如何转换一个对象方法以便我可以手动调用它?
从这个问题,我看到types.MethodType()
可以用来将自由函数转换为绑定方法。但似乎我需要做相反的事情(?)