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

@次のように、クラスが定義された後に、装飾演算子によって提供される構文糖衣を省き、イベントに手動でパッチを当てることができると考えました。

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()、フリー関数をバインドされたメソッドに変換するために使用できることがわかります。しかし、私は逆のことをする必要があるようです(?)

4

1 に答える 1

1

あなたが望むことを行う1つの方法は、ファクトリ関数を使用することだと思いますが、D-Busモジュールがインストールされていないため、以下はテストされていません.

問題の根本は、そのクラスのインスタンスが作成されるまで提供されないデータを必要とするクラス定義時にデコレータを使用しようとしていることです。そのための 1 つの回避策は、関数内でクラスを定義し、クロージャを使用して、必要なときにデータを利用できるようにすることです。factory 関数は、必要に応じてクラス自体ではなく、作成されたクラスのインスタンスを返すことに注意してください。

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 に答える