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>
しかし、 (上記の 2 番目の例の割り当てのように)デコレータ関数を手動で呼び出すと、次のように表示されます。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()
、フリー関数をバインドされたメソッドに変換するために使用できることがわかります。しかし、私は逆のことをする必要があるようです(?)