[geany/geany-plugins] bc9f2d: Convert to a proxy plugin

Thomas Martitz git-noreply at xxxxx
Sun Mar 6 12:22:50 UTC 2016


Branch:      refs/heads/master
Author:      Thomas Martitz <kugel at rockbox.org>
Committer:   Thomas Martitz <kugel at rockbox.org>
Date:        Sun, 06 Mar 2016 12:22:50 UTC
Commit:      bc9f2db62842bd7a8d9920da34ac1e5593f49e0e
             https://github.com/geany/geany-plugins/commit/bc9f2db62842bd7a8d9920da34ac1e5593f49e0e

Log Message:
-----------
Convert to a proxy plugin

This allows to drop the custom loader and plugin manager and instead
make use of Geany's new proxy plugin feature, where python plugins
are embedded into the standard plugin manager as first class citizen.

Existing plugins continue to run with one exception: The help and configure
methods have been renamed, and in case of configure the samantics have changed
accordingly to Geany's unified configure dialog for all plugins. So existing
scripts that used either show_help() or show_configure() need to to the following:

1) rename show_help() to help()
2a) rename show_configure to configure()
2b) change configure to just return the content widget and remove the creation
    of a custom dialog

The plugin script dir is now $geanylibdir/geany, the same as for native plugns.
Geany loooks only there and doesn't make a difference between native and
sub-plugins.


Modified Paths:
--------------
    geanypy/geany/Makefile.am
    geanypy/geany/__init__.py
    geanypy/geany/loader.py
    geanypy/geany/manager.py
    geanypy/geany/plugin.py
    geanypy/plugins/Makefile.am
    geanypy/plugins/console.py
    geanypy/src/Makefile.am
    geanypy/src/geanypy-plugin.c
    geanypy/src/geanypy-plugin.h
    geanypy/src/geanypy-signalmanager.c

Modified: geanypy/geany/Makefile.am
2 lines changed, 0 insertions(+), 2 deletions(-)
===================================================================
@@ -1,7 +1,5 @@
 geanypy_sources				=	__init__.py \
 								console.py \
-								manager.py \
-								loader.py \
 								plugin.py \
 								signalmanager.py
 geanypy_objects				=	$(geanypy_sources:.py=.pyc)


Modified: geanypy/geany/__init__.py
2 lines changed, 0 insertions(+), 2 deletions(-)
===================================================================
@@ -15,9 +15,7 @@
 import encoding
 import filetypes
 import highlighting
-import loader
 import main
-import manager
 import msgwindow
 import navqueue
 import prefs


Modified: geanypy/geany/loader.py
172 lines changed, 0 insertions(+), 172 deletions(-)
===================================================================
@@ -1,172 +0,0 @@
-import os
-import imp
-from collections import namedtuple
-import geany
-
-PluginInfo = namedtuple('PluginInfo', 'filename, name, version, description, author, cls')
-
-
-class PluginLoader(object):
-
-	plugins = {}
-
-	def __init__(self, plugin_dirs):
-
-		self.plugin_dirs = plugin_dirs
-
-		self.available_plugins = []
-		for plugin in self.iter_plugin_info():
-			self.available_plugins.append(plugin)
-
-		self.restore_loaded_plugins()
-
-
-	def update_loaded_plugins_file(self):
-		for path in self.plugin_dirs:
-			if os.path.isdir(path):
-				try:
-					state_file = os.path.join(path, '.loaded_plugins')
-					with open(state_file, 'w') as f:
-						for plugfn in self.plugins:
-							f.write("%s\n" % plugfn)
-				except IOError as err:
-					if err.errno == 13: #perms
-						pass
-					else:
-						raise
-
-
-	def restore_loaded_plugins(self):
-		loaded_plugins = []
-		for path in reversed(self.plugin_dirs):
-			state_file = os.path.join(path, ".loaded_plugins")
-			if os.path.exists(state_file):
-				for line in open(state_file):
-					line = line.strip()
-					if line not in loaded_plugins:
-						loaded_plugins.append(line)
-		for filename in loaded_plugins:
-			self.load_plugin(filename)
-
-
-	def load_all_plugins(self):
-
-		for plugin_info in self.iter_plugin_info():
-			if plugin_filename.endswith('test.py'): # hack for testing
-				continue
-			plug = self.load_plugin(plugin_info.filename)
-			if plug:
-				print("Loaded plugin: %s" % plugin_info.filename)
-				print("  Name: %s v%s" % (plug.name, plug.version))
-				print("  Desc: %s" % plug.description)
-				print("  Author: %s" % plug.author)
-
-
-	def unload_all_plugins(self):
-
-		for plugin in self.plugins:
-			self.unload_plugin(plugin)
-
-
-	def reload_all_plugins(self):
-
-		self.unload_all_plugins()
-		self.load_all_plugins()
-
-
-	def iter_plugin_info(self):
-
-		for d in self.plugin_dirs:
-			if os.path.isdir(d):
-				for current_file in os.listdir(d):
-					#check inside folders inside the plugins dir so we can load .py files here as plugins
-					current_path=os.path.abspath(os.path.join(d, current_file))
-					if os.path.isdir(current_path):
-						for plugin_folder_file in os.listdir(current_path):
-							if plugin_folder_file.endswith('.py'):
-								#loop around results if its fails to load will never reach yield
-								for p in self.load_plugin_info(current_path,plugin_folder_file):
-									yield p
-									
-					#not a sub directory so if it ends with .py lets just attempt to load it as a plugin
-					if current_file.endswith('.py'):
-						#loop around results if its fails to load will never reach yield
-						for p in self.load_plugin_info(d,current_file):
-							yield p
-								
-	def load_plugin_info(self,d,f):
-		filename = os.path.abspath(os.path.join(d, f))
-		if filename.endswith("test.py"):
-			pass
-		text = open(filename).read()
-		module_name = os.path.basename(filename)[:-3]
-		try:
-			module = imp.load_source(module_name, filename)
-		except ImportError as exc:
-			print "Error: failed to import settings module ({})".format(exc)
-			module=None
-		if module:	
-			for k, v in module.__dict__.iteritems():
-				if k == geany.Plugin.__name__:
-					continue
-				try:
-					if issubclass(v, geany.Plugin):
-						inf = PluginInfo(
-								filename,
-								getattr(v, '__plugin_name__'),
-								getattr(v, '__plugin_version__', ''),
-								getattr(v, '__plugin_description__', ''),
-								getattr(v, '__plugin_author__', ''),
-								v)
-						yield inf
-						
-				except TypeError:
-					continue
-
-
-	def load_plugin(self, filename):
-
-		for avail in self.available_plugins:
-			if avail.filename == filename:
-				inst = avail.cls()
-				self.plugins[filename] = inst
-				self.update_loaded_plugins_file()
-				geany.ui_utils.set_statusbar('GeanyPy: plugin activated: %s' %
-					inst.name, True)
-				return inst
-
-
-	def unload_plugin(self, filename):
-
-		try:
-			plugin = self.plugins[filename]
-			name = plugin.name
-			plugin.cleanup()
-			del self.plugins[filename]
-			self.update_loaded_plugins_file()
-			geany.ui_utils.set_statusbar('GeanyPy: plugin deactivated: %s' %
-				name, True)
-		except KeyError:
-			print("Unable to unload plugin '%s': it's not loaded" % filename)
-
-
-	def reload_plugin(self, filename):
-
-		if filename in self.plugins:
-			self.unload_plugin(filename)
-		self.load_plugin(filename)
-
-
-	def plugin_has_help(self, filename):
-
-		for plugin_info in self.iter_plugin_info():
-			if plugin_info.filename == filename:
-				return hasattr(plugin_info.cls, 'show_help')
-
-
-	def plugin_has_configure(self, filename):
-
-		try:
-			return hasattr(self.plugins[filename], 'show_configure')
-		except KeyError:
-			return None


Modified: geanypy/geany/manager.py
179 lines changed, 0 insertions(+), 179 deletions(-)
===================================================================
@@ -1,179 +0,0 @@
-import gtk
-import gobject
-import glib
-from htmlentitydefs import name2codepoint
-from loader import PluginLoader
-
-
-class PluginManager(gtk.Dialog):
-
-	def __init__(self, plugin_dirs=[]):
-		gtk.Dialog.__init__(self, title="Plugin Manager")
-		self.loader = PluginLoader(plugin_dirs)
-
-		self.set_default_size(400, 450)
-		self.set_has_separator(True)
-		icon = self.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU)
-		self.set_icon(icon)
-
-		self.connect("response", lambda w,d: self.hide())
-
-		vbox = gtk.VBox(False, 12)
-		vbox.set_border_width(12)
-
-		lbl = gtk.Label("Choose plugins to load or unload:")
-		lbl.set_alignment(0.0, 0.5)
-		vbox.pack_start(lbl, False, False, 0)
-
-		sw = gtk.ScrolledWindow()
-		sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-		sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
-		vbox.pack_start(sw, True, True, 0)
-
-		self.treeview = gtk.TreeView()
-		sw.add(self.treeview)
-
-		vbox.show_all()
-
-		self.get_content_area().add(vbox)
-
-		action_area = self.get_action_area()
-		action_area.set_spacing(0)
-		action_area.set_homogeneous(False)
-
-		btn = gtk.Button(stock=gtk.STOCK_CLOSE)
-		btn.set_border_width(6)
-		btn.connect("clicked", lambda x: self.response(gtk.RESPONSE_CLOSE))
-		action_area.pack_start(btn, False, True, 0)
-		btn.show()
-
-		self.btn_help = gtk.Button(stock=gtk.STOCK_HELP)
-		self.btn_help.set_border_width(6)
-		self.btn_help.set_no_show_all(True)
-		action_area.pack_start(self.btn_help, False, True, 0)
-		action_area.set_child_secondary(self.btn_help, True)
-
-		self.btn_prefs = gtk.Button(stock=gtk.STOCK_PREFERENCES)
-		self.btn_prefs.set_border_width(6)
-		self.btn_prefs.set_no_show_all(True)
-		action_area.pack_start(self.btn_prefs, False, True, 0)
-		action_area.set_child_secondary(self.btn_prefs, True)
-
-		action_area.show()
-
-		self.load_plugins_list()
-
-
-	def on_help_button_clicked(self, button, treeview, model):
-		path = treeview.get_cursor()[0]
-		iter = model.get_iter(path)
-		filename = model.get_value(iter, 2)
-		for plugin in self.loader.available_plugins:
-			if plugin.filename == filename:
-				plugin.cls.show_help()
-				break
-		else:
-			print("Plugin does not support help function")
-
-
-	def on_preferences_button_clicked(self, button, treeview, model):
-		path = treeview.get_cursor()[0]
-		iter = model.get_iter(path)
-		filename = model.get_value(iter, 2)
-		try:
-			self.loader.plugins[filename].show_configure()
-		except KeyError:
-			print("Plugin is not loaded, can't run configure function")
-
-
-	def activate_plugin(self, filename):
-		self.loader.load_plugin(filename)
-
-
-	def deactivate_plugin(self, filename):
-		self.loader.unload_plugin(filename)
-
-
-	def load_plugins_list(self):
-		liststore = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str)
-
-		self.btn_help.connect("clicked",
-			self.on_help_button_clicked, self.treeview, liststore)
-
-		self.btn_prefs.connect("clicked",
-			self.on_preferences_button_clicked, self.treeview, liststore)
-
-		self.treeview.set_model(liststore)
-		self.treeview.set_headers_visible(False)
-		self.treeview.set_grid_lines(True)
-
-		check_renderer = gtk.CellRendererToggle()
-		check_renderer.set_radio(False)
-		check_renderer.connect('toggled', self.on_plugin_load_toggled, liststore)
-		text_renderer = gtk.CellRendererText()
-
-		check_column = gtk.TreeViewColumn(None, check_renderer, active=0)
-		text_column = gtk.TreeViewColumn(None, text_renderer, markup=1)
-
-		self.treeview.append_column(check_column)
-		self.treeview.append_column(text_column)
-
-		self.treeview.connect('row-activated',
-			self.on_row_activated, check_renderer, liststore)
-		self.treeview.connect('cursor-changed',
-			self.on_selected_plugin_changed, liststore)
-
-		self.load_sorted_plugins_info(liststore)
-
-
-	def load_sorted_plugins_info(self, list_store):
-
-		plugin_info_list = list(self.loader.iter_plugin_info())
-		#plugin_info_list.sort(key=lambda pi: pi[1])
-
-		for plugin_info in plugin_info_list:
-
-			lbl = str('<big><b>%s</b></big> <small>%s</small>\n%s\n' +
-					'<small><b>Author:</b> %s\n' +
-					'<b>Filename:</b> %s</small>') % (
-					glib.markup_escape_text(plugin_info.name),
-					glib.markup_escape_text(plugin_info.version),
-					glib.markup_escape_text(plugin_info.description),
-					glib.markup_escape_text(plugin_info.author),
-					glib.markup_escape_text(plugin_info.filename))
-
-			loaded = plugin_info.filename in self.loader.plugins
-
-			list_store.append([loaded, lbl, plugin_info.filename])
-
-
-	def on_selected_plugin_changed(self, treeview, model):
-
-		path = treeview.get_cursor()[0]
-		iter = model.get_iter(path)
-		filename = model.get_value(iter, 2)
-		active = model.get_value(iter, 0)
-
-		if self.loader.plugin_has_configure(filename):
-			self.btn_prefs.set_visible(True)
-		else:
-			self.btn_prefs.set_visible(False)
-
-		if self.loader.plugin_has_help(filename):
-			self.btn_help.set_visible(True)
-		else:
-			self.btn_help.set_visible(False)
-
-
-	def on_plugin_load_toggled(self, cell, path, model):
-		active = not cell.get_active()
-		iter = model.get_iter(path)
-		model.set_value(iter, 0, active)
-		if active:
-			self.activate_plugin(model.get_value(iter, 2))
-		else:
-			self.deactivate_plugin(model.get_value(iter, 2))
-
-
-	def on_row_activated(self, tvw, path, view_col, cell, model):
-		self.on_plugin_load_toggled(cell, path, model)


Modified: geanypy/geany/plugin.py
1 lines changed, 0 insertions(+), 1 deletions(-)
===================================================================
@@ -49,7 +49,6 @@ class Plugin(object):
 	#__plugin_version__ = None
 	#__plugin_author__ = None
 
-
 	_events = {
 		"document-open": [],
 		# TODO: add more events here


Modified: geanypy/plugins/Makefile.am
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -1,4 +1,4 @@
 geanypy_plugins				=	demo.py hello.py console.py
-geanypydir					=	$(datadir)/geany/geanypy/plugins
+geanypydir					=	$(libdir)/geany
 geanypy_DATA				=	$(geanypy_plugins)
 EXTRA_DIST					=	$(geanypy_plugins)


Modified: geanypy/plugins/console.py
16 lines changed, 2 insertions(+), 14 deletions(-)
===================================================================
@@ -191,16 +191,7 @@ def on_bg_color_changed(self, clr_btn, data=None):
 		self.bg = clr_btn.get_color().to_string()
 
 
-	def show_configure(self):
-		dialog = gtk.Dialog("Configure Python Console",
-							geany.main_widgets.window,
-							gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-							(gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT))
-
-		dialog.set_has_separator(True)
-
-		content_area = dialog.get_content_area()
-		content_area.set_border_width(6)
+	def configure(self, dialog):
 
 		vbox = gtk.VBox(spacing=6)
 		vbox.set_border_width(6)
@@ -306,8 +297,5 @@ def show_configure(self):
 
 		vbox.pack_start(fra_general, True, True, 0)
 		vbox.pack_start(fra_appearances, False, True, 0)
-		content_area.pack_start(vbox, True, True, 0)
-		content_area.show_all()
 
-		dialog.run()
-		dialog.destroy()
+		return vbox


Modified: geanypy/src/Makefile.am
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -6,7 +6,7 @@ geanyplugindir				=	$(libdir)/geany
 geanypy_la_LDFLAGS			=	-module -avoid-version -Wl,--export-dynamic
 geanypy_la_CPPFLAGS			=	@GEANY_CFLAGS@ @PYGTK_CFLAGS@ @PYTHON_CPPFLAGS@ \
 								-DGEANYPY_PYTHON_DIR="\"$(libdir)/geany/geanypy\"" \
-								-DGEANYPY_PLUGIN_DIR="\"$(datadir)/geany/geanypy/plugins\"" \
+								-DGEANYPY_PLUGIN_DIR="\"$(libdir)/geany\"" \
 								-DG_LOG_DOMAIN=\"GeanyPy\"
 geanypy_la_CFLAGS			=	@GEANYPY_CFLAGS@ @GMODULE_CFLAGS@
 geanypy_la_LIBADD			=	@GEANY_LIBS@ @PYGTK_LIBS@ \


Modified: geanypy/src/geanypy-plugin.c
337 lines changed, 212 insertions(+), 125 deletions(-)
===================================================================
@@ -27,25 +27,10 @@
 
 #include "geanypy.h"
 
-G_MODULE_EXPORT GeanyPlugin		*geany_plugin;
-G_MODULE_EXPORT GeanyData		*geany_data;
-G_MODULE_EXPORT GeanyFunctions	*geany_functions;
-
-
-G_MODULE_EXPORT PLUGIN_VERSION_CHECK(211)
-
-G_MODULE_EXPORT PLUGIN_SET_INFO(
-	_("GeanyPy"),
-	_("Python plugins support"),
-	"1.0",
-	"Matthew Brush <mbrush at codebrainz.ca>")
-
-
-static GtkWidget *loader_item = NULL;
-static PyObject *manager = NULL;
-static gchar *plugin_dir = NULL;
-static SignalManager *signal_manager = NULL;
+#include <glib.h>
+#include <glib/gstdio.h>
 
+GeanyData *geany_data;
 
 /* Forward declarations to prevent compiler warnings. */
 PyMODINIT_FUNC initapp(void);
@@ -131,7 +116,9 @@ GeanyPy_start_interpreter(void)
         "import os, sys\n"
         "path = '%s'.replace('~', os.path.expanduser('~'))\n"
         "sys.path.append(path)\n"
-        "import geany\n", py_dir);
+        "path = '%s'.replace('~', os.path.expanduser('~'))\n"
+        "sys.path.append(path)\n"
+        "import geany\n", py_dir, GEANYPY_PLUGIN_DIR);
     g_free(py_dir);
 
     PyRun_SimpleString(init_code);
@@ -146,141 +133,241 @@ GeanyPy_stop_interpreter(void)
         Py_Finalize();
 }
 
+typedef struct
+{
+	PyObject *base;
+	SignalManager *signal_manager;
+}
+GeanyPyData;
 
-static void
-GeanyPy_init_manager(const gchar *dir)
+typedef struct
 {
-    PyObject *module, *man, *args;
-    gchar *sys_plugin_dir = NULL;
+	PyObject *class;
+	PyObject *module;
+	PyObject *instance;
+}
+GeanyPyPluginData;
 
-    g_return_if_fail(dir != NULL);
+static gboolean has_error(void)
+{
+	if (PyErr_Occurred())
+	{
+		PyErr_Print();
+		return TRUE;
+	}
+	return FALSE;
+}
 
-    module = PyImport_ImportModule("geany.manager");
-    if (module == NULL)
-    {
-        g_warning(_("Failed to import manager module"));
-        return;
-    }
+static gboolean geanypy_proxy_init(GeanyPlugin *plugin, gpointer pdata)
+{
+	GeanyPyPluginData *data = (GeanyPyPluginData *) pdata;
 
-    man = PyObject_GetAttrString(module, "PluginManager");
-    Py_DECREF(module);
+	data->instance = PyObject_CallObject(data->class, NULL);
+	if (has_error())
+		return FALSE;
 
-    if (man == NULL)
-    {
-        g_warning(_("Failed to retrieve PluginManager from manager module"));
-        return;
-    }
+	return TRUE;
+}
 
-#ifdef GEANYPY_WINDOWS
-	{ /* Detect the system plugin's dir at runtime on Windows since we
-	   * don't really know where Geany is installed. */
-		gchar *geany_base_dir;
-		geany_base_dir = g_win32_get_package_installation_directory_of_module(NULL);
-		if (geany_base_dir)
-		{
-			sys_plugin_dir = g_build_filename(geany_base_dir, "lib", "geanypy", "plugins", NULL);
-			g_free(geany_base_dir);
-		}
-		if (!g_file_test(sys_plugin_dir, G_FILE_TEST_EXISTS))
-		{
-			g_warning(_("System plugin directory not found."));
-			g_free(sys_plugin_dir);
-			sys_plugin_dir = NULL;
-		}
-	}
-#else
-	sys_plugin_dir = g_strdup(GEANYPY_PLUGIN_DIR);
-#endif
 
+static void geanypy_proxy_cleanup(GeanyPlugin *plugin, gpointer pdata)
+{
+	GeanyPyPluginData *data = (GeanyPyPluginData *) pdata;
 
-	g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "User plugins: %s", dir);
+	PyObject_CallMethod(data->instance, "cleanup", NULL);
+	if (has_error())
+		return;
+}
 
-	if (sys_plugin_dir)
+
+static GtkWidget *geanypy_proxy_configure(GeanyPlugin *plugin, GtkDialog *parent, gpointer pdata)
+{
+	GeanyPyPluginData *data = (GeanyPyPluginData *) pdata;
+	PyObject *o, *oparent;
+	GObject *widget;
+
+	oparent = pygobject_new(G_OBJECT(parent));
+	o = PyObject_CallMethod(data->instance, "configure", "O", oparent, NULL);
+	Py_DECREF(oparent);
+
+	if (!has_error() && o != Py_None)
 	{
-		g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "System plugins: %s", sys_plugin_dir);
-		args = Py_BuildValue("([s, s])", sys_plugin_dir, dir);
-		g_free(sys_plugin_dir);
+		/* Geany wants only the underlying GtkWidget, we must only ref that
+		 * and free the pygobject wrapper */
+		widget = g_object_ref(pygobject_get(o));
+		Py_DECREF(o);
+		return GTK_WIDGET(widget);
 	}
-	else
-		args = Py_BuildValue("([s])", dir);
 
-    manager = PyObject_CallObject(man, args);
-    if (PyErr_Occurred())
-		PyErr_Print();
-    Py_DECREF(man);
-    Py_DECREF(args);
-
-    if (manager == NULL)
-    {
-        g_warning(_("Unable to instantiate new PluginManager"));
-        return;
-    }
+	Py_DECREF(o); /* Must unref even if it's Py_None */
+	return NULL;
+}
+
+static void geanypy_proxy_help(GeanyPlugin *plugin, gpointer pdata)
+{
+	GeanyPyPluginData *data = (GeanyPyPluginData *) pdata;
+
+	PyObject_CallMethod(data->instance, "help", NULL);
+	if (has_error())
+		return;
 }
 
+static gint
+geanypy_probe(GeanyPlugin *proxy, const gchar *filename, gpointer pdata)
+{
+	gchar *file_plugin = g_strdup_printf("%.*s.plugin",
+			(int)(strrchr(filename, '.') - filename), filename);
+	gint ret = PROXY_IGNORED;
 
-static void
-GeanyPy_show_manager(void)
+	/* avoid clash with libpeas py plugins, those come with a corresponding <plugin>.plugin file */
+	if (!g_file_test(file_plugin, G_FILE_TEST_EXISTS))
+		ret = PROXY_MATCHED;
+
+	g_free(file_plugin);
+	return ret;
+}
+
+
+static const gchar *string_from_attr(PyObject *o, const gchar *attr)
 {
-    PyObject *show_method;
-
-    g_return_if_fail(manager != NULL);
-
-    show_method = PyObject_GetAttrString(manager, "show_all");
-    if (show_method == NULL)
-    {
-        g_warning(_("Unable to get show_all() method on plugin manager"));
-        return;
-    }
-    PyObject_CallObject(show_method, NULL);
-    Py_DECREF(show_method);
+	PyObject *string = PyObject_GetAttrString(o, attr);
+	const gchar *ret = PyString_AsString(string);
+	Py_DECREF(string);
+
+	return ret;
+}
+
+
+static gpointer
+geanypy_load(GeanyPlugin *proxy, GeanyPlugin *subplugin, const gchar *filename, gpointer pdata)
+{
+	GeanyPyData *data = pdata;
+	PyObject *fromlist, *module, *dict, *key, *val, *found = NULL;
+	Py_ssize_t pos = 0;
+	gchar *modulename, *dot;
+	gpointer ret = NULL;
+
+	modulename = g_path_get_basename(filename);
+	/* We are guaranteed that filename has a .py extension
+	 * because we did geany_plugin_register_proxy() for it */
+	dot = strrchr(modulename, '.');
+	*dot = '\0';
+	/* we need a fromlist to be able to import modules with a '.' in the
+	 * name. -- libpeas */
+	fromlist = PyTuple_New (0);
+
+	module = PyImport_ImportModuleEx(modulename, NULL, NULL, fromlist);
+	if (has_error() || !module)
+		goto err;
+
+	dict = PyModule_GetDict(module);
+
+	while (PyDict_Next (dict, &pos, &key, &val) && found == NULL)
+	{
+		if (PyType_Check(val) && PyObject_IsSubclass(val, data->base))
+			found = val;
+	}
+
+	if (found)
+	{
+		GeanyPyPluginData *pdata = g_slice_new(GeanyPyPluginData);
+		PluginInfo *info     = subplugin->info;
+		GeanyPluginFuncs *funcs = subplugin->funcs;
+		Py_INCREF(found);
+		pdata->module        = module;
+		pdata->class         = found;
+		pdata->instance      = NULL;
+		info->name           = string_from_attr(pdata->class, "__plugin_name__");
+		info->description    = string_from_attr(pdata->class, "__plugin_description__");
+		info->version        = string_from_attr(pdata->class, "__plugin_version__");
+		info->author         = string_from_attr(pdata->class, "__plugin_author__");
+		funcs->init          = geanypy_proxy_init;
+		funcs->cleanup       = geanypy_proxy_cleanup;
+		if (PyObject_HasAttrString(found, "configure"))
+			funcs->configure = geanypy_proxy_configure;
+		if (PyObject_HasAttrString(found, "help"))
+			funcs->help      = geanypy_proxy_help;
+		if (GEANY_PLUGIN_REGISTER_FULL(subplugin, 224, pdata, NULL))
+			ret              = pdata;
+	}
+
+err:
+	g_free(modulename);
+	Py_DECREF(fromlist);
+	return ret;
 }
 
 
 static void
-on_python_plugin_loader_activate(GtkMenuItem *item, gpointer user_data)
+geanypy_unload(GeanyPlugin *plugin, GeanyPlugin *subplugin, gpointer load_data, gpointer pdata_)
 {
-    GeanyPy_show_manager();
+	GeanyPyPluginData *pdata = load_data;
+
+	Py_XDECREF(pdata->instance);
+	Py_DECREF(pdata->class);
+	Py_DECREF(pdata->module);
+	while (PyGC_Collect());
+	g_slice_free(GeanyPyPluginData, pdata);
 }
 
 
-G_MODULE_EXPORT void
-plugin_init(GeanyData *data)
+static gboolean geanypy_init(GeanyPlugin *plugin_, gpointer pdata)
 {
-    GeanyPy_start_interpreter();
-    signal_manager = signal_manager_new(geany_plugin);
-
-    plugin_dir = g_build_filename(geany->app->configdir,
-		"plugins", "geanypy", "plugins", NULL);
-
-    if (!g_file_test(plugin_dir, G_FILE_TEST_IS_DIR))
-    {
-        if (g_mkdir_with_parents(plugin_dir, 0755) == -1)
-        {
-            g_warning(_("Unable to create Python plugins directory: %s: %s"),
-                plugin_dir,
-                strerror(errno));
-            g_free(plugin_dir);
-            plugin_dir = NULL;
-        }
-    }
-
-    if (plugin_dir != NULL)
-        GeanyPy_init_manager(plugin_dir);
-
-    loader_item = gtk_menu_item_new_with_label(_("Python Plugin Manager"));
-	gtk_widget_set_sensitive(loader_item, plugin_dir != NULL);
-	gtk_menu_append(GTK_MENU(geany->main_widgets->tools_menu), loader_item);
-	gtk_widget_show(loader_item);
-	g_signal_connect(loader_item, "activate",
-		G_CALLBACK(on_python_plugin_loader_activate), NULL);
+	const gchar *exts[] = { "py", NULL };
+	GeanyPyData *state = pdata;
+	PyObject *module;
+
+	plugin_->proxy_funcs->probe   = geanypy_probe;
+	plugin_->proxy_funcs->load    = geanypy_load;
+	plugin_->proxy_funcs->unload  = geanypy_unload;
+
+	geany_data = plugin_->geany_data;
+
+	GeanyPy_start_interpreter();
+	state->signal_manager = signal_manager_new(plugin_);
+
+	module = PyImport_ImportModule("geany.plugin");
+	if (has_error() || !module)
+		goto err;
+
+	state->base = PyObject_GetAttrString(module, "Plugin");
+	Py_DECREF(module);
+	if (has_error() || !state->base)
+		goto err;
+
+	if (!geany_plugin_register_proxy(plugin_, exts)) {
+		Py_DECREF(state->base);
+		goto err;
+	}
+
+	return TRUE;
+
+err:
+	signal_manager_free(state->signal_manager);
+	GeanyPy_stop_interpreter();
+	return FALSE;
 }
 
 
-G_MODULE_EXPORT void plugin_cleanup(void)
+static void geanypy_cleanup(GeanyPlugin *plugin, gpointer pdata)
 {
-    signal_manager_free(signal_manager);
-    Py_XDECREF(manager);
+	GeanyPyData *state = pdata;
+	signal_manager_free(state->signal_manager);
+	Py_DECREF(state->base);
 	GeanyPy_stop_interpreter();
-    gtk_widget_destroy(loader_item);
-    g_free(plugin_dir);
+}
+
+G_MODULE_EXPORT void
+geany_load_module(GeanyPlugin *plugin)
+{
+	GeanyPyData *state = g_new0(GeanyPyData, 1);
+
+	plugin->info->name        = _("GeanyPy");
+	plugin->info->description = _("Python plugins support");
+	plugin->info->version     = "1.0";
+	plugin->info->author      = "Matthew Brush <mbrush at codebrainz.ca>";
+	plugin->funcs->init       = geanypy_init;
+	plugin->funcs->cleanup    = geanypy_cleanup;
+
+	GEANY_PLUGIN_REGISTER_FULL(plugin, 226, state, g_free);
 }


Modified: geanypy/src/geanypy-plugin.h
5 lines changed, 1 insertions(+), 4 deletions(-)
===================================================================
@@ -26,10 +26,7 @@
 extern "C" {
 #endif
 
-
-extern GeanyPlugin		*geany_plugin;
-extern GeanyData		*geany_data;
-extern GeanyFunctions	*geany_functions;
+extern GeanyData *geany_data;
 
 
 #ifndef PyMODINIT_FUNC


Modified: geanypy/src/geanypy-signalmanager.c
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -88,6 +88,7 @@ GObject *signal_manager_get_gobject(SignalManager *signal_manager)
 
 static void signal_manager_connect_signals(SignalManager *man)
 {
+	GeanyPlugin *geany_plugin = man->geany_plugin;
 	plugin_signal_connect(geany_plugin, NULL, "build-start", TRUE, G_CALLBACK(on_build_start), man);
 	plugin_signal_connect(geany_plugin, NULL, "document-activate", TRUE, G_CALLBACK(on_document_activate), man);
 	plugin_signal_connect(geany_plugin, NULL, "document-before-save", TRUE, G_CALLBACK(on_document_before_save), man);



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Plugins-Commits mailing list