_dbus_bindings: Expose name-validation functions to Python code.
[dbus-python-phuang.git] / dbus / matchrules.py
blobc5c0b1b42dda041767444e395e335e4a6d1107ec
1 from dbus.exceptions import DBusException
3 class SignalMatchNode:
4 def __init__(self):
5 self.wildcard = None
6 self.finite = {}
7 self.rules = []
9 def add(self, key, leaf=None):
10 node = None
12 if key:
13 if self.finite.has_key(key):
14 node = self.finite[key]
15 else:
16 node = SignalMatchNode()
17 self.finite[key] = node
18 else:
19 if self.wildcard:
20 node = self.wildcard
21 else:
22 node = SignalMatchNode()
23 self.wildcard = node
25 node.rules.append(leaf)
26 return node
28 def get_matches(self, key):
29 result = []
30 if self.wildcard:
31 result.append(self.wildcard)
33 if self.finite.has_key(key):
34 result.append(self.finite[key])
36 return result
38 def get_match(self, key):
39 if key:
40 if self.finite.has_key(key):
41 return self.finite[key]
42 else:
43 return None
45 return self.wildcard
47 def has_children(self):
48 if self.wildcard or len(self.finite.iterkeys()) > 0:
49 return True
50 return False
52 def remove_child(self, child, key=None):
53 if self.wildcard == child:
54 self.wildcard = None
55 elif self.finite.has_key(key):
56 del self.finite[key]
58 class SignalMatchTree:
59 """This class creates an ordered tree of SignalMatchRules
60 to speed searchs. Left branches are wildcard elements
61 and all other branches are concreet elements.
62 """
63 def __init__(self):
64 self._tree = SignalMatchNode()
66 def add(self, rule):
67 interface = self._tree.add(rule.sender)
68 signal = interface.add(rule.dbus_interface)
69 path = signal.add(rule.signal_name)
70 path.add(rule.path, leaf=rule)
72 def exec_matches(self, match_rule, message):
73 args = message.get_args_list()
75 sender_matches = self._tree.get_matches(match_rule.sender)
76 for sender_node in sender_matches:
77 interface_matches = sender_node.get_matches(match_rule.dbus_interface)
78 for interface_node in interface_matches:
79 signal_matches = interface_node.get_matches(match_rule.signal_name)
80 for signal_node in signal_matches:
81 path_matches = signal_node.get_matches(match_rule.path)
82 for path_node in path_matches:
83 if(path_node.rules):
84 for rule in path_node.rules:
85 if (rule.match_args_from_list(args)):
86 rule.execute(message, args)
88 def remove(self, rule):
89 try:
90 sender = self._tree.get_match(rule.sender)
91 interface = sender.get_match(rule.dbus_interface)
92 signal = interface.get_match(rule.signal_name)
93 path = signal.get_match(rule.path)
95 rule_matches = []
96 for _rule in path.rules:
97 if _rule.is_match(rule):
98 rule_matches.append(_rule)
100 for _rule in rule_matches:
101 path.rules.remove(_rule)
103 #clean up tree
104 if len(path.rules) == 0:
105 signal.remove_child(path, key = rule.path)
106 if not signal.has_children():
107 interface.remove_child(signal, key = rule.signal_name)
108 if not interface.has_children():
109 sender.remove_child(interface, key = rule.dbus_interface)
110 if not sender.has_children():
111 self._tree.remove_child(sender, key = rule.sender)
113 except:
114 raise DBusException ("Trying to remove unkown rule: %s"%str(rule))
116 class SignalMatchRule:
117 """This class represents a dbus rule used to filter signals.
118 When a rule matches a filter, the signal is propagated to the handler_funtions
120 def __init__(self, signal_name, dbus_interface, sender, path):
121 self.handler_functions = []
123 self.signal_name = signal_name
124 self.dbus_interface = dbus_interface
125 self.sender = sender
126 self.path = path
127 self.args = None
129 def add_args_match(self, args):
130 self.args = args
132 def execute(self, message, args=None):
133 keywords = {}
135 if self.sender_keyword is not None:
136 keywords[self.sender_keyword] = message.get_sender()
137 if self.path_keyword is not None:
138 keywords[self.path_keyword] = message.get_path()
140 # optimization just in case we already extracted the args
141 if not args:
142 args = message.get_args_list()
144 for handler in self.handler_functions:
145 if getattr(handler, "_dbus_pass_message", False):
146 keywords["dbus_message"] = message
148 if len(keywords) == 0:
149 handler(*args)
150 else:
151 handler(*args, **keywords)
153 def add_handler(self, handler):
154 self.handler_functions.append(handler)
156 #matches only those arguments listed by self
157 def match_args_from_list(self, args_list):
158 if not self.args:
159 return True
161 last_index = len(args_list) - 1
162 for (index, value) in self.args.iteritems():
163 if index > last_index:
164 return False
166 if not (args_list[index] == value):
167 return False
169 return True
171 #does exact matching
172 def match_args_from_rule(self, rule):
173 if self.args == rule.args:
174 return True
176 if self.args == None or rule.args == None:
177 return False
179 my_args_list = self.args.items()
180 match_args_list = rule.args.iterms()
182 if len(my_args_list) != len(match_args_list):
183 return False
185 for (key, value) in my_args_list:
186 if rule.args.get(key) != value:
187 return False
189 return True
191 def is_match(self, rule):
192 if (self.signal_name == rule.signal_name and
193 self.dbus_interface == rule.dbus_interface and
194 self.sender == rule.sender and
195 self.path == rule.path and
196 self.match_args_from_rule(rule)):
197 if rule.handler_functions == []:
198 return True
200 _funcs_copy_a = self.handler_functions[0:]
201 _funcs_copy_b = rule.handler_functions[0:]
202 _funcs_copy_a.sort()
203 _funcs_copy_b.sort()
205 return _funcs_copy_a == _funcs_copy_b
207 return False
209 def __repr__(self):
210 """Returns a custom representation of this DBusMatchRule that can
211 be used with _dbus_bindings
213 repr = "type='signal'"
214 if (self.dbus_interface):
215 repr = repr + ",interface='%s'" % (self.dbus_interface)
217 if (self.sender):
218 repr = repr + ",sender='%s'" % (self.sender)
220 if (self.path):
221 repr = repr + ",path='%s'" % (self.path)
223 if (self.signal_name):
224 repr = repr + ",member='%s'" % (self.signal_name)
226 if (self.args):
227 my_args_list = self.args.items()
228 my_args_list.sort()
229 for (index, value) in my_args_list:
230 repr = repr + ",arg%i='%s'" % (index, value)
232 return repr