我正在尝试使用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
我认为我可以在定义类之后手动省略@
装饰运算符和补丁事件提供的语法糖,如下所示:
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
这种行为对我来说完全不可思议。我已经阅读了很多关于装饰器的教程,并且认为我对它们理解得很好,但我已经被小时困扰了。如果这个装饰器希望用'raw'函数调用,我如何转换一个对象方法,以便我可以手动调用它?
从this question开始,我看到types.MethodType()
可用于将自由函数转换为绑定方法。但似乎我需要做相反的事情(?)
答案 0 :(得分:1)
我认为通过使用工厂功能来实现您想要的一种方法 - 但是由于我没有安装D-Bus模块,因此未经测试。
问题的根源是你试图在类定义时使用装饰器,这需要在创建该类的实例之前不提供的数据。一种解决方法是在函数内定义类并使用closures,以便在需要时提供数据。请注意,工厂函数返回创建的类的实例,而不是类本身,尽管可以根据需要返回。
import dbus
import dbus.service
import gobject
from dbus.mainloop.glib import DBusGMainLoop
def event_factory(event_name):
class Event(dbus.service.Object):
def __init__(self):
self.busName = dbus.service.BusName('com.acme.EventManager',
bus=dbus.SessionBus())
dbus.service.Object.__init__(self,
self.busName,
'/com/acme/EventManager/'+event_name)
@dbus.service.signal(dbus_interface='com.acme.EventManager.'+event_name,
signature='v')
def emit(self, data):
print "In %s event, got: %s " % (event_name, data)
return Event() # return an instance of the class
if __name__ == "__main__":
DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
loop = gobject.MainLoop()
connect = event_factory('Connect')
disconnect = event_factory('Disconnect')
loop.run()