2

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

4

1 回答 1

1

我认为做你想做的事情的一种方法是使用工厂函数——但是以下内容未经测试,因为我没有安装 D-Bus 模块。

问题的根源是您试图在类定义时使用装饰器,该装饰器需要在创建该类的实例之前不提供的数据。一种解决方法是在函数内定义类并使用闭包,以便数据在需要时可用。请注意,工厂函数返回创建的类的实例,而不是类本身,尽管如果需要也可以。

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()
于 2012-12-31T12:35:38.690 回答