From e3b933445fe4b18af6ec462fc40da5f482e447a0 Mon Sep 17 00:00:00 2001
From: Florent Le Coz <louiz@louiz.org>
Date: Fri, 23 Sep 2011 17:43:01 +0200
Subject: [teisenbe] first attempt at a plugin system.

---
 src/core.py           | 27 +++++++++++++++++++++++++++
 src/plugin.py         | 33 +++++++++++++++++++++++++++++++++
 src/plugin_manager.py | 25 +++++++++++++++++++++++++
 3 files changed, 85 insertions(+)
 create mode 100644 src/plugin.py
 create mode 100644 src/plugin_manager.py

(limited to 'src')

diff --git a/src/core.py b/src/core.py
index 3cdc7592..d644c19b 100644
--- a/src/core.py
+++ b/src/core.py
@@ -36,6 +36,8 @@ import windows
 import connection
 import timed_events
 
+from plugin_manager import PluginManager
+
 from data_forms import DataFormsTab
 from config import config, options
 from logger import logger
@@ -125,6 +127,8 @@ class Core(object):
             'server_cycle': (self.command_server_cycle, _('Usage: /server_cycle [domain] [message]\nServer Cycle: disconnect and reconnects in all the rooms in domain.'), None),
             'bind': (self.command_bind, _('Usage: /bind <key> <equ>\nBind: bind a key to an other key or to a “command”. For example "/bind ^H KEY_UP" makes Control + h do the same same than the Up key.'), None),
             'pubsub': (self.command_pubsub, _('Usage: /pubsub <domain>\nPubsub: Open a pubsub browser on the given domain'), None),
+            'load': (self.command_load, _('Usage: /load <script.py>\nLoad: Load the specified python script'), None),
+            'unload': (self.command_unload, _('Usage: /unload <script.py>\nUnload: Unload the specified python script'), None),
             }
 
         self.key_func = {
@@ -169,6 +173,7 @@ class Core(object):
         self.xmpp.add_event_handler("chatstate_inactive", self.on_chatstate_inactive)
 
         self.timed_events = set()
+        self.plugin_manager = PluginManager(self)
 
     def coucou(self):
         self.command_pubsub('pubsub.louiz.org')
@@ -1121,6 +1126,28 @@ class Core(object):
     def completion_status(self, the_input):
         return the_input.auto_completion([status for status in possible_show], ' ')
 
+    def command_load(self, arg):
+        """
+        /load <script.py>
+        """
+        args = arg.split()
+        if len(args) != 1:
+            self.command_help('load')
+            return
+        filename = args[0]
+        self.plugin_manager.load(filename)
+
+    def command_unload(self, arg):
+        """
+        /unload <script.py>
+        """
+        args = arg.split()
+        if len(args) != 1:
+            self.command_help('unload')
+            return
+        filename = args[0]
+        self.plugin_manager.unload(filename)
+
     def command_message(self, arg):
         """
         /message <jid> [message]
diff --git a/src/plugin.py b/src/plugin.py
new file mode 100644
index 00000000..e8386d16
--- /dev/null
+++ b/src/plugin.py
@@ -0,0 +1,33 @@
+import inspect
+
+class BasePlugin(object):
+    """
+    Class that all plugins derive from.  Any methods beginning with command_
+    are interpreted as a command and beginning with on_ are interpreted as
+    event handlers
+    """
+
+    def __init__(self, core):
+        self.core = core
+        for k, v in inspect.getmembers(self, inspect.ismethod):
+            if k.startswith('on_'):
+                core.xmpp.add_event_handler(k[3:], v)
+            elif k.startswith('command_'):
+                command = k[len('command_'):]
+                core.commands[command] = (v, v.__doc__, None)
+        self.init()
+
+    def init(self):
+        pass
+
+    def cleanup(self):
+        pass
+
+    def unload(self):
+        for k, v in inspect.getmembers(self, inspect.ismethod):
+            if k.startswith('on_'):
+                self.core.xmpp.del_event_handler(k[3:], v)
+            elif k.startswith('command_'):
+                command = k[len('command_'):]
+                del self.core.commands[command]
+        self.cleanup()
diff --git a/src/plugin_manager.py b/src/plugin_manager.py
new file mode 100644
index 00000000..3f900e39
--- /dev/null
+++ b/src/plugin_manager.py
@@ -0,0 +1,25 @@
+class PluginManager(object):
+    def __init__(self, core):
+        self.core = core
+        self.plugins = {}
+
+    def load(self, name):
+        if name in self.plugins:
+            self.plugins[name].unload()
+
+        try:
+            code = compile(open(name).read(), name, 'exec')
+            from plugin import BasePlugin
+            globals = { 'BasePlugin' : BasePlugin }
+            exec(code, globals)
+            self.plugins[name] = globals['Plugin'](self.core)
+        except Exception as e:
+            self.core.information("Could not load plugin: %s" % (e,))
+
+    def unload(self, name):
+        if name in self.plugins:
+            try:
+                self.plugins[name].unload()
+                del self.plugins[name]
+            except Exception as e:
+                self.core.information("Could not unload plugin (may not be safe to try again): %s" % (e,))
-- 
cgit v1.2.3