summaryrefslogtreecommitdiffstats
path: root/python
diff options
context:
space:
mode:
authorJohn (J5) Palmieri <johnp@redhat.com>2005-05-24 00:21:07 +0000
committerJohn (J5) Palmieri <johnp@redhat.com>2005-05-24 00:21:07 +0000
commite43a353d27f1b9643c21ccff53a7759fc3dcdffe (patch)
treead51cf8b4ec9801fe11e012b7453ddb52c51ab44 /python
parent8af9f11e54317698424bc15ca566e46268646d73 (diff)
* 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.
Diffstat (limited to 'python')
-rw-r--r--python/Makefile.am2
-rw-r--r--python/_dbus.py69
-rw-r--r--python/decorators.py10
-rw-r--r--python/examples/example-signal-emitter.py8
-rw-r--r--python/examples/example-signal-recipient.py4
-rw-r--r--python/matchrules.py170
6 files changed, 211 insertions, 52 deletions
diff --git a/python/Makefile.am b/python/Makefile.am
index 1cd40ee3..23e9cea4 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -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
diff --git a/python/_dbus.py b/python/_dbus.py
index ca7a156a..d408704a 100644
--- a/python/_dbus.py
+++ b/python/_dbus.py
@@ -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):
diff --git a/python/decorators.py b/python/decorators.py
index 4be6e207..7847b3a2 100644
--- a/python/decorators.py
+++ b/python/decorators.py
@@ -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__
diff --git a/python/examples/example-signal-emitter.py b/python/examples/example-signal-emitter.py
index 662c2bc7..428f1440 100644
--- a/python/examples/example-signal-emitter.py
+++ b/python/examples/example-signal-emitter.py
@@ -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)
diff --git a/python/examples/example-signal-recipient.py b/python/examples/example-signal-recipient.py
index 1dbf6a65..2f932a9b 100644
--- a/python/examples/example-signal-recipient.py
+++ b/python/examples/example-signal-recipient.py
@@ -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()
diff --git a/python/matchrules.py b/python/matchrules.py
new file mode 100644
index 00000000..e27c4c8f
--- /dev/null
+++ b/python/matchrules.py
@@ -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