Siegfried Gevatter has proposed merging lp:~rainct/zeitgeist/bug799199 into lp:zeitgeist.
Requested reviews: Zeitgeist Framework Team (zeitgeist) Related bugs: Bug #799199 in Zeitgeist Framework: "zeitgeist.client: option to override Event class" https://bugs.launchpad.net/zeitgeist/+bug/799199 For more details, see: https://code.launchpad.net/~rainct/zeitgeist/bug799199/+merge/65125 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 :). -- https://code.launchpad.net/~rainct/zeitgeist/bug799199/+merge/65125 Your team Zeitgeist Framework Team is requested to review the proposed merge of lp:~rainct/zeitgeist/bug799199 into lp:zeitgeist.
=== 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 @@ +#!/usr/bin/python +# -.- coding: utf-8 -.- + +# Update python path to use local zeitgeist module +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +import unittest + +from zeitgeist.client import ZeitgeistClient +from zeitgeist import datamodel + +import testutils +from testutils import parse_events + +class EventAndSubjectOverrides (testutils.RemoteTestCase): + """ + This class tests the functionality allowing users to override the + Event and Subject types instantiated by ZeitgeistClient (LP: #799199). + """ + + class CustomEvent(datamodel.Event): + pass + + class CustomSubject(datamodel.Subject): + pass + + class CustomNothing(object): + pass + + def testEventOverrideWhiteBox(self): + self.assertEqual(self.client._event_type, datamodel.Event) + self.client.register_event_subclass(self.CustomEvent) + self.assertEqual(self.client._event_type, self.CustomEvent) + + def testSubjectOverrideWhiteBox(self): + self.assertEqual(self.client._event_type._subject_type, datamodel.Subject) + self.client.register_subject_subclass(self.CustomSubject) + self.assertEqual(self.client._event_type._subject_type, self.CustomSubject) + + def testEventAndSubjectOverrideWhiteBox(self): + self.client.register_event_subclass(self.CustomEvent) + self.client.register_subject_subclass(self.CustomSubject) + self.assertTrue(issubclass(self.client._event_type, self.CustomEvent)) + self.assertEqual(self.client._event_type._subject_type, self.CustomSubject) + + def testBadOverride(self): + self.assertRaises(TypeError, lambda: + self.client.register_event_subclass(self.CustomNothing)) + self.assertRaises(TypeError, lambda: + self.client.register_subject_subclass(self.CustomNothing)) + + def testEventAndSubjectOverrideBlackBox(self): + self.client.register_event_subclass(self.CustomEvent) + self.client.register_subject_subclass(self.CustomSubject) + self.insertEventsAndWait(parse_events("test/data/single_event.js")) + result = self.findEventsForValuesAndWait() + self.assertTrue(len(result[0].subjects) >= 1) + self.assertTrue(isinstance(result[0], self.CustomEvent)) + self.assertTrue(isinstance(result[0].subjects[0], self.CustomSubject)) + + def testMonitorOverrideBlackBox(self): + self.client.register_event_subclass(self.CustomEvent) + self.client.register_subject_subclass(self.CustomSubject) + mainloop = self.create_mainloop() + + def notify_insert_handler(time_range, events): + self.assertTrue(len(events[0].subjects) >= 1) + self.assertTrue(isinstance(events[0], self.CustomEvent)) + self.assertTrue( + isinstance(events[0].subjects[0], self.CustomSubject)) + mainloop.quit() + + self.client.install_monitor(datamodel.TimeRange.always(), [], + notify_insert_handler, notify_insert_handler) + self.client.insert_events(parse_events("test/data/single_event.js")) + mainloop.run() === 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 @@ # Used in Monitor._next_path() to generate unique path names _last_path_id = 0 + + _event_type = Event def __init__ (self, time_range, event_templates, insert_callback, - delete_callback, monitor_path=None): + delete_callback, monitor_path=None, event_type=None): if not monitor_path: monitor_path = Monitor._next_path() elif isinstance(monitor_path, (str, unicode)): monitor_path = dbus.ObjectPath(monitor_path) + if event_type: + if not issubclass(event_type, Event): + raise TypeError("Event subclass expected.") + self._event_type = event_type + self._time_range = time_range self._templates = event_templates self._path = monitor_path self._insert_callback = insert_callback self._delete_callback = delete_callback dbus.service.Object.__init__(self, dbus.SessionBus(), monitor_path) - def get_path (self): return self._path path = property(get_path, @@ -303,7 +309,7 @@ See :meth:`ZeitgeistClient.install_monitor` """ self._insert_callback(TimeRange(time_range[0], time_range[1]), - map(Event, events)) + map(self._event_type, events)) @dbus.service.method("org.gnome.zeitgeist.Monitor", in_signature="(xx)au") @@ -350,6 +356,7 @@ """ _installed_monitors = [] + _event_type = Event @staticmethod def get_event_and_extra_arguments(arguments): @@ -382,6 +389,35 @@ "Error reinstalling monitor: %s" % err)) self._iface.connect_join(reconnect_monitors) + def register_event_subclass(self, event_type): + """ + Register a subclass of Event with this ZeiteistClient instance. When + data received over D-Bus is instantiated into an Event class, the + provided subclass will be used. + """ + if not issubclass(event_type, Event): + raise TypeError("Event subclass expected.") + self._event_type = event_type + + def register_subject_subclass(self, subject_type): + """ + Register a subclass of Subject with this ZeiteistClient instance. When + data received over D-Bus is instantiated into a Subject class, the + provided subclass will be used. + + Note that this method works by changing the Event type associated with + this ZeitgeistClient instance, so it should always be called *after* + any register_event_subclass calls. + + Even better, if you also have a custom Event subclass, you may directly + override the Subject type by changing its _subject_type class variable. + """ + if not issubclass(subject_type, Subject): + raise TypeError("Subject subclass expected.") + class EventWithCustomSubject(self._event_type): + _subject_type = subject_type + self._event_type = EventWithCustomSubject + def _safe_error_handler(self, error_handler, *args): if error_handler is not None: if callable(error_handler): @@ -664,7 +700,7 @@ num_events, result_type, reply_handler=lambda raw: events_reply_handler( - map(Event.new_for_struct, raw)), + map(self._event_type.new_for_struct, raw)), error_handler=self._safe_error_handler(error_handler, events_reply_handler, [])) @@ -725,7 +761,7 @@ # the raw DBus reply into a list of Event instances self._iface.GetEvents(event_ids, reply_handler=lambda raw: events_reply_handler( - map(Event.new_for_struct, raw)), + map(self._event_type.new_for_struct, raw)), error_handler=self._safe_error_handler(error_handler, events_reply_handler, [])) @@ -876,7 +912,8 @@ mon = Monitor(time_range, event_templates, notify_insert_handler, - notify_delete_handler, monitor_path=monitor_path) + notify_delete_handler, monitor_path=monitor_path, + event_type=self._event_type) self._iface.InstallMonitor(mon.path, mon.time_range, mon.templates, === 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 @@ SUPPORTS_NEGATION = (Interpretation, Manifestation, Actor, Origin) SUPPORTS_WILDCARDS = (Actor, Origin) + _subject_type = Subject + def __init__(self, struct = None): """ If 'struct' is set it must be a list containing the event @@ -608,11 +610,11 @@ self.append("") elif len(struct) == 2: self.append(self._check_event_struct(struct[0])) - self.append(map(Subject, struct[1])) + self.append(map(self._subject_type, struct[1])) self.append("") elif len(struct) == 3: self.append(self._check_event_struct(struct[0])) - self.append(map(Subject, struct[1])) + self.append(map(self._subject_type, struct[1])) self.append(struct[2]) else: raise ValueError("Invalid struct length %s" % len(struct)) @@ -702,7 +704,7 @@ if self._dict_contains_subject_keys(values): if "subjects" in values: raise ValueError("Subject keys, subject_*, specified together with full subject list") - subj = Subject() + subj = self._subject_type() subj.uri = values.get("subject_uri", "") subj.current_uri = values.get("subject_current_uri", "") subj.interpretation = values.get("subject_interpretation", "") @@ -737,12 +739,12 @@ Append a new empty Subject and return a reference to it """ if not subject: - subject = Subject() + subject = self._subject_type() self.subjects.append(subject) return subject def get_subjects(self): - return self[1] + return self[1] def set_subjects(self, subjects): self[1] = subjects
_______________________________________________ Mailing list: https://launchpad.net/~zeitgeist Post to : zeitgeist@lists.launchpad.net Unsubscribe : https://launchpad.net/~zeitgeist More help : https://help.launchpad.net/ListHelp