如何手动调用dbus.service.signal装饰器?

时间:2012-12-30 04:21:34

标签: python dbus

我正在尝试使用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()可用于将自由函数转换为绑定方法。但似乎我需要做相反的事情(?)

1 个答案:

答案 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()