[kupfer] core: Guard plugin loading and guard plugin intialization better



commit 9edf307b8733783c85c73acb05bd870e709737ad
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Sat Feb 6 15:04:53 2010 +0100

    core: Guard plugin loading and guard plugin intialization better
    
    We guard plugin loading so that plugins that raise exceptions when its
    attributes (sources, actions etc) are instantiated are disabled safely
    and don't cause Kupfer to stop functioning.
    
    We also guard source initialization and remove content decorators from
    the same plugin if we raise an exception.
    
    This solves the current evilplugin testcase, so that this debug plugin
    can be installed without breaking kupfer.

 kupfer/core/data.py       |   14 +++++++++-----
 kupfer/core/pluginload.py |   17 +++++++++++++++++
 kupfer/core/sources.py    |   25 ++++++++++---------------
 3 files changed, 36 insertions(+), 20 deletions(-)
---
diff --git a/kupfer/core/data.py b/kupfer/core/data.py
index 105ec0c..63c136b 100644
--- a/kupfer/core/data.py
+++ b/kupfer/core/data.py
@@ -1,3 +1,5 @@
+from __future__ import with_statement
+
 import itertools
 import operator
 import os
@@ -557,11 +559,13 @@ class DataController (gobject.GObject, pretty.OutputMixin):
 		Load @plugin_id, register all its Actions, Content and TextSources.
 		Return its sources.
 		"""
-		plugin = pluginload.load_plugin(plugin_id)
-		self.register_text_sources(plugin.text_sources)
-		self.register_action_decorators(plugin.action_decorators)
-		self.register_content_decorators(plugin.content_decorators)
-		return set(plugin.sources)
+		with pluginload.exception_guard(plugin_id):
+			plugin = pluginload.load_plugin(plugin_id)
+			self.register_text_sources(plugin.text_sources)
+			self.register_action_decorators(plugin.action_decorators)
+			self.register_content_decorators(plugin.content_decorators)
+			return set(plugin.sources)
+		return set()
 
 	def _plugin_enabled(self, setctl, plugin_id, enabled):
 		from kupfer.core import plugins
diff --git a/kupfer/core/pluginload.py b/kupfer/core/pluginload.py
index 1b4f91e..c914352 100644
--- a/kupfer/core/pluginload.py
+++ b/kupfer/core/pluginload.py
@@ -1,3 +1,7 @@
+import contextlib
+
+from kupfer import pretty
+
 from kupfer.core import plugins
 from kupfer.core.plugins import (load_plugin_sources, sources_attribute,
 		action_decorators_attribute, text_sources_attribute,
@@ -44,3 +48,16 @@ def load_plugin(plugin_id):
 	desc.sources = sources
 	return desc
 
+ contextlib contextmanager
+def exception_guard(name, callback=None, *args):
+	"Guard for exceptions, print traceback and call @callback if any is raised"
+	try:
+		yield
+	except Exception:
+		import traceback
+		pretty.print_error(__name__, "Loading %s raised an exception:" % name)
+		traceback.print_exc()
+		pretty.print_error(__name__, "This error is probably a bug in", name)
+		pretty.print_error(__name__, "Please file a bug report")
+		if callback is not None:
+			callback(*args)
diff --git a/kupfer/core/sources.py b/kupfer/core/sources.py
index 0e7163f..a8d4fd2 100644
--- a/kupfer/core/sources.py
+++ b/kupfer/core/sources.py
@@ -11,6 +11,7 @@ import time
 
 from kupfer import config, pretty, scheduler
 from kupfer.obj import base, sources
+from kupfer.core import pluginload
 
 class PeriodicRescanner (pretty.OutputMixin):
 	"""
@@ -433,19 +434,13 @@ class SourceController (pretty.OutputMixin):
 				continue
 			sourcepickler.pickle_source(source)
 
-	@contextlib.contextmanager
-	def _exception_guard(self, source):
-		"Guard for exceptions, ousting @source from catalog if any is raised"
-		try:
-			yield
-		except Exception:
-			import traceback
-			self.output_error("Loading %s raised an exception:" % source)
-			traceback.print_exc()
-			self.output_error("This error is probably a bug in %s" % source)
-			self.output_error("Please file a bug report")
-			self.sources.discard(source)
-			self.toplevel_sources.discard(source)
+	def _remove_source(self, source):
+		"Oust @source from catalog if any exception is raised"
+		self.sources.discard(source)
+		self.toplevel_sources.discard(source)
+		source_type = type(source)
+		for typ in self.content_decorators:
+			self.content_decorators[typ].discard(source_type)
 
 	def initialize(self):
 		"Initialize all sources and cache toplevel sources"
@@ -456,14 +451,14 @@ class SourceController (pretty.OutputMixin):
 
 	def _initialize_sources(self, sources):
 		for src in set(sources):
-			with self._exception_guard(src):
+			with pluginload.exception_guard(src, self._remove_source, src):
 				src.initialize()
 
 	def _cache_sources(self, sources):
 		# Make sure that the toplevel sources are chached
 		# either newly rescanned or the cache is fully loaded
 		for src in set(sources):
-			with self._exception_guard(src):
+			with pluginload.exception_guard(src, self._remove_source, src):
 				force = (src not in self._restored_sources)
 				self.rescanner.rescan_now(src, force_update=force)
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]