[QGIS Commit] r12690 - in trunk/qgis: python src/python

svn_qgis at osgeo.org svn_qgis at osgeo.org
Thu Jan 7 09:59:28 EST 2010


Author: wonder
Date: 2010-01-07 09:59:27 -0500 (Thu, 07 Jan 2010)
New Revision: 12690

Modified:
   trunk/qgis/python/utils.py
   trunk/qgis/src/python/qgspythonutilsimpl.cpp
Log:
Python support: use a wrapper for import statement that tracks modules of plugins.
This enables complete unload and reload of plugins:
- qgis.utils.unloadPlugin(name) - now removes also plugin's modules (files) from python
- qgis.utils.reloadPlugin(name) - unloads and starts the plugin again (using fresh source files)


Modified: trunk/qgis/python/utils.py
===================================================================
--- trunk/qgis/python/utils.py	2010-01-07 13:49:33 UTC (rev 12689)
+++ trunk/qgis/python/utils.py	2010-01-07 14:59:27 UTC (rev 12690)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 QGIS utilities module
 
@@ -6,6 +7,9 @@
 from PyQt4.QtCore import QCoreApplication
 import sys
 import traceback
+import glob
+import os.path
+import re
 
 
 #######################
@@ -94,6 +98,30 @@
 # list of active (started) plugins
 active_plugins = []
 
+# list of plugins in plugin directory and home plugin directory
+available_plugins = []
+
+
+def updateAvailablePlugins():
+  from qgis.core import QgsApplication
+  pythonPath = unicode(QgsApplication.pkgDataPath()) + "/python"
+  homePythonPath = unicode(QgsApplication.qgisSettingsDirPath()) + "/python"
+
+  plugins = map(os.path.basename, glob.glob(pythonPath + "/plugins/*"))
+  homePlugins = map(os.path.basename, glob.glob(homePythonPath + "/plugins/*"))
+
+  # merge the lists
+  for p in homePlugins:
+    if p not in plugins:
+      plugins.append(p)
+
+  global available_plugins
+  available_plugins = plugins
+
+# update list on start
+updateAvailablePlugins()
+
+
 def pluginMetadata(packageName, fct):
   """ fetch metadata from a plugin """
   try:
@@ -103,7 +131,7 @@
     return "__error__"
 
 def loadPlugin(packageName):
-  """ load plugin's package and ensure that plugin is reloaded when changed """
+  """ load plugin's package """
 
   try:
     __import__(packageName)
@@ -117,7 +145,6 @@
   # retry
   try:
     __import__(packageName)
-    reload(packageName)
     return True
   except:
     msgTemplate = QCoreApplication.translate("Python", "Couldn't load plugin '%1' from ['%2']")
@@ -140,6 +167,7 @@
   try:
     plugins[packageName] = package.classFactory(iface)
   except:
+    _unloadPluginModules(packageName)
     msg = QCoreApplication.translate("Python", "%1 due an error when calling its classFactory() method").arg(errMsg)
     showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
     return False
@@ -148,6 +176,8 @@
   try:
     plugins[packageName].initGui()
   except:
+    del plugins[packageName]
+    _unloadPluginModules(packageName)
     msg = QCoreApplication.translate("Python", "%1 due an error when calling its initGui() method" ).arg( errMsg )
     showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
     return False
@@ -169,15 +199,77 @@
     plugins[packageName].unload()
     del plugins[packageName]
     active_plugins.remove(packageName)
+    _unloadPluginModules(packageName)
     return True
   except Exception, e:
     msg = QCoreApplication.translate("Python", "Error while unloading plugin %1").arg(packageName)
     showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
     return False
 
+
+def _unloadPluginModules(packageName):
+  """ unload plugin package with all its modules (files) """
+  global _plugin_modules
+  mods = _plugin_modules[packageName]
+
+  for mod in mods:
+    # if it looks like a Qt resource file, try to do a cleanup
+    # otherwise we might experience a segfault next time the plugin is loaded
+    # because Qt will try to access invalid plugin resource data
+    if "resources" in mod:
+      try:
+	sys.modules[mod].qCleanupResources()
+      except:
+	pass
+    # try to remove the module from python
+    try:
+      del sys.modules[mod]
+    except:
+      pass
+  # remove the plugin entry
+  del _plugin_modules[packageName]
+
+
 def isPluginLoaded(packageName):
   """ find out whether a plugin is active (i.e. has been started) """
   global plugins, active_plugins
 
   if not plugins.has_key(packageName): return False
   return (packageName in active_plugins)
+
+
+def reloadPlugin(packageName):
+  """ unload and start again a plugin """
+  global active_plugins
+  if packageName not in active_plugins:
+    return # it's not active
+
+  unloadPlugin(packageName)
+  loadPlugin(packageName)
+  startPlugin(packageName)
+
+
+#######################
+# IMPORT wrapper
+
+import __builtin__
+
+_builtin_import = __builtin__.__import__
+_plugin_modules = { }
+
+def _import(name, globals={}, locals={}, fromlist=[], level=-1):
+  """ wrapper around builtin import that keeps track of loaded plugin modules """
+  mod = _builtin_import(name, globals, locals, fromlist, level)
+
+  if mod and '__file__' in mod.__dict__:
+    module_name = mod.__name__
+    package_name = module_name.split('.')[0]
+    # check whether the module belongs to one of our plugins 
+    if package_name in available_plugins:
+      if package_name not in _plugin_modules:
+        _plugin_modules[package_name] = set()
+      _plugin_modules[package_name].add(module_name)
+    
+  return mod
+
+__builtin__.__import__ = _import

Modified: trunk/qgis/src/python/qgspythonutilsimpl.cpp
===================================================================
--- trunk/qgis/src/python/qgspythonutilsimpl.cpp	2010-01-07 13:49:33 UTC (rev 12689)
+++ trunk/qgis/src/python/qgspythonutilsimpl.cpp	2010-01-07 14:59:27 UTC (rev 12690)
@@ -424,23 +424,11 @@
 
 QStringList QgsPythonUtilsImpl::pluginList()
 {
-  QDir pluginDir( QgsPythonUtilsImpl::pluginsPath(), "*",
-                  QDir::Name | QDir::IgnoreCase, QDir::Dirs | QDir::NoDotAndDotDot );
+  runString( "qgis.utils.updateAvailablePlugins()" );
 
-  QDir homePluginDir( QgsPythonUtilsImpl::homePluginsPath(), "*",
-                      QDir::Name | QDir::IgnoreCase, QDir::Dirs | QDir::NoDotAndDotDot );
-
-  QStringList pluginList = pluginDir.entryList();
-
-  for ( uint i = 0; i < homePluginDir.count(); i++ )
-  {
-    QString packageName = homePluginDir[i];
-    if ( !pluginList.contains( packageName ) )
-      pluginList.append( packageName );
-
-  }
-
-  return pluginList;
+  QString output;
+  evalString( "'\\n'.join(qgis.utils.available_plugins)", output );
+  return output.split( QChar( '\n' ) );
 }
 
 QString QgsPythonUtilsImpl::getPluginMetadata( QString pluginName, QString function )



More information about the QGIS-commit mailing list