Merge lp:~rainct/zeitgeist/bug799199 into lp:zeitgeist/0.1

Proposed by Siegfried Gevatter
Status: Merged
Merged at revision: 1770
Proposed branch: lp:~rainct/zeitgeist/bug799199
Merge into: lp:zeitgeist/0.1
Diff against target: 248 lines (+128/-11)
3 files modified
test/client-test.py (+78/-0)
zeitgeist/client.py (+43/-6)
zeitgeist/datamodel.py (+7/-5)
To merge this branch: bzr merge lp:~rainct/zeitgeist/bug799199
Reviewer Review Type Date Requested Status
Markus Korn Approve
Review via email: mp+65125@code.launchpad.net

Description of the change

It's often convenient for Python applications using the zeitgeist module to create their own subclasses of Event and Subject (so they can add additional functions to them, etc). This branch makes it possible to register such subclasses with zeitgeist.client.ZeitgeistClient.

I'm looking forward to using this feature in Activity Log Manager and Zeitgeist Explorer :).

To post a comment you must log in.
Revision history for this message
Markus Korn (thekorn) wrote :

Good work Siegfried,
tests are running fine, code is looking good, feel free to merge.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'test/client-test.py'
--- test/client-test.py 1970-01-01 00:00:00 +0000
+++ test/client-test.py 2011-06-19 14:18:32 +0000
@@ -0,0 +1,78 @@
1#!/usr/bin/python
2# -.- coding: utf-8 -.-
3
4# Update python path to use local zeitgeist module
5import sys
6import os
7sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
8
9import unittest
10
11from zeitgeist.client import ZeitgeistClient
12from zeitgeist import datamodel
13
14import testutils
15from testutils import parse_events
16
17class EventAndSubjectOverrides (testutils.RemoteTestCase):
18 """
19 This class tests the functionality allowing users to override the
20 Event and Subject types instantiated by ZeitgeistClient (LP: #799199).
21 """
22
23 class CustomEvent(datamodel.Event):
24 pass
25
26 class CustomSubject(datamodel.Subject):
27 pass
28
29 class CustomNothing(object):
30 pass
31
32 def testEventOverrideWhiteBox(self):
33 self.assertEqual(self.client._event_type, datamodel.Event)
34 self.client.register_event_subclass(self.CustomEvent)
35 self.assertEqual(self.client._event_type, self.CustomEvent)
36
37 def testSubjectOverrideWhiteBox(self):
38 self.assertEqual(self.client._event_type._subject_type, datamodel.Subject)
39 self.client.register_subject_subclass(self.CustomSubject)
40 self.assertEqual(self.client._event_type._subject_type, self.CustomSubject)
41
42 def testEventAndSubjectOverrideWhiteBox(self):
43 self.client.register_event_subclass(self.CustomEvent)
44 self.client.register_subject_subclass(self.CustomSubject)
45 self.assertTrue(issubclass(self.client._event_type, self.CustomEvent))
46 self.assertEqual(self.client._event_type._subject_type, self.CustomSubject)
47
48 def testBadOverride(self):
49 self.assertRaises(TypeError, lambda:
50 self.client.register_event_subclass(self.CustomNothing))
51 self.assertRaises(TypeError, lambda:
52 self.client.register_subject_subclass(self.CustomNothing))
53
54 def testEventAndSubjectOverrideBlackBox(self):
55 self.client.register_event_subclass(self.CustomEvent)
56 self.client.register_subject_subclass(self.CustomSubject)
57 self.insertEventsAndWait(parse_events("test/data/single_event.js"))
58 result = self.findEventsForValuesAndWait()
59 self.assertTrue(len(result[0].subjects) >= 1)
60 self.assertTrue(isinstance(result[0], self.CustomEvent))
61 self.assertTrue(isinstance(result[0].subjects[0], self.CustomSubject))
62
63 def testMonitorOverrideBlackBox(self):
64 self.client.register_event_subclass(self.CustomEvent)
65 self.client.register_subject_subclass(self.CustomSubject)
66 mainloop = self.create_mainloop()
67
68 def notify_insert_handler(time_range, events):
69 self.assertTrue(len(events[0].subjects) >= 1)
70 self.assertTrue(isinstance(events[0], self.CustomEvent))
71 self.assertTrue(
72 isinstance(events[0].subjects[0], self.CustomSubject))
73 mainloop.quit()
74
75 self.client.install_monitor(datamodel.TimeRange.always(), [],
76 notify_insert_handler, notify_insert_handler)
77 self.client.insert_events(parse_events("test/data/single_event.js"))
78 mainloop.run()
079
=== modified file 'zeitgeist/client.py'
--- zeitgeist/client.py 2011-06-15 14:18:58 +0000
+++ zeitgeist/client.py 2011-06-19 14:18:32 +0000
@@ -257,21 +257,27 @@
257 257
258 # Used in Monitor._next_path() to generate unique path names258 # Used in Monitor._next_path() to generate unique path names
259 _last_path_id = 0259 _last_path_id = 0
260
261 _event_type = Event
260262
261 def __init__ (self, time_range, event_templates, insert_callback,263 def __init__ (self, time_range, event_templates, insert_callback,
262 delete_callback, monitor_path=None):264 delete_callback, monitor_path=None, event_type=None):
263 if not monitor_path:265 if not monitor_path:
264 monitor_path = Monitor._next_path()266 monitor_path = Monitor._next_path()
265 elif isinstance(monitor_path, (str, unicode)):267 elif isinstance(monitor_path, (str, unicode)):
266 monitor_path = dbus.ObjectPath(monitor_path)268 monitor_path = dbus.ObjectPath(monitor_path)
267 269
270 if event_type:
271 if not issubclass(event_type, Event):
272 raise TypeError("Event subclass expected.")
273 self._event_type = event_type
274
268 self._time_range = time_range275 self._time_range = time_range
269 self._templates = event_templates276 self._templates = event_templates
270 self._path = monitor_path277 self._path = monitor_path
271 self._insert_callback = insert_callback278 self._insert_callback = insert_callback
272 self._delete_callback = delete_callback279 self._delete_callback = delete_callback
273 dbus.service.Object.__init__(self, dbus.SessionBus(), monitor_path)280 dbus.service.Object.__init__(self, dbus.SessionBus(), monitor_path)
274
275 281
276 def get_path (self): return self._path282 def get_path (self): return self._path
277 path = property(get_path,283 path = property(get_path,
@@ -303,7 +309,7 @@
303 See :meth:`ZeitgeistClient.install_monitor`309 See :meth:`ZeitgeistClient.install_monitor`
304 """310 """
305 self._insert_callback(TimeRange(time_range[0], time_range[1]),311 self._insert_callback(TimeRange(time_range[0], time_range[1]),
306 map(Event, events))312 map(self._event_type, events))
307 313
308 @dbus.service.method("org.gnome.zeitgeist.Monitor",314 @dbus.service.method("org.gnome.zeitgeist.Monitor",
309 in_signature="(xx)au")315 in_signature="(xx)au")
@@ -350,6 +356,7 @@
350 """356 """
351 357
352 _installed_monitors = []358 _installed_monitors = []
359 _event_type = Event
353 360
354 @staticmethod361 @staticmethod
355 def get_event_and_extra_arguments(arguments):362 def get_event_and_extra_arguments(arguments):
@@ -382,6 +389,35 @@
382 "Error reinstalling monitor: %s" % err))389 "Error reinstalling monitor: %s" % err))
383 self._iface.connect_join(reconnect_monitors)390 self._iface.connect_join(reconnect_monitors)
384 391
392 def register_event_subclass(self, event_type):
393 """
394 Register a subclass of Event with this ZeiteistClient instance. When
395 data received over D-Bus is instantiated into an Event class, the
396 provided subclass will be used.
397 """
398 if not issubclass(event_type, Event):
399 raise TypeError("Event subclass expected.")
400 self._event_type = event_type
401
402 def register_subject_subclass(self, subject_type):
403 """
404 Register a subclass of Subject with this ZeiteistClient instance. When
405 data received over D-Bus is instantiated into a Subject class, the
406 provided subclass will be used.
407
408 Note that this method works by changing the Event type associated with
409 this ZeitgeistClient instance, so it should always be called *after*
410 any register_event_subclass calls.
411
412 Even better, if you also have a custom Event subclass, you may directly
413 override the Subject type by changing its _subject_type class variable.
414 """
415 if not issubclass(subject_type, Subject):
416 raise TypeError("Subject subclass expected.")
417 class EventWithCustomSubject(self._event_type):
418 _subject_type = subject_type
419 self._event_type = EventWithCustomSubject
420
385 def _safe_error_handler(self, error_handler, *args):421 def _safe_error_handler(self, error_handler, *args):
386 if error_handler is not None:422 if error_handler is not None:
387 if callable(error_handler):423 if callable(error_handler):
@@ -664,7 +700,7 @@
664 num_events,700 num_events,
665 result_type,701 result_type,
666 reply_handler=lambda raw: events_reply_handler(702 reply_handler=lambda raw: events_reply_handler(
667 map(Event.new_for_struct, raw)),703 map(self._event_type.new_for_struct, raw)),
668 error_handler=self._safe_error_handler(error_handler,704 error_handler=self._safe_error_handler(error_handler,
669 events_reply_handler, []))705 events_reply_handler, []))
670 706
@@ -725,7 +761,7 @@
725 # the raw DBus reply into a list of Event instances761 # the raw DBus reply into a list of Event instances
726 self._iface.GetEvents(event_ids,762 self._iface.GetEvents(event_ids,
727 reply_handler=lambda raw: events_reply_handler(763 reply_handler=lambda raw: events_reply_handler(
728 map(Event.new_for_struct, raw)),764 map(self._event_type.new_for_struct, raw)),
729 error_handler=self._safe_error_handler(error_handler,765 error_handler=self._safe_error_handler(error_handler,
730 events_reply_handler, []))766 events_reply_handler, []))
731 767
@@ -876,7 +912,8 @@
876 912
877 913
878 mon = Monitor(time_range, event_templates, notify_insert_handler,914 mon = Monitor(time_range, event_templates, notify_insert_handler,
879 notify_delete_handler, monitor_path=monitor_path)915 notify_delete_handler, monitor_path=monitor_path,
916 event_type=self._event_type)
880 self._iface.InstallMonitor(mon.path,917 self._iface.InstallMonitor(mon.path,
881 mon.time_range,918 mon.time_range,
882 mon.templates,919 mon.templates,
883920
=== modified file 'zeitgeist/datamodel.py'
--- zeitgeist/datamodel.py 2011-05-07 13:26:49 +0000
+++ zeitgeist/datamodel.py 2011-06-19 14:18:32 +0000
@@ -577,6 +577,8 @@
577 SUPPORTS_NEGATION = (Interpretation, Manifestation, Actor, Origin)577 SUPPORTS_NEGATION = (Interpretation, Manifestation, Actor, Origin)
578 SUPPORTS_WILDCARDS = (Actor, Origin)578 SUPPORTS_WILDCARDS = (Actor, Origin)
579 579
580 _subject_type = Subject
581
580 def __init__(self, struct = None):582 def __init__(self, struct = None):
581 """583 """
582 If 'struct' is set it must be a list containing the event584 If 'struct' is set it must be a list containing the event
@@ -608,11 +610,11 @@
608 self.append("")610 self.append("")
609 elif len(struct) == 2:611 elif len(struct) == 2:
610 self.append(self._check_event_struct(struct[0]))612 self.append(self._check_event_struct(struct[0]))
611 self.append(map(Subject, struct[1]))613 self.append(map(self._subject_type, struct[1]))
612 self.append("")614 self.append("")
613 elif len(struct) == 3:615 elif len(struct) == 3:
614 self.append(self._check_event_struct(struct[0]))616 self.append(self._check_event_struct(struct[0]))
615 self.append(map(Subject, struct[1]))617 self.append(map(self._subject_type, struct[1]))
616 self.append(struct[2])618 self.append(struct[2])
617 else:619 else:
618 raise ValueError("Invalid struct length %s" % len(struct))620 raise ValueError("Invalid struct length %s" % len(struct))
@@ -702,7 +704,7 @@
702 if self._dict_contains_subject_keys(values):704 if self._dict_contains_subject_keys(values):
703 if "subjects" in values:705 if "subjects" in values:
704 raise ValueError("Subject keys, subject_*, specified together with full subject list")706 raise ValueError("Subject keys, subject_*, specified together with full subject list")
705 subj = Subject()707 subj = self._subject_type()
706 subj.uri = values.get("subject_uri", "")708 subj.uri = values.get("subject_uri", "")
707 subj.current_uri = values.get("subject_current_uri", "")709 subj.current_uri = values.get("subject_current_uri", "")
708 subj.interpretation = values.get("subject_interpretation", "")710 subj.interpretation = values.get("subject_interpretation", "")
@@ -737,12 +739,12 @@
737 Append a new empty Subject and return a reference to it739 Append a new empty Subject and return a reference to it
738 """740 """
739 if not subject:741 if not subject:
740 subject = Subject()742 subject = self._subject_type()
741 self.subjects.append(subject)743 self.subjects.append(subject)
742 return subject744 return subject
743 745
744 def get_subjects(self):746 def get_subjects(self):
745 return self[1] 747 return self[1]
746 748
747 def set_subjects(self, subjects):749 def set_subjects(self, subjects):
748 self[1] = subjects750 self[1] = subjects

Subscribers

People subscribed via source and target branches