mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-01-03 17:20:13 +01:00
* python/decorators.py: import dbus_bindings
* python/matchrules.py (SignalMatchRule, SignalMatchTree, SignalMatchNode): new classes that implement wildcard signal callback matching using a tree lookup. Heavily modified from a patch sent by Celso Pinto (fd.o bug #3241) * _dbus.py (add_signal_receiver, remove_signal_receiver, _signal_func): use new match classes to handle signals.
This commit is contained in:
parent
8af9f11e54
commit
e43a353d27
7 changed files with 223 additions and 52 deletions
12
ChangeLog
12
ChangeLog
|
|
@ -1,3 +1,15 @@
|
|||
2005-05-23 John (J5) Palmieri <johnp@redhat.com>
|
||||
|
||||
* python/decorators.py: import dbus_bindings
|
||||
|
||||
* python/matchrules.py (SignalMatchRule, SignalMatchTree,
|
||||
SignalMatchNode): new classes that implement wildcard signal
|
||||
callback matching using a tree lookup. Heavily modified from a
|
||||
patch sent by Celso Pinto (fd.o bug #3241)
|
||||
|
||||
* _dbus.py (add_signal_receiver, remove_signal_receiver, _signal_func):
|
||||
use new match classes to handle signals.
|
||||
|
||||
2005-05-19 John (J5) Palmieri <johnp@redhat.com>
|
||||
|
||||
* python/dbus_bindings.pyx.in: s/TYPE_PATH/TYPE_OBJECT_PATH
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ SUBDIRS=examples
|
|||
INCLUDES=-I$(top_builddir) -I$(top_builddir)/dbus $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) $(PYTHON_INCLUDES)
|
||||
|
||||
dbusdir = $(pythondir)/dbus
|
||||
dbus_PYTHON = __init__.py _dbus.py decorators.py exceptions.py services.py proxies.py _util.py types.py
|
||||
dbus_PYTHON = __init__.py _dbus.py decorators.py exceptions.py services.py proxies.py _util.py types.py matchrules.py
|
||||
|
||||
dbusbindingsdir = $(pyexecdir)/dbus
|
||||
dbusbindings_LTLIBRARIES = dbus_bindings.la
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ from decorators import *
|
|||
from proxies import *
|
||||
from exceptions import *
|
||||
from services import *
|
||||
from matchrules import *
|
||||
|
||||
import re
|
||||
import inspect
|
||||
|
|
@ -79,7 +80,7 @@ class Bus:
|
|||
self._connection = dbus_bindings.bus_get(bus_type)
|
||||
|
||||
self._connection.add_filter(self._signal_func)
|
||||
self._match_rule_to_receivers = { }
|
||||
self._match_rule_tree = SignalMatchTree()
|
||||
if (glib_mainloop):
|
||||
self._connection.setup_with_g_main()
|
||||
|
||||
|
|
@ -111,61 +112,47 @@ class Bus:
|
|||
return self.ProxyObjectClass(self, named_service, object_path)
|
||||
|
||||
def add_signal_receiver(self, handler_function, signal_name=None, dbus_interface=None, named_service=None, path=None):
|
||||
match_rule = self._get_match_rule(signal_name, dbus_interface, named_service, path)
|
||||
if (named_service and named_service[0] != ':'):
|
||||
bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
||||
named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
|
||||
|
||||
match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
|
||||
match_rule.add_handler(handler_function)
|
||||
|
||||
if (not self._match_rule_to_receivers.has_key(match_rule)):
|
||||
self._match_rule_to_receivers[match_rule] = [handler_function]
|
||||
else:
|
||||
self._match_rule_to_receivers[match_rule].append(handler_function)
|
||||
self._match_rule_tree.add(match_rule)
|
||||
|
||||
dbus_bindings.bus_add_match(self._connection, match_rule)
|
||||
dbus_bindings.bus_add_match(self._connection, str(match_rule))
|
||||
|
||||
def remove_signal_receiver(self, handler_function, signal_name=None, dbus_interface=None, named_service=None, path=None):
|
||||
match_rule = self._get_match_rule(signal_name, dbus_interface, named_service, path)
|
||||
|
||||
if self._match_rule_to_receivers.has_key(match_rule):
|
||||
if self._match_rule_to_receivers[match_rule].__contains__(handler_function):
|
||||
self._match_rule_to_receivers[match_rule].pop(handler_function)
|
||||
dbus_bindings.bus_remove_match(self._connection, match_rule)
|
||||
if (named_service and named_service[0] != ':'):
|
||||
bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
||||
named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
|
||||
|
||||
match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
|
||||
match_rule.add_handler(handler_function)
|
||||
|
||||
self._match_rule_tree.remove(match_rule)
|
||||
|
||||
#TODO we leak match rules in the lower level bindings. We need to ref count them
|
||||
|
||||
def get_unix_user(self, named_service):
|
||||
"""Get the unix user for the given named_service on this Bus"""
|
||||
return dbus_bindings.bus_get_unix_user(self._connection, named_service)
|
||||
|
||||
#TODO: Rethink match rules. Right now matches have to be exact.
|
||||
def _get_match_rule(self, signal_name, dbus_interface, named_service, path):
|
||||
match_rule = "type='signal'"
|
||||
if (dbus_interface):
|
||||
match_rule = match_rule + ",interface='%s'" % (dbus_interface)
|
||||
if (named_service):
|
||||
if (named_service[0] != ':' and named_service != "org.freedesktop.DBus"):
|
||||
bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
||||
named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
|
||||
|
||||
match_rule = match_rule + ",sender='%s'" % (named_service)
|
||||
if (path):
|
||||
match_rule = match_rule + ",path='%s'" % (path)
|
||||
if (signal_name):
|
||||
match_rule = match_rule + ",member='%s'" % (signal_name)
|
||||
return match_rule
|
||||
|
||||
def _signal_func(self, connection, message):
|
||||
if (message.get_type() != dbus_bindings.MESSAGE_TYPE_SIGNAL):
|
||||
return dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
|
||||
|
||||
dbus_interface = message.get_interface()
|
||||
named_service = message.get_sender()
|
||||
path = message.get_path()
|
||||
signal_name = message.get_member()
|
||||
named_service = message.get_sender()
|
||||
path = message.get_path()
|
||||
signal_name = message.get_member()
|
||||
|
||||
match_rule = self._get_match_rule(signal_name, dbus_interface, named_service, path)
|
||||
match_rule = SignalMatchRule(signal_name, dbus_interface, named_service, path)
|
||||
|
||||
if (self._match_rule_to_receivers.has_key(match_rule)):
|
||||
receivers = self._match_rule_to_receivers[match_rule]
|
||||
args = message.get_args_list()
|
||||
|
||||
for receiver in receivers:
|
||||
args = message.get_args_list()
|
||||
receiver(*args)
|
||||
self._match_rule_tree.exec_matches(match_rule, *args)
|
||||
|
||||
def start_service_by_name(self, named_service):
|
||||
return dbus_bindings.bus_start_service_by_name(self._connection, named_service)
|
||||
|
|
@ -203,8 +190,8 @@ class Interface:
|
|||
|
||||
def connect_to_signal(self, signal_name, handler_function, dbus_interface = None):
|
||||
if not dbus_interface:
|
||||
dbus_interface = self._dbus_interface
|
||||
|
||||
dbus_interface = self._dbus_interface
|
||||
|
||||
self._obj.connect_to_signal(signal_name, handler_function, dbus_interface)
|
||||
|
||||
def __getattr__(self, member, **keywords):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import _util
|
||||
import inspect
|
||||
import dbus_bindings
|
||||
|
||||
|
||||
def method(dbus_interface):
|
||||
|
|
@ -18,14 +19,15 @@ def signal(dbus_interface):
|
|||
_util._validate_interface_or_name(dbus_interface)
|
||||
def decorator(func):
|
||||
def emit_signal(self, *args, **keywords):
|
||||
func(self, *args, **keywords)
|
||||
message = dbus_bindings.Signal(self._object_path, dbus_interface, func.__name__)
|
||||
func(self, *args, **keywords)
|
||||
message = dbus_bindings.Signal(self._object_path, dbus_interface, func.__name__)
|
||||
iter = message.get_iter(True)
|
||||
|
||||
for arg in args:
|
||||
iter.append(arg)
|
||||
|
||||
|
||||
self._connection.send(message)
|
||||
|
||||
|
||||
emit_signal._dbus_is_signal = True
|
||||
emit_signal._dbus_interface = dbus_interface
|
||||
emit_signal.__name__ = func.__name__
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ class TestObject(dbus.Object):
|
|||
@dbus.signal('org.designfu.TestService')
|
||||
def HelloSignal(self, message):
|
||||
# The signal is emitted when this method exits
|
||||
# You can have code here if you wish
|
||||
# You can have code here if you wish
|
||||
pass
|
||||
|
||||
@dbus.method('org.designfu.TestService')
|
||||
def emitHelloSignal(self):
|
||||
#you emit signals by calling the signal's skeleton method
|
||||
self.HelloSignal("Hello")
|
||||
return "Signal emitted"
|
||||
|
||||
self.HelloSignal("Hello")
|
||||
return "Signal emitted"
|
||||
|
||||
session_bus = dbus.SessionBus()
|
||||
service = dbus.Service("org.designfu.TestService", bus=session_bus)
|
||||
object = TestObject(service)
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ def handle_error(e):
|
|||
|
||||
def emit_signal():
|
||||
#call the emitHelloSignal method async
|
||||
object.emitHelloSignal(dbus_interface="org.designfu.TestService",
|
||||
reply_handler = handle_reply, error_handler = handle_error)
|
||||
object.emitHelloSignal(dbus_interface="org.designfu.TestService")
|
||||
#reply_handler = handle_reply, error_handler = handle_error)
|
||||
return True
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
|
|
|
|||
170
python/matchrules.py
Normal file
170
python/matchrules.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
from exceptions import *
|
||||
|
||||
class SignalMatchNode:
|
||||
def __init__(self):
|
||||
self.wildcard = None
|
||||
self.finite = {}
|
||||
self.rules = []
|
||||
|
||||
def add(self, key, leaf=None):
|
||||
node = None
|
||||
|
||||
if key:
|
||||
if self.finite.has_key(key):
|
||||
node = self.finite[key]
|
||||
else:
|
||||
node = SignalMatchNode()
|
||||
self.finite[key] = node
|
||||
else:
|
||||
if self.wildcard:
|
||||
node = self.wildcard
|
||||
else:
|
||||
node = SignalMatchNode()
|
||||
self.wildcard = node
|
||||
|
||||
node.rules.append(leaf)
|
||||
return node
|
||||
|
||||
def get_matches(self, key):
|
||||
result = []
|
||||
if self.wildcard:
|
||||
result.append(self.wildcard)
|
||||
|
||||
if self.finite.has_key(key):
|
||||
result.append(self.finite[key])
|
||||
|
||||
return result
|
||||
|
||||
def get_match(self, key):
|
||||
if key:
|
||||
if self.finite.has_key(key):
|
||||
return self.finite[key]
|
||||
else:
|
||||
return None
|
||||
|
||||
return self.wildcard
|
||||
|
||||
def has_children(self):
|
||||
if self.wildcard or len(self.finite.iterkeys()) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_child(self, child, key=None):
|
||||
if self.wildcard == child:
|
||||
self.wildcard = None
|
||||
elif self.finite.had_key(key):
|
||||
del self.finite[key]
|
||||
|
||||
class SignalMatchTree:
|
||||
"""This class creates an ordered tree of SignalMatchRules
|
||||
to speed searchs. Left branches are wildcard elements
|
||||
and all other branches are concreet elements.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._tree = SignalMatchNode()
|
||||
|
||||
def add(self, rule):
|
||||
interface = self._tree.add(rule.sender)
|
||||
signal = interface.add(rule.dbus_interface)
|
||||
path = signal.add(rule.signal_name)
|
||||
path.add(rule.path, leaf=rule)
|
||||
|
||||
def exec_matches(self, match_rule, *args):
|
||||
sender_matches = self._tree.get_matches(match_rule.sender)
|
||||
for sender_node in sender_matches:
|
||||
interface_matches = sender_node.get_matches(match_rule.dbus_interface)
|
||||
for interface_node in interface_matches:
|
||||
signal_matches = interface_node.get_matches(match_rule.signal_name)
|
||||
for signal_node in signal_matches:
|
||||
path_matches = signal_node.get_matches(match_rule.path)
|
||||
for path_node in path_matches:
|
||||
if(path_node.rules):
|
||||
for rule in path_node.rules:
|
||||
rule.execute(*args)
|
||||
|
||||
def remove(self, rule):
|
||||
try:
|
||||
sender = self._tree.get_match(rule.sender)
|
||||
interface = sender.get_match(rule.dbus_interface)
|
||||
signal = interface.get_match(rule.signal_name)
|
||||
path = signal.get_match(rule.path)
|
||||
|
||||
rule_matches = []
|
||||
for _rule in path.rules:
|
||||
if _rule.is_match(rule):
|
||||
rule_matches.append(_rule)
|
||||
|
||||
for _rule in rule_matches:
|
||||
path.rules.remove(_rule)
|
||||
|
||||
#clean up tree
|
||||
if len(path.rules) == 0:
|
||||
signal.remove_child(path, key = rule.path)
|
||||
if not signal.has_children():
|
||||
interface.remove_child(signal, key = rule.signal_name)
|
||||
if not interface.has_children():
|
||||
sender.remove_child(interface, key = rule.dbus_interface)
|
||||
if not sender.has_children():
|
||||
self._tree.remove_child(sender, key = rule.sender)
|
||||
|
||||
except:
|
||||
raise DBusException ("Trying to remove unkown rule: %s"%str(rule))
|
||||
|
||||
class SignalMatchRule:
|
||||
"""This class represents a dbus rule used to filter signals.
|
||||
When a rule matches a filter, the signal is propagated to the handler_funtions
|
||||
"""
|
||||
def __init__(self, signal_name, dbus_interface, sender, path):
|
||||
self.handler_functions = []
|
||||
|
||||
self.signal_name = signal_name
|
||||
self.dbus_interface = dbus_interface
|
||||
self.sender = sender
|
||||
self.path = path
|
||||
|
||||
def execute(self, *args):
|
||||
for handler in self.handler_functions:
|
||||
handler(*args)
|
||||
|
||||
def add_handler(self, handler):
|
||||
self.handler_functions.append(handler)
|
||||
|
||||
def is_match(self, rule):
|
||||
if (self.signal_name == rule.signal_name and
|
||||
self.dbus_interface == rule.dbus_interface and
|
||||
self.sender == rule.sender and
|
||||
self.path == rule.path):
|
||||
_funcs_copy_a = self.dbus.handler_functions[0:]
|
||||
_funcs_copy_b = rule.dbus.handler_functions[0:]
|
||||
_funcs_copy_a.sort()
|
||||
_funcs_copy_b.sort()
|
||||
return a == b
|
||||
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
"""Returns a custom representation of this DBusMatchRule that can
|
||||
be used with dbus_bindings
|
||||
"""
|
||||
repr = "type='signal'"
|
||||
if (self.dbus_interface):
|
||||
repr = repr + ",interface='%s'" % (self.dbus_interface)
|
||||
else:
|
||||
repr = repr + ",interface='*'"
|
||||
|
||||
if (self.sender):
|
||||
repr = repr + ",sender='%s'" % (self.sender)
|
||||
else:
|
||||
repr = repr + ",sender='*'"
|
||||
|
||||
if (self.path):
|
||||
repr = repr + ",path='%s'" % (self.path)
|
||||
else:
|
||||
repr = repr + ",path='*'"
|
||||
|
||||
if (self.signal_name):
|
||||
repr = repr + ",member='%s'" % (self.signal_name)
|
||||
else:
|
||||
repr = repr + ",member='*'"
|
||||
|
||||
return repr
|
||||
Loading…
Add table
Reference in a new issue