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

Reply via email to