From 9b926bc8f333757d3337951c3cfcf3804839d02d Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Fri, 23 Dec 2011 01:05:22 +0100 Subject: [PATCH 01/34] adaptation to python3 - syntax and module name changes made by 2to3 - move main outside 'test' directory and call it runtests (main was causing pbs with the new relatives imports vs __main__ see http://bugs.python.org/issue1762972) - clean up useless "import *" and various indendation incoherencies --HG-- branch : python3-transition rename : package/test/main.py => package/runtests.py --- package/doc/conf.py | 8 +- package/{test/main.py => runtests.py} | 6 +- package/test/plugins/ConfigPlugin.py | 2 - package/test/plugins/SimplePlugin.py | 2 - package/test/plugins/VersionedPlugin10.py | 3 +- package/test/plugins/VersionedPlugin11.py | 3 +- package/test/plugins/VersionedPlugin111.py | 3 +- package/test/plugins/VersionedPlugin12.py | 3 +- package/test/plugins/VersionedPlugin12a1.py | 3 +- .../pluginstoinstall/AutoInstallPlugin.py | 2 - .../autoinstallWRONGzipplugin.zip | Bin 1133 -> 1122 bytes .../pluginstoinstall/autoinstallZIPplugin.zip | Bin 1124 -> 1113 bytes .../autoinstalldirplugin/__init__.py | 2 - package/test/test_All.py | 12 +- package/test/test_AutoInstallPlugin.py | 14 +- package/test/test_ConfigPlugin.py | 10 +- package/test/test_FilterPlugin.py | 305 +++++++++--------- package/test/test_SimplePlugin.py | 13 +- package/test/test_Singleton.py | 10 +- package/test/test_VersionedPlugin.py | 4 +- package/yapsy/AutoInstallPluginManager.py | 8 +- package/yapsy/ConfigurablePluginManager.py | 4 +- package/yapsy/PluginManager.py | 31 +- 23 files changed, 221 insertions(+), 227 deletions(-) rename package/{test/main.py => runtests.py} (93%) diff --git a/package/doc/conf.py b/package/doc/conf.py index 28173d0..4ee21a1 100644 --- a/package/doc/conf.py +++ b/package/doc/conf.py @@ -40,8 +40,8 @@ master_doc = 'index' # General information about the project. -project = u'Yapsy' -copyright = u'2010-2011, Thibauld Nion' +project = 'Yapsy' +copyright = '2010-2011, Thibauld Nion' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -183,8 +183,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Yapsy.tex', u'Yapsy Documentation', - u'Thibauld Nion', 'manual'), + ('index', 'Yapsy.tex', 'Yapsy Documentation', + 'Thibauld Nion', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/package/test/main.py b/package/runtests.py similarity index 93% rename from package/test/main.py rename to package/runtests.py index 72498e8..a82a08f 100644 --- a/package/test/main.py +++ b/package/runtests.py @@ -13,7 +13,7 @@ import logging -from test_All import MainTestSuite +from test.test_All import MainTestSuite def usage(): """ @@ -38,13 +38,13 @@ def main(argv): try: opts, args = getopt.getopt(argv[1:], "vdh", ["help"]) except getopt.GetoptError: - print usage() + print(usage()) sys.exit(2) loglevel = logging.ERROR test_verbosity = 1 for o,a in opts: if o in ("-h","--help"): - print usage() + print(usage()) sys.exit(0) elif o == "-d": loglevel = logging.DEBUG diff --git a/package/test/plugins/ConfigPlugin.py b/package/test/plugins/ConfigPlugin.py index 642fe0a..656e145 100644 --- a/package/test/plugins/ConfigPlugin.py +++ b/package/test/plugins/ConfigPlugin.py @@ -7,8 +7,6 @@ This is certainly the second simplest plugin ever. """ -import test_settings -import main from yapsy.IPlugin import IPlugin class ConfigPlugin(IPlugin): diff --git a/package/test/plugins/SimplePlugin.py b/package/test/plugins/SimplePlugin.py index fb6355e..64625a3 100644 --- a/package/test/plugins/SimplePlugin.py +++ b/package/test/plugins/SimplePlugin.py @@ -7,8 +7,6 @@ This is certainly the second simplest plugin ever. """ -import test_settings -import main from yapsy.IPlugin import IPlugin class SimplePlugin(IPlugin): diff --git a/package/test/plugins/VersionedPlugin10.py b/package/test/plugins/VersionedPlugin10.py index d1ca29b..f673cee 100644 --- a/package/test/plugins/VersionedPlugin10.py +++ b/package/test/plugins/VersionedPlugin10.py @@ -7,8 +7,7 @@ This is certainly the second simplest plugin ever. """ -from test_settings import * -import main +from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin class VersionedPlugin10(IPlugin): diff --git a/package/test/plugins/VersionedPlugin11.py b/package/test/plugins/VersionedPlugin11.py index 35f1893..3c62290 100644 --- a/package/test/plugins/VersionedPlugin11.py +++ b/package/test/plugins/VersionedPlugin11.py @@ -7,8 +7,7 @@ This is certainly the second simplest plugin ever. """ -from test_settings import * -import main +from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin class VersionedPlugin11(IPlugin): diff --git a/package/test/plugins/VersionedPlugin111.py b/package/test/plugins/VersionedPlugin111.py index 1f44fce..5e39d91 100644 --- a/package/test/plugins/VersionedPlugin111.py +++ b/package/test/plugins/VersionedPlugin111.py @@ -7,8 +7,7 @@ This is certainly the second simplest plugin ever. """ -from test_settings import * -import main +from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin class VersionedPlugin111(IPlugin): diff --git a/package/test/plugins/VersionedPlugin12.py b/package/test/plugins/VersionedPlugin12.py index e5ff740..56c649f 100644 --- a/package/test/plugins/VersionedPlugin12.py +++ b/package/test/plugins/VersionedPlugin12.py @@ -7,8 +7,7 @@ This is certainly the second simplest plugin ever. """ -from test_settings import * -import main +from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin class VersionedPlugin12(IPlugin): diff --git a/package/test/plugins/VersionedPlugin12a1.py b/package/test/plugins/VersionedPlugin12a1.py index bca7262..420baa4 100644 --- a/package/test/plugins/VersionedPlugin12a1.py +++ b/package/test/plugins/VersionedPlugin12a1.py @@ -7,8 +7,7 @@ This is certainly the second simplest plugin ever. """ -from test_settings import * -import main +from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin class VersionedPlugin12a1(IPlugin): diff --git a/package/test/pluginstoinstall/AutoInstallPlugin.py b/package/test/pluginstoinstall/AutoInstallPlugin.py index 2ca11b9..dbe2a03 100644 --- a/package/test/pluginstoinstall/AutoInstallPlugin.py +++ b/package/test/pluginstoinstall/AutoInstallPlugin.py @@ -7,8 +7,6 @@ This is certainly the second simplest plugin ever. """ -import test_settings -import main from yapsy.IPlugin import IPlugin class AutoInstallPlugin(IPlugin): diff --git a/package/test/pluginstoinstall/autoinstallWRONGzipplugin.zip b/package/test/pluginstoinstall/autoinstallWRONGzipplugin.zip index 008c1efd90ed0e817503a2feb4cbf83c8431f301..ee8b5862da7528c831ab8b84bed30e5453c93a41 100644 GIT binary patch delta 458 zcmaFM@rYxCG9#}F`*izrSFdjhV`N~MG1-7or~dNc&we0WTEWf0$nt`jfdNd^PCA%( z$bhHq{1Z(Zivwzb>n`V67d0LB+r41IuG}pXLN*>gcKf_%w1iBU>(oj2|9!ss@lobv zjl8~V`M-`S&OR^xdNGnIl1(}L^e9b3)UW+ z@!Z_>bl&FJoffyY9gSf4u-yG{1oQLP%0WdwR|VFlnVJyb=4hO^HKi7>KTE)?a#84 zTqFD{zY0k$kNzR{Ehe!2=dYlMx;#lG$vI0iCdqhZRZi0Wv34Kl&xxA-xAX(NnOV3P vK)}I!syze4ibwe`GIh01vdjD%L`@(1~5@OX=BkL z1D>|@KO$N@7w!pOi`(AkV^H#Hl5yXpA6akas9yf`ZK^L*>%yHnm-7GqQ_23g#Z$zb z{k8ewnZF-gWxA%HZLo0TzUt;9Z-1GlPpSF*Gp6>-G>5w8iCe$E&^+OWb@ar2JLQ*62>W+hu3isUa@x9zw0PmTVCLB}$cD*R=( z&5N-9zVmMUma7%de)^vMRJu7{UW|y&YHUZMXdY3nk(8H sJ&pat8sN>$!ZrCE(@C^gS;B0^42+S<51FkPuS}Lv`*izrSFdjhV`N~M!NkCzz#ubOfkkZc1!mrQP6pJ7emeWS`0K?;rbssB z?ALc$cQ`AU9K7yO|LuykO544q*QyQ}A6vC#rCOVCOo{*WtljbU{`CJuPn`UZOU=fl3 zvmionmzp%M-{zXCyE+Q_Ys;nyRLNd67SXxr*7oY2NAI^=8*H zXw66Y3#(@Y`nEsIPI8U#tNbb?wLJQV*teL#_Mg9kBI@!al_cjZ&6p(Pl~p-O`^VaS zoIfXO_TSPE@MdP=VgLaL@2Qh-FdgJYiJ!^Kn5{rjGWjvH72|=)ax4yP{J(F@s delta 465 zcmV;?0WSX82;>N`8UYg*iIqP{ZH;eX0RRBG0ssIY034IA10#P400WK$@=lHf@=kSl z3jhHG=mP)%1n2_*0DY3Na>6hShG+Z~Dh$xx5!PO}j!ZCg>XI{#lK5)uoF&up_LWTn zg`v4D(r^9#Bx~`sNfb8+>o>!h`{30A)-P)?p|QSugT(FnHD{E3^Z8tT^p-$=gORoM z?hM=`5Sri{AbWot91_FeQfEEj31d~tlqexGu&6a5v$)fgJc^NOV>rTD4|J}!`%C#K zkD*e=X(D(}9JZcVJNLQWU$oVD=J`#{@^!OaA&a)_Fcw1m8PG5`g#aQ_a!l3bw3;>8 zLS0*Lxvp1)uFXalr&-Qkg9YT!YG(;y&=EaD!4hp`sP2D_)M&2_+mmK|wBPwjvcU_T zbHb}-QhCt5CO9B^pkxdZwaK~a4@`$#y3;a~qc|8CVlddGh)@PP&F)5b&mlrYPSH=8 z4gLdq6*H>o(R@v;2+OIiSwib{2j9Y;JL)lCUmDB%AX_GHzZ6Z1Ln$p!as|KjHz7YV z H00000yXwtq diff --git a/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py b/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py index b0df239..f00f90a 100644 --- a/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py +++ b/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py @@ -7,8 +7,6 @@ This is certainly the second simplest plugin ever. """ -import test_settings -import main from yapsy.IPlugin import IPlugin class AutoInstallDirPlugin(IPlugin): diff --git a/package/test/test_All.py b/package/test/test_All.py index ec1f2c8..fffa766 100644 --- a/package/test/test_All.py +++ b/package/test/test_All.py @@ -13,12 +13,12 @@ # load the tests -import test_SimplePlugin -import test_Singleton -import test_ConfigPlugin -import test_VersionedPlugin -import test_AutoInstallPlugin -import test_FilterPlugin +from . import test_SimplePlugin +from . import test_Singleton +from . import test_ConfigPlugin +from . import test_VersionedPlugin +from . import test_AutoInstallPlugin +from . import test_FilterPlugin # add them to a common test suite diff --git a/package/test/test_AutoInstallPlugin.py b/package/test/test_AutoInstallPlugin.py index c46aa52..534fca1 100644 --- a/package/test/test_AutoInstallPlugin.py +++ b/package/test/test_AutoInstallPlugin.py @@ -1,4 +1,4 @@ -import test_settings +from . import test_settings import unittest import os import shutil @@ -38,25 +38,25 @@ def tearDown(self): try: os.remove(os.path.join(self.pluginManager.plugins_places[0], "autoinstallplugin.yapsy-autoinstall-plugin")) - except OSError,e: + except OSError as e: # print e pass try: os.remove(os.path.join(self.pluginManager.plugins_places[0], "AutoInstallPlugin.py")) - except OSError,e: + except OSError as e: # print e pass try: os.remove(os.path.join(self.pluginManager.plugins_places[0], "autoinstalldirplugin.yapsy-autoinstall-plugin")) - except OSError,e: + except OSError as e: # print e pass try: shutil.rmtree(os.path.join(self.pluginManager.plugins_places[0], "autoinstalldirplugin")) - except OSError,e: + except OSError as e: # print e pass @@ -165,13 +165,13 @@ def tearDown(self): try: os.remove(os.path.join(self.pluginManager.plugins_places[0], "autoinstallzipplugin.yapsy-autoinstall-plugin")) - except OSError,e: + except OSError as e: # print e pass try: shutil.rmtree(os.path.join(self.pluginManager.plugins_places[0], "autoinstallzipplugin")) - except OSError,e: + except OSError as e: # print e pass diff --git a/package/test/test_ConfigPlugin.py b/package/test/test_ConfigPlugin.py index a2e673a..f071e79 100644 --- a/package/test/test_ConfigPlugin.py +++ b/package/test/test_ConfigPlugin.py @@ -1,8 +1,8 @@ -import test_settings +from . import test_settings import os import unittest -import ConfigParser +import configparser from yapsy.ConfigurablePluginManager import ConfigurablePluginManager @@ -20,8 +20,8 @@ def setUp(self): init """ # create a config file - self.config_file = self.CONFIG_FILE - self.config_parser = ConfigParser.SafeConfigParser() + self.config_file = self.CONFIG_FILE + self.config_parser = configparser.SafeConfigParser() self.plugin_info = None # create the plugin manager self.pluginManager = ConfigurablePluginManager( @@ -49,7 +49,7 @@ def testConfigurationFileExistence(self): # get rid of the plugin manager and create a new one del self.pluginManager del self.config_parser - self.config_parser = ConfigParser.SafeConfigParser() + self.config_parser = configparser.SafeConfigParser() self.config_parser.read(self.config_file) self.assert_(self.config_parser.has_section("Plugin Management")) self.assert_(self.config_parser.has_option("Plugin Management", diff --git a/package/test/test_FilterPlugin.py b/package/test/test_FilterPlugin.py index 0b66d2b..34991dc 100644 --- a/package/test/test_FilterPlugin.py +++ b/package/test/test_FilterPlugin.py @@ -1,5 +1,8 @@ -import test_settings -from test_settings import TEST_MESSAGE +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- + +from . import test_settings +from .test_settings import TEST_MESSAGE import unittest import os import re @@ -19,157 +22,157 @@ def isPluginOk(self,info): class FilteredTestsCase(unittest.TestCase): - """ - Test the correct loading of a simple plugin as well as basic - commands. - """ - - def setUp(self): - """ - init - """ - # create the plugin manager -# print os.path.join(os.path.dirname(os.path.abspath(__file__)),"plugins") - self.filteredPluginManager = testFilter( - directories_list=[os.path.join( + """ + Test the correct loading of a simple plugin as well as basic + commands. + """ + + def setUp(self): + """ + init + """ + # create the plugin manager +# print os.path.join(os.path.dirname(os.path.abspath(__file__)),"plugins") + self.filteredPluginManager = testFilter( + directories_list=[os.path.join( os.path.dirname(os.path.abspath(__file__)),"plugins")], - plugin_info_ext="yapsy-filter-plugin", - ) - # load the plugins that may be found - self.filteredPluginManager.collectPlugins() - # Will be used later - self.plugin_info = None - - def plugin_loading_check(self): - """ - Test if the correct plugins have been loaded. - """ - # check nb of categories - self.assertEqual(len(self.filteredPluginManager.getCategories()),1) - sole_category = self.filteredPluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.filteredPluginManager.getPluginsOfCategory(sole_category)),1) - plugins = self.filteredPluginManager.getPluginsOfCategory(sole_category) - for plugin_info in plugins: - TEST_MESSAGE("plugin info: %s" % plugin_info) - self.plugin_info = plugin_info - self.assert_(self.plugin_info) - self.assertEqual(self.plugin_info.name,"Simple Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - - def testLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - - - def testActivationAndDeactivation(self): - """ - Test if the activation procedure works. - """ - self.plugin_loading_check() - self.assert_(not self.plugin_info.plugin_object.is_activated) - TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) - self.plugin_info.plugin_object.activate() - self.assert_(self.plugin_info.plugin_object.is_activated) - self.plugin_info.plugin_object.deactivate() - self.assert_(not self.plugin_info.plugin_object.is_activated) - - - def testRejectedList(self): - """ - Test if the list of rejected plugins is correct. - """ - for plugin in self.filteredPluginManager.getRejectedPlugins(): - TEST_MESSAGE("plugin info: %s" % plugin[2]) - self.assertEqual(plugin[2].name,"Config Plugin") - - def testRejectedStable(self): - reject1 = list(self.filteredPluginManager.getRejectedPlugins()) - self.filteredPluginManager.collectPlugins() - reject2 = list(self.filteredPluginManager.getRejectedPlugins()) - self.assertEqual(len(reject1),len(reject2)) - - - def testRejectPlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.rejectedPlugins - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - nrRejected = len(rejected) - for plugin in rejected: - self.filteredPluginManager.rejectPluginCandidate(plugin) - self.assertEqual(nrRejected,len(self.filteredPluginManager.rejectedPlugins)) - - def testRemovePlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.rejectedPlugins - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - for plugin in rejected: - self.filteredPluginManager.removePluginCandidate(plugin) - self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual( nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) - - def testAppendRejectedPlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.getRejectedPlugins() - nrRejected = len(rejected) - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - #Remove the rejected plugins into out own list. - for plugin in rejected: - self.filteredPluginManager.removePluginCandidate(plugin) - self.assertEquals(len(self.filteredPluginManager.getRejectedPlugins()),0) - - ##Now Actually test Append. - for plugin in rejected: - self.filteredPluginManager.appendPluginCandidate(plugin) - self.assertEqual(nrRejected ,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual(nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) - - def testAppendOkPlugins(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.getRejectedPlugins() - nrRejected = len(rejected) - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - #Remove the rejected plugins again. - for plugin in rejected: - self.filteredPluginManager.removePluginCandidate(plugin) - self.assertEquals(len(self.filteredPluginManager.getRejectedPlugins()),0) - - for plugin in rejected: - #change the name so it is acceptable. - plugin[2].name = "X" + plugin[2].name[1:] - self.filteredPluginManager.appendPluginCandidate(plugin) - self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual(nrRejected + nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) - - - - - def testUnrejectPlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.rejectedPlugins - nrRejected = len(rejected) - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - for plugin in rejected: - self.filteredPluginManager.unrejectPluginCandidate(plugin) - self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual( nrRejected + nrCandidates , - len(self.filteredPluginManager.getPluginCandidates())) + plugin_info_ext="yapsy-filter-plugin", + ) + # load the plugins that may be found + self.filteredPluginManager.collectPlugins() + # Will be used later + self.plugin_info = None + + def plugin_loading_check(self): + """ + Test if the correct plugins have been loaded. + """ + # check nb of categories + self.assertEqual(len(self.filteredPluginManager.getCategories()),1) + sole_category = self.filteredPluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual(len(self.filteredPluginManager.getPluginsOfCategory(sole_category)),1) + plugins = self.filteredPluginManager.getPluginsOfCategory(sole_category) + for plugin_info in plugins: + TEST_MESSAGE("plugin info: %s" % plugin_info) + self.plugin_info = plugin_info + self.assert_(self.plugin_info) + self.assertEqual(self.plugin_info.name,"Simple Plugin") + self.assertEqual(sole_category,self.plugin_info.category) + + def testLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + + + def testActivationAndDeactivation(self): + """ + Test if the activation procedure works. + """ + self.plugin_loading_check() + self.assert_(not self.plugin_info.plugin_object.is_activated) + TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) + self.plugin_info.plugin_object.activate() + self.assert_(self.plugin_info.plugin_object.is_activated) + self.plugin_info.plugin_object.deactivate() + self.assert_(not self.plugin_info.plugin_object.is_activated) + + + def testRejectedList(self): + """ + Test if the list of rejected plugins is correct. + """ + for plugin in self.filteredPluginManager.getRejectedPlugins(): + TEST_MESSAGE("plugin info: %s" % plugin[2]) + self.assertEqual(plugin[2].name,"Config Plugin") + + def testRejectedStable(self): + reject1 = list(self.filteredPluginManager.getRejectedPlugins()) + self.filteredPluginManager.collectPlugins() + reject2 = list(self.filteredPluginManager.getRejectedPlugins()) + self.assertEqual(len(reject1),len(reject2)) + + + def testRejectPlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.rejectedPlugins + #If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + nrRejected = len(rejected) + for plugin in rejected: + self.filteredPluginManager.rejectPluginCandidate(plugin) + self.assertEqual(nrRejected,len(self.filteredPluginManager.rejectedPlugins)) + + def testRemovePlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.rejectedPlugins + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + #If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + for plugin in rejected: + self.filteredPluginManager.removePluginCandidate(plugin) + self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) + self.assertEqual( nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) + + def testAppendRejectedPlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.getRejectedPlugins() + nrRejected = len(rejected) + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + + #If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + #Remove the rejected plugins into out own list. + for plugin in rejected: + self.filteredPluginManager.removePluginCandidate(plugin) + self.assertEquals(len(self.filteredPluginManager.getRejectedPlugins()),0) + + ##Now Actually test Append. + for plugin in rejected: + self.filteredPluginManager.appendPluginCandidate(plugin) + self.assertEqual(nrRejected ,len(self.filteredPluginManager.rejectedPlugins)) + self.assertEqual(nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) + + def testAppendOkPlugins(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.getRejectedPlugins() + nrRejected = len(rejected) + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + + #If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + #Remove the rejected plugins again. + for plugin in rejected: + self.filteredPluginManager.removePluginCandidate(plugin) + self.assertEquals(len(self.filteredPluginManager.getRejectedPlugins()),0) + + for plugin in rejected: + #change the name so it is acceptable. + plugin[2].name = "X" + plugin[2].name[1:] + self.filteredPluginManager.appendPluginCandidate(plugin) + self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) + self.assertEqual(nrRejected + nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) + + + + + def testUnrejectPlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.rejectedPlugins + nrRejected = len(rejected) + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + #If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + for plugin in rejected: + self.filteredPluginManager.unrejectPluginCandidate(plugin) + self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) + self.assertEqual( nrRejected + nrCandidates , + len(self.filteredPluginManager.getPluginCandidates())) suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(FilteredTestsCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(FilteredTestsCase), + ]) diff --git a/package/test/test_SimplePlugin.py b/package/test/test_SimplePlugin.py index 2dc8531..3712dba 100644 --- a/package/test/test_SimplePlugin.py +++ b/package/test/test_SimplePlugin.py @@ -1,6 +1,7 @@ +#!/usr/bin/python # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- -import test_settings +from . import test_settings import unittest import os @@ -77,7 +78,7 @@ class SimplePluginAdvancedManipulationTestsCase(unittest.TestCase): Test some advanced manipulation on the core data of a PluginManager. """ - + def testCategoryManipulation(self): """ Test querying, removing and adding plugins from/to a category. @@ -89,13 +90,13 @@ def testCategoryManipulation(self): spm.collectPlugins() # check that the getCategories works self.assertEqual(len(spm.getCategories()),1) - sole_category = spm.getCategories()[0] + sole_category = spm.getCategories()[0] # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) - plugin_info = spm.getPluginsOfCategory(sole_category)[0] + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) + plugin_info = spm.getPluginsOfCategory(sole_category)[0] # try to remove it and check that is worked spm.removePluginFromCategory(plugin_info,sole_category) - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),0) + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),0) # now re-add this plugin the to same category spm.appendPluginToCategory(plugin_info,sole_category) self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) diff --git a/package/test/test_Singleton.py b/package/test/test_Singleton.py index f170e6c..e1b6d7a 100644 --- a/package/test/test_Singleton.py +++ b/package/test/test_Singleton.py @@ -1,8 +1,10 @@ -import test_settings +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- + +from . import test_settings import os import unittest -import ConfigParser +import configparser from yapsy.ConfigurablePluginManager import ConfigurablePluginManager from yapsy.VersionedPluginManager import VersionedPluginManager @@ -26,8 +28,8 @@ def setUp(self): init """ # create a config file - self.config_file = self.CONFIG_FILE - self.config_parser = ConfigParser.SafeConfigParser() + self.config_file = self.CONFIG_FILE + self.config_parser = configparser.SafeConfigParser() self.plugin_info = None # create the plugin manager diff --git a/package/test/test_VersionedPlugin.py b/package/test/test_VersionedPlugin.py index 44d8fd5..7866d52 100644 --- a/package/test/test_VersionedPlugin.py +++ b/package/test/test_VersionedPlugin.py @@ -1,5 +1,5 @@ -import test_settings -from test_settings import TEST_MESSAGE +from . import test_settings +from .test_settings import TEST_MESSAGE import unittest import os diff --git a/package/yapsy/AutoInstallPluginManager.py b/package/yapsy/AutoInstallPluginManager.py index 31834ba..2e89986 100644 --- a/package/yapsy/AutoInstallPluginManager.py +++ b/package/yapsy/AutoInstallPluginManager.py @@ -17,7 +17,7 @@ import logging import shutil import zipfile -import StringIO +import io from yapsy.IPlugin import IPlugin @@ -133,12 +133,12 @@ def installFromZIP(self, plugin_ZIP_filename): """ if not os.path.isfile(plugin_ZIP_filename): logging.warning("Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename) - print "Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename + print("Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename) return False candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename) if candidateZipFile.testzip() is not None: logging.warning("Corruption detected in Zip file '%s'." % plugin_ZIP_filename) - print "Corruption detected in Zip file '%s'." % plugin_ZIP_filename + print("Corruption detected in Zip file '%s'." % plugin_ZIP_filename) return False zipContent = candidateZipFile.namelist() logging.info("Investigating the content of a zip file containing: '%s'" % zipContent) @@ -178,7 +178,7 @@ def installFromZIP(self, plugin_ZIP_filename): for infoFileName in infoFileCandidates: infoFile = candidateZipFile.read(infoFileName) logging.info("Assuming the zipped plugin info file to be '%s'" % infoFileName) - pluginName,moduleName,_ = self._getPluginNameAndModuleFromStream(StringIO.StringIO(infoFile)) + pluginName,moduleName,_ = self._getPluginNameAndModuleFromStream(io.StringIO(str(infoFile,encoding="utf-8"))) if moduleName is None: continue logging.info("Checking existence of the expected module '%s' in the zip file" % moduleName) diff --git a/package/yapsy/ConfigurablePluginManager.py b/package/yapsy/ConfigurablePluginManager.py index f952064..b334576 100644 --- a/package/yapsy/ConfigurablePluginManager.py +++ b/package/yapsy/ConfigurablePluginManager.py @@ -255,11 +255,11 @@ def loadPlugins(self,callback=None): for each plugin candidate look for its category, load it and stores it in the appropriate slot of the ``category_mapping``. """ - self._component.loadPlugins() + self._component.loadPlugins() # now load the plugins according to the recorded configuration if self.config_parser.has_section(self.CONFIG_SECTION_NAME): # browse all the categories - for category_name in self._component.category_mapping.keys(): + for category_name in list(self._component.category_mapping.keys()): # get the list of plugins to be activated for this # category option_name = "%s_plugins_to_load"%category_name diff --git a/package/yapsy/PluginManager.py b/package/yapsy/PluginManager.py index 9e4702c..26f0587 100644 --- a/package/yapsy/PluginManager.py +++ b/package/yapsy/PluginManager.py @@ -65,8 +65,9 @@ Plugin Info File Format ----------------------- -The plugin info file gathers, as its name suggests, some basic -information about the plugin. +The plugin info file is a text file *encoded in ASCII or UTF-8* and +gathering, as its name suggests, some basic information about the +plugin. - it gives crucial information needed to be able to load the plugin @@ -121,7 +122,7 @@ import sys import os import logging -import ConfigParser +import configparser from yapsy.IPlugin import IPlugin from yapsy.PluginInfo import PluginInfo @@ -228,7 +229,7 @@ def getCategories(self): """ Return the list of all categories. """ - return self.category_mapping.keys() + return list(self.category_mapping.keys()) def removePluginFromCategory(self,plugin,category_name): """ @@ -255,7 +256,7 @@ def getAllPlugins(self): Return the list of all plugins (belonging to all categories). """ allPlugins = [] - for pluginsOfOneCategory in self.category_mapping.itervalues(): + for pluginsOfOneCategory in self.category_mapping.values(): allPlugins.extend(pluginsOfOneCategory) return allPlugins @@ -285,24 +286,24 @@ def _getPluginNameAndModuleFromStream(self, infoFileObject, candidate_infofile=" and decorators. """ # parse the information buffer to get info about the plugin - config_parser = ConfigParser.SafeConfigParser() + config_parser = configparser.SafeConfigParser() try: config_parser.readfp(infoFileObject) - except Exception,e: - logging.debug("Could not parse the plugin file '%s' (exception raised was '%s')" % (candidate_infofile,e)) + except Exception as e: + logging.warning("Could not parse the plugin file '%s' (exception raised was '%s')" % (candidate_infofile,e)) return (None, None, None) # check if the basic info is available if not config_parser.has_section("Core"): - logging.debug("Plugin info file has no 'Core' section (in '%s')" % candidate_infofile) + logging.warning("Plugin info file has no 'Core' section (in '%s')" % candidate_infofile) return (None, None, None) if not config_parser.has_option("Core","Name") or not config_parser.has_option("Core","Module"): - logging.debug("Plugin info file has no 'Name' or 'Module' section (in '%s')" % candidate_infofile) + logging.warning("Plugin info file has no 'Name' or 'Module' section (in '%s')" % candidate_infofile) return (None, None, None) # check that the given name is valid name = config_parser.get("Core", "Name") name = name.strip() if PLUGIN_NAME_FORBIDEN_STRING in name: - logging.debug("Plugin name contains forbiden character: %s (in '%s')" % (PLUGIN_NAME_FORBIDEN_STRING, + logging.warning("Plugin name contains forbiden character: %s (in '%s')" % (PLUGIN_NAME_FORBIDEN_STRING, candidate_infofile)) return (None, None, None) return (name,config_parser.get("Core", "Module"), config_parser) @@ -477,9 +478,9 @@ def loadPlugins(self, callback=None): sys.path.append(plugin_info.path) try: candidateMainFile = open(candidate_filepath+".py","r") - exec(candidateMainFile,candidate_globals) - except Exception,e: - logging.debug("Unable to execute the code in plugin: %s" % candidate_filepath) + exec(candidateMainFile.read(),candidate_globals) + except Exception as e: + logging.warning("Unable to execute the code in plugin: %s" % candidate_filepath) logging.debug("\t The following problem occured: %s %s " % (os.linesep, e)) if "__init__" in os.path.basename(candidate_filepath): sys.path.remove(plugin_info.path) @@ -488,7 +489,7 @@ def loadPlugins(self, callback=None): if "__init__" in os.path.basename(candidate_filepath): sys.path.remove(plugin_info.path) # now try to find and initialise the first subclass of the correct plugin interface - for element in candidate_globals.itervalues(): + for element in candidate_globals.values(): current_category = None for category_name in self.categories_interfaces: try: From da6623d2aed7ce03fc9ce3092e3e58b94822a6fb Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Fri, 23 Dec 2011 01:08:48 +0100 Subject: [PATCH 02/34] preparing a python3 specific release --HG-- branch : python3-transition --- package/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/setup.py b/package/setup.py index e000204..c59676f 100644 --- a/package/setup.py +++ b/package/setup.py @@ -29,7 +29,7 @@ setup( name = "Yapsy", - version = "1.9", + version = "1.9-python3", packages = find_packages(), # the unit tests From 8ae44a5802357a75feebaa3f733edfe50b7d792e Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Fri, 23 Dec 2011 02:47:43 +0100 Subject: [PATCH 03/34] mention compatibility with python3 of course --HG-- branch : python3-transition --- package/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/package/setup.py b/package/setup.py index c59676f..ce1eddf 100644 --- a/package/setup.py +++ b/package/setup.py @@ -52,6 +52,7 @@ 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python :: 3', 'Topic :: Software Development :: Libraries :: Python Modules'], platforms='All', ) From 8b8348132c287acc01762a1acb0980d9c650b70e Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Fri, 23 Dec 2011 02:48:32 +0100 Subject: [PATCH 04/34] Added tag release_Yapsy-1.9-python3 for changeset ee9987b833d6 --HG-- branch : python3-transition --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 1bf0e3f..53a725f 100644 --- a/.hgtags +++ b/.hgtags @@ -4,3 +4,4 @@ 27a222ccc739de48d114ae282f848aad27775904 SubversionImport 87ace7ce5663b6c662bcfa54818e099ffdcceb53 release_Yapsy-1.8 43a85ec50934b636dce6647eb5342b70b809ca20 release_Yapsy-1.9 +ee9987b833d65887487af38cecf0eb9ed6b871eb release_Yapsy-1.9-python3 From 2a2efff1ce740bb8d5969184a65f2ef2aeb225e8 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 15 Jul 2012 17:03:47 +0200 Subject: [PATCH 05/34] insist on the fact that this is a Python3 version --HG-- branch : python3-transition --- package/README.txt | 2 +- package/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package/README.txt b/package/README.txt index cc7ac27..bf524a2 100644 --- a/package/README.txt +++ b/package/README.txt @@ -2,7 +2,7 @@ Yapsy is a small library implementing the core mechanisms needed to build a plugin system into a wider application. The main purpose is to depend only on Python's standard libraries (at -least version 2.3) and to implement only the basic functionalities +least version 3.2) and to implement only the basic functionalities needed to detect, load and keep track of several plugins. For more info see doc/index.rst diff --git a/package/setup.py b/package/setup.py index d13364f..2feb913 100644 --- a/package/setup.py +++ b/package/setup.py @@ -34,7 +34,7 @@ setup( name = "Yapsy", - version = __import__("yapsy").__version__, + version = __import__("yapsy").__version__+"-python3", packages = ['yapsy'], package_dir = {'yapsy':'yapsy'}, From 5d1e5c84f1e7bc210070422f88c3f895fb2dc4cb Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 15 Jul 2012 17:11:33 +0200 Subject: [PATCH 06/34] also include the new runtest.py utility script --HG-- branch : python3-transition --- package/MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/package/MANIFEST.in b/package/MANIFEST.in index 7c5ec36..3bb1960 100644 --- a/package/MANIFEST.in +++ b/package/MANIFEST.in @@ -1,6 +1,7 @@ include README.txt include LICENSE.txt include TODO.txt +include runtests.py recursive-include test *.py *-plugin *.zip From cff4d417026a3b8027d56150979c27131b59269a Mon Sep 17 00:00:00 2001 From: "thibauld@koala-ultrabook.home" Date: Fri, 2 Nov 2012 17:57:11 +0100 Subject: [PATCH 07/34] new setup scripts to bundle the py2 and py3 versions in the same src package --HG-- branch : python3-transition --- utils/2n3_package.sh | 34 ++++++++++++++++ utils/2n3bundle/MANIFEST.in | 24 +++++++++++ utils/2n3bundle/setup.py | 74 ++++++++++++++++++++++++++++++++++ utils/2n3bundle/test_switch.py | 14 +++++++ 4 files changed, 146 insertions(+) create mode 100755 utils/2n3_package.sh create mode 100644 utils/2n3bundle/MANIFEST.in create mode 100644 utils/2n3bundle/setup.py create mode 100644 utils/2n3bundle/test_switch.py diff --git a/utils/2n3_package.sh b/utils/2n3_package.sh new file mode 100755 index 0000000..f7a2bb6 --- /dev/null +++ b/utils/2n3_package.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# -*- coding: utf-8 -*- + + +PY2_YAPSY_DIR="../../default" +PY3_YAPSY_DIR="../../python3-transition/package" +BUNDLE_DIR="./2n3bundle" + +if [ -d $PY2_YAPSY_DIR ]; then + cp -r $PY2_YAPSY_DIR $BUNDLE_DIR/src2 + echo "Copied Python2-compatible sources of yapsy." +else + echo "Unable to find Python2-compatible sources of yapsy, aborting !" + exit 1 +fi + +if [ -d $PY3_YAPSY_DIR ]; then + cp -r $PY3_YAPSY_DIR $BUNDLE_DIR/src3 + echo "Copied Python3-compatible sources of yapsy." +else + echo "Unable to find Python3-compatible sources of yapsy, aborting !" + exit 1 +fi + +pushd $BUNDLE_DIR + +python setup.py $@ + +popd + +rm -r $BUNDLE_DIR/src2 +rm -r $BUNDLE_DIR/src3 +echo "Temporary source copies cleaned up." + diff --git a/utils/2n3bundle/MANIFEST.in b/utils/2n3bundle/MANIFEST.in new file mode 100644 index 0000000..3bb681f --- /dev/null +++ b/utils/2n3bundle/MANIFEST.in @@ -0,0 +1,24 @@ +include test_switch.py + +include src2/package/README.txt +include src2/package/LICENSE.txt +include src2/package/TODO.txt + +recursive-include src2/package/test *.py *-plugin *.zip + +recursive-include src2/package/artwork * +recursive-include src2/package/doc * +prune src2/package/doc/_build + + + +include src3/package/README.txt +include src3/package/LICENSE.txt +include src3/package/TODO.txt +include src3/package/runtests.py + +recursive-include src3/package/test *.py *-plugin *.zip + +recursive-include src3/package/artwork * +recursive-include src3/package/doc * +prune src3/package/doc/_build diff --git a/utils/2n3bundle/setup.py b/utils/2n3bundle/setup.py new file mode 100644 index 0000000..82a471d --- /dev/null +++ b/utils/2n3bundle/setup.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- + +""" +The setup.py script needed to build a .egg for an easier distribution +and installation of yapsy. + +Requires 'Easy Install' to be installed :) +see there: http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions + +Then to create a package run: +$ python setup.py bdist_egg + +To use the generated .egg file then: +easy_install Yapsy-{yapsy version}-py{python version}.egg + +Automagical stuff: + + - test everything:: + + python setup.py test + + - build the packages (sources an egg) and upload all the stuff to pypi:: + + python setup.py sdist bdist_egg upload + + - build the documentation + + python setup.py build_sphinx +""" + +from setuptools import setup + + +import sys + +# Trick from http://python3porting.com/2to3.html#distribution-section +if sys.version < '3': + package_par_dir = 'src2/package' +else: + package_par_dir = 'src3/package' + +sys.path.append(package_par_dir) +import yapsy + +setup( + name = "Yapsy", + version = yapsy.__version__+"-py2py3", + packages = ['yapsy'], + package_dir = {'yapsy':package_par_dir+"/yapsy"}, + + # the unit tests + test_suite = "test_switch.MainTestSuite", + + # metadata for upload to PyPI + author = "Thibauld Nion", + author_email = "tibonihoo@users.sourceforge.net", + description = "Yet another plugin system", + license = "BSD", + keywords = "plugin manager", + url = "http://yapsy.sourceforge.net", + # more details + long_description = open(package_par_dir+"/README.txt").read(), + classifiers=['Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 2', + 'Topic :: Software Development :: Libraries :: Python Modules'], + platforms='All', + ) + diff --git a/utils/2n3bundle/test_switch.py b/utils/2n3bundle/test_switch.py new file mode 100644 index 0000000..50f05b5 --- /dev/null +++ b/utils/2n3bundle/test_switch.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys + +if sys.version < '3': + sys.path.insert(0,"src2/package") +else: + sys.path.insert(0,"src3/package") + + +from test.test_All import MainTestSuite + + From 80340d352b0d91a6e4a0f28abca82f924f7ca8da Mon Sep 17 00:00:00 2001 From: "thibauld@koala-ultrabook.home" Date: Fri, 2 Nov 2012 17:57:29 +0100 Subject: [PATCH 08/34] remove useless import --HG-- branch : python3-transition --- package/runtests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/package/runtests.py b/package/runtests.py index a82a08f..5de6ef9 100644 --- a/package/runtests.py +++ b/package/runtests.py @@ -7,7 +7,6 @@ """ import sys -import os import getopt import unittest import logging From fdf235a248c492e7f5a3eef9afdecf45f7e2994d Mon Sep 17 00:00:00 2001 From: "thibauld@koala-ultrabook.home" Date: Fri, 2 Nov 2012 17:57:59 +0100 Subject: [PATCH 09/34] no need to duplicate the todo list for now --HG-- branch : python3-transition --- package/TODO.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/package/TODO.txt b/package/TODO.txt index 43b1666..cd7cc59 100644 --- a/package/TODO.txt +++ b/package/TODO.txt @@ -3,9 +3,5 @@ ====== -- doc: sample code for filtering plugins -- code: consider making the filter and versionned plugin into plugin - manager child classes (instead of decorators) -- code: find a correct design to make extending the plugin "loading" - easier and chainable (policies/mixins, traits ?) - +- follow evolutions from default branch + From 04254db6d6e1eb2354a1b83c268ecf02876020cd Mon Sep 17 00:00:00 2001 From: "thibauld@koala-ultrabook.home" Date: Fri, 2 Nov 2012 17:58:32 +0100 Subject: [PATCH 10/34] small improvement in format and remove shamefullll typo --HG-- branch : python3-transition --- package/README.txt | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/package/README.txt b/package/README.txt index bf524a2..fdfb707 100644 --- a/package/README.txt +++ b/package/README.txt @@ -1,25 +1,28 @@ Yapsy is a small library implementing the core mechanisms needed to build a plugin system into a wider application. -The main purpose is to depend only on Python's standard libraries (at -least version 3.2) and to implement only the basic functionalities -needed to detect, load and keep track of several plugins. - -For more info see doc/index.rst +The main purpose is to depend only on Python's standard libraries and +to implement only the basic functionalities needed to detect, load and +keep track of several plugins. To use yapsy, make sure that the "yapsy" directory is in your Python loading path and just import the needed class from yapsy (e.g. "from yapsy.PluginManager import PluginManager"). To see more examples, you may want to have a look at the unit tests inside the "test" directory. -Please let me know if you find this usefull. +Please let me know if you find this useful. + +For more info see: + - online at: http://packages.python.org/Yapsy/ + - in the sources at: ./doc/index.rst Thibauld Nion -Site of the project: -http://yapsy.sourceforge.net/ +--- + +Site of the project: http://yapsy.sourceforge.net/ List of Contributors: -Thibauld Nion -Rob McMullen -Roger Gammans + - Thibauld Nion + - Rob McMullen + - Roger Gammans From c9f1e473b191dd48df28eb4136f083dc2f348a12 Mon Sep 17 00:00:00 2001 From: "thibauld@koala-ultrabook.home" Date: Fri, 2 Nov 2012 18:03:12 +0100 Subject: [PATCH 11/34] correct one path and add doc --HG-- branch : python3-transition --- utils/2n3_package.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/2n3_package.sh b/utils/2n3_package.sh index f7a2bb6..1028e7a 100755 --- a/utils/2n3_package.sh +++ b/utils/2n3_package.sh @@ -1,9 +1,13 @@ #!/bin/bash # -*- coding: utf-8 -*- +# Uses the content of 2n3bundle to build a source package that holds +# both sources and also requires that the default and +# python3_transition branches are checkdout at paths corresponding to +# the variables below. PY2_YAPSY_DIR="../../default" -PY3_YAPSY_DIR="../../python3-transition/package" +PY3_YAPSY_DIR="../../python3-transition" BUNDLE_DIR="./2n3bundle" if [ -d $PY2_YAPSY_DIR ]; then From 2381a53e90e4c50e61256ec7285025025b12ccee Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Fri, 2 Nov 2012 22:30:34 +0100 Subject: [PATCH 12/34] correct manifest to make sure yapsy module for both py versions --HG-- branch : python3-transition --- utils/2n3bundle/MANIFEST.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/2n3bundle/MANIFEST.in b/utils/2n3bundle/MANIFEST.in index 3bb681f..475fc18 100644 --- a/utils/2n3bundle/MANIFEST.in +++ b/utils/2n3bundle/MANIFEST.in @@ -6,6 +6,8 @@ include src2/package/TODO.txt recursive-include src2/package/test *.py *-plugin *.zip +recursive-include src2/package/yapsy *.py + recursive-include src2/package/artwork * recursive-include src2/package/doc * prune src2/package/doc/_build @@ -19,6 +21,8 @@ include src3/package/runtests.py recursive-include src3/package/test *.py *-plugin *.zip +recursive-include src3/package/yapsy *.py + recursive-include src3/package/artwork * recursive-include src3/package/doc * prune src3/package/doc/_build From 7a6846c6ae9829d2fe93bc401acb361940bdae09 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 4 Nov 2012 17:40:00 +0100 Subject: [PATCH 13/34] a little warning about a potential bug --HG-- branch : python3-transition --- package/yapsy/PluginManager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package/yapsy/PluginManager.py b/package/yapsy/PluginManager.py index 25d36d1..cf3058a 100644 --- a/package/yapsy/PluginManager.py +++ b/package/yapsy/PluginManager.py @@ -476,7 +476,9 @@ def loadPlugins(self, callback=None): if "__init__" in os.path.basename(candidate_filepath): sys.path.append(plugin_info.path) try: - candidateMainFile = open(candidate_filepath+".py","r") + candidateMainFile = open(candidate_filepath+".py","r") + # TODO: make sure that we can get proper traceback + # info even when using exec(f.read()) exec(candidateMainFile.read(),candidate_globals) except Exception as e: logging.warning("Unable to execute the code in plugin: %s" % candidate_filepath) From e8dd684be863cad96a9576ff5d0be59be53f2424 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Tue, 18 Dec 2012 21:38:44 +0100 Subject: [PATCH 14/34] fix version prefix --HG-- branch : python3-transition --- utils/2n3bundle/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/2n3bundle/setup.py b/utils/2n3bundle/setup.py index 82a471d..bae5bc9 100644 --- a/utils/2n3bundle/setup.py +++ b/utils/2n3bundle/setup.py @@ -45,7 +45,7 @@ setup( name = "Yapsy", - version = yapsy.__version__+"-py2py3", + version = yapsy.__version__+"-pythons2n3", packages = ['yapsy'], package_dir = {'yapsy':package_par_dir+"/yapsy"}, From 4d7917676530e83d324e7b1c9e335f565da640d8 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Tue, 18 Dec 2012 22:50:48 +0100 Subject: [PATCH 15/34] Added tag release_Yapsy-1.10-python3 for changeset 7000b8072f00 --HG-- branch : python3-transition --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index c5c5920..feee626 100644 --- a/.hgtags +++ b/.hgtags @@ -10,3 +10,4 @@ ee9987b833d65887487af38cecf0eb9ed6b871eb release_Yapsy-1.9-python3 0000000000000000000000000000000000000000 release_Yapsy-1.10 0000000000000000000000000000000000000000 release_Yapsy-1.10 b934a474c2e8fa765bfda7bce134060184284872 release_Yapsy-1.10 +7000b8072f00e42d9c448092bbd2149cc76e8d21 release_Yapsy-1.10-python3 From 04670ee59727ec09a59adf90c0cb565f1ce4d398 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Tue, 18 Dec 2012 23:02:08 +0100 Subject: [PATCH 16/34] Removed tag release_Yapsy-1.10-python3 --HG-- branch : python3-transition --- .hgtags | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgtags b/.hgtags index feee626..b5d9a2e 100644 --- a/.hgtags +++ b/.hgtags @@ -11,3 +11,5 @@ ee9987b833d65887487af38cecf0eb9ed6b871eb release_Yapsy-1.9-python3 0000000000000000000000000000000000000000 release_Yapsy-1.10 b934a474c2e8fa765bfda7bce134060184284872 release_Yapsy-1.10 7000b8072f00e42d9c448092bbd2149cc76e8d21 release_Yapsy-1.10-python3 +7000b8072f00e42d9c448092bbd2149cc76e8d21 release_Yapsy-1.10-python3 +0000000000000000000000000000000000000000 release_Yapsy-1.10-python3 From c31a312e09c1d21fd305a83dfb1da69e2ed9cf37 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Tue, 18 Dec 2012 23:02:28 +0100 Subject: [PATCH 17/34] fix 2n3 bundle --HG-- branch : python3-transition --- utils/2n3bundle/MANIFEST.in | 4 ++-- utils/2n3bundle/setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/2n3bundle/MANIFEST.in b/utils/2n3bundle/MANIFEST.in index 475fc18..25e5dfd 100644 --- a/utils/2n3bundle/MANIFEST.in +++ b/utils/2n3bundle/MANIFEST.in @@ -2,7 +2,7 @@ include test_switch.py include src2/package/README.txt include src2/package/LICENSE.txt -include src2/package/TODO.txt +include src2/package/CHANGELOG.txt recursive-include src2/package/test *.py *-plugin *.zip @@ -16,7 +16,7 @@ prune src2/package/doc/_build include src3/package/README.txt include src3/package/LICENSE.txt -include src3/package/TODO.txt +include src3/package/CHANGELOG.txt include src3/package/runtests.py recursive-include src3/package/test *.py *-plugin *.zip diff --git a/utils/2n3bundle/setup.py b/utils/2n3bundle/setup.py index bae5bc9..e6079be 100644 --- a/utils/2n3bundle/setup.py +++ b/utils/2n3bundle/setup.py @@ -40,7 +40,7 @@ else: package_par_dir = 'src3/package' -sys.path.append(package_par_dir) +sys.path.insert(0,package_par_dir) import yapsy setup( From 465c234769753788b1ffdbaf9d73c6753ce3928b Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Tue, 18 Dec 2012 23:02:36 +0100 Subject: [PATCH 18/34] Added tag release_Yapsy-1.10-python3 for changeset 87fbef4ba66b --HG-- branch : python3-transition --- .hgtags | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgtags b/.hgtags index b5d9a2e..dafa8d4 100644 --- a/.hgtags +++ b/.hgtags @@ -13,3 +13,5 @@ b934a474c2e8fa765bfda7bce134060184284872 release_Yapsy-1.10 7000b8072f00e42d9c448092bbd2149cc76e8d21 release_Yapsy-1.10-python3 7000b8072f00e42d9c448092bbd2149cc76e8d21 release_Yapsy-1.10-python3 0000000000000000000000000000000000000000 release_Yapsy-1.10-python3 +0000000000000000000000000000000000000000 release_Yapsy-1.10-python3 +87fbef4ba66ba9f98692e9b657479fe5daae12a2 release_Yapsy-1.10-python3 From 87a4e312a06465fa16372404477b439790ec5517 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 13 Jan 2013 15:18:20 +0100 Subject: [PATCH 19/34] avoid having too much errors even in the ErroneousPlugin :) --HG-- branch : python3-transition --- package/test/plugins/ErroneousPlugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/package/test/plugins/ErroneousPlugin.py b/package/test/plugins/ErroneousPlugin.py index 444ac8a..94fc0dd 100644 --- a/package/test/plugins/ErroneousPlugin.py +++ b/package/test/plugins/ErroneousPlugin.py @@ -7,8 +7,6 @@ This is certainly the second simplest plugin ever. """ -import test_settings -import main from yapsy.IPlugin import IPlugin from import_error import the_error_is_here From d131cac21b52d1df1c440bc9cbb47948182821ba Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 13 Jan 2013 15:44:04 +0100 Subject: [PATCH 20/34] fix 2n3 packaging script --HG-- branch : python3-transition --- utils/2n3_package.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/2n3_package.sh b/utils/2n3_package.sh index 1028e7a..9b07823 100755 --- a/utils/2n3_package.sh +++ b/utils/2n3_package.sh @@ -19,7 +19,8 @@ else fi if [ -d $PY3_YAPSY_DIR ]; then - cp -r $PY3_YAPSY_DIR $BUNDLE_DIR/src3 + mkdir "./2n3bundle/src3" + cp -r $PY3_YAPSY_DIR/package $BUNDLE_DIR/src3/package echo "Copied Python3-compatible sources of yapsy." else echo "Unable to find Python3-compatible sources of yapsy, aborting !" From 4336ece599120bd284deea17089669b56391d77f Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 13 Jan 2013 16:06:58 +0100 Subject: [PATCH 21/34] Added tag release_Yapsy-1.10.1-python3 for changeset f59fd5772939 --HG-- branch : python3-transition --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f9fc8f0..9636b89 100644 --- a/.hgtags +++ b/.hgtags @@ -15,3 +15,4 @@ a5c62d9f560fa44bbf41fdd0d2c9ddc85144f637 release_Yapsy-1.10.1 0000000000000000000000000000000000000000 release_Yapsy-1.10-python3 0000000000000000000000000000000000000000 release_Yapsy-1.10-python3 87fbef4ba66ba9f98692e9b657479fe5daae12a2 release_Yapsy-1.10-python3 +f59fd5772939d6779725d0bb55b612ba5b254534 release_Yapsy-1.10.1-python3 From 9c8927c075c7c22689b3032f1b43239a97e69ba7 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Tue, 21 May 2013 23:10:05 +0200 Subject: [PATCH 22/34] test building with python 3 here --HG-- branch : python3-transition --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 34beac9..4d2368c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: python python: - - "2.4" - - "2.5" - - "2.6" - - "2.7" + - "3.1" + - "3.2" + - "3.3" # command to run tests script: python package/setup.py test From ad1dbf0a1c20a035ef9822e564ea04a74c5ca5e1 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Wed, 22 May 2013 00:17:56 +0200 Subject: [PATCH 23/34] only show build status of python3 branch --HG-- branch : python3-transition --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 9eae5f8..9ed5899 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,5 @@ Yapsy's development is [hosted at Sourceforge](http://sourceforge.net/projects/y Yapsy is also continuously tested on [travis-ci](https://travis-ci.org): -[![Build Status](https://travis-ci.org/tibonihoo/yapsy.png?branch=master)](https://travis-ci.org/tibonihoo/yapsy) - [![Build Status](https://travis-ci.org/tibonihoo/yapsy.png?branch=python3)](https://travis-ci.org/tibonihoo/yapsy) From d870cac33692b01c783039a5aefad8063cdf6076 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Wed, 22 May 2013 00:18:28 +0200 Subject: [PATCH 24/34] don't test build on python 3.1 (not available on travis) --HG-- branch : python3-transition --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4d2368c..edc6cd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "3.1" - "3.2" - "3.3" # command to run tests From 13d93a7388a79dceef2934a0814cd3e2cac84311 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Wed, 22 May 2013 22:30:50 +0200 Subject: [PATCH 25/34] fix typo --HG-- branch : python3-transition --- utils/2n3_package.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/2n3_package.sh b/utils/2n3_package.sh index 9b07823..0558c4a 100755 --- a/utils/2n3_package.sh +++ b/utils/2n3_package.sh @@ -3,7 +3,7 @@ # Uses the content of 2n3bundle to build a source package that holds # both sources and also requires that the default and -# python3_transition branches are checkdout at paths corresponding to +# python3_transition branches are checkout'd at paths corresponding to # the variables below. PY2_YAPSY_DIR="../../default" From c334caf22c5f675545496595d7e503fcab40931d Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Wed, 22 May 2013 22:37:26 +0200 Subject: [PATCH 26/34] Added tag release_Yapsy-1.10.2-python3 for changeset c1f8228a9fd0 --HG-- branch : python3-transition --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 3cf4375..323bc0b 100644 --- a/.hgtags +++ b/.hgtags @@ -17,3 +17,4 @@ a5c62d9f560fa44bbf41fdd0d2c9ddc85144f637 release_Yapsy-1.10.1 87fbef4ba66ba9f98692e9b657479fe5daae12a2 release_Yapsy-1.10-python3 f59fd5772939d6779725d0bb55b612ba5b254534 release_Yapsy-1.10.1-python3 5c0ff8646c2e1b6e0a4676b57676ed5adbe6b479 release_Yapsy-1.10.2 +c1f8228a9fd08bbffbfd8b6b8ecd1de5c2d9236f release_Yapsy-1.10.2-python3 From f6c30aa673247962ab5d936509d1c4827284dc9d Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Wed, 22 May 2013 23:50:40 +0200 Subject: [PATCH 27/34] add more doc to src pkg script --HG-- branch : python3-transition --- utils/2n3_package.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/2n3_package.sh b/utils/2n3_package.sh index 0558c4a..3c339ee 100755 --- a/utils/2n3_package.sh +++ b/utils/2n3_package.sh @@ -1,10 +1,15 @@ #!/bin/bash # -*- coding: utf-8 -*- + # Uses the content of 2n3bundle to build a source package that holds # both sources and also requires that the default and # python3_transition branches are checkout'd at paths corresponding to # the variables below. +# +# Usage example: +# ./2n3_package.sh sdist upload + PY2_YAPSY_DIR="../../default" PY3_YAPSY_DIR="../../python3-transition" From ec8996cc2ab82cdc044d0330c2ee7e2bdb668670 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Wed, 22 May 2013 23:53:25 +0200 Subject: [PATCH 28/34] update changelog --HG-- branch : python3-transition --- package/CHANGELOG.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/CHANGELOG.txt b/package/CHANGELOG.txt index 87772c5..18a73fa 100644 --- a/package/CHANGELOG.txt +++ b/package/CHANGELOG.txt @@ -2,6 +2,8 @@ version-1.10.2 [2013-05-22] - code: fix compatibility with python2.5 - doc: add links to travis-ci and readthedocs.org + - code: fix AutoInstall test failures [contrib. Agustin Henze] + - code: replace deprecated methods usage (for Python3) version-1.10.1 [2013-01-13] From c06c211e5f940cac2604125a1083b8ad80a5fab2 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Fri, 6 Dec 2013 22:01:42 +0100 Subject: [PATCH 29/34] change version name to comply with PEP440 and resolve recurrent pbs with pip --HG-- branch : python3-transition --- package/setup.py | 2 +- utils/2n3bundle/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package/setup.py b/package/setup.py index 797434a..6f4b8e5 100644 --- a/package/setup.py +++ b/package/setup.py @@ -38,7 +38,7 @@ try: setup( name = "Yapsy", - version = __import__("yapsy").__version__+"-pythons2n3", + version = __import__("yapsy").__version__+"23", packages = ['yapsy'], package_dir = {'yapsy':'yapsy'}, diff --git a/utils/2n3bundle/setup.py b/utils/2n3bundle/setup.py index e6079be..a76913c 100644 --- a/utils/2n3bundle/setup.py +++ b/utils/2n3bundle/setup.py @@ -45,7 +45,7 @@ setup( name = "Yapsy", - version = yapsy.__version__+"-pythons2n3", + version = yapsy.__version__+"23", packages = ['yapsy'], package_dir = {'yapsy':package_par_dir+"/yapsy"}, From dce73b2d122595090c4e372f363fac2a2d15bcf1 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 23 Mar 2014 00:23:44 +0100 Subject: [PATCH 30/34] fix merge (py2>3) --HG-- branch : python3-transition --- package/test/test_All.py | 2 +- package/test/test_PluginInfo.py | 4 ++-- package/yapsy/ConfigurablePluginManager.py | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package/test/test_All.py b/package/test/test_All.py index 0fde690..42c88cf 100644 --- a/package/test/test_All.py +++ b/package/test/test_All.py @@ -24,7 +24,7 @@ from . import test_FilterPlugin from . import test_ErrorInPlugin from . import test_PluginFileLocator -import test_PluginInfo +from . import test_PluginInfo # add them to a common test suite diff --git a/package/test/test_PluginInfo.py b/package/test/test_PluginInfo.py index 4fcd938..bd6ca6d 100644 --- a/package/test/test_PluginInfo.py +++ b/package/test/test_PluginInfo.py @@ -2,9 +2,9 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t; python-indent: 4 -*- import test_settings -from ConfigParser import ConfigParser +from configparser import ConfigParser import unittest -import os + from yapsy.PluginInfo import PluginInfo diff --git a/package/yapsy/ConfigurablePluginManager.py b/package/yapsy/ConfigurablePluginManager.py index cc9d706..57e0387 100644 --- a/package/yapsy/ConfigurablePluginManager.py +++ b/package/yapsy/ConfigurablePluginManager.py @@ -17,8 +17,7 @@ from yapsy.PluginManagerDecorator import PluginManagerDecorator from yapsy.PluginManager import PLUGIN_NAME_FORBIDEN_STRING - - + class ConfigurablePluginManager(PluginManagerDecorator): """ @@ -255,7 +254,7 @@ def loadPlugins(self,callback=None): for each plugin candidate look for its category, load it and stores it in the appropriate slot of the ``category_mapping``. """ - self._component.loadPlugins(callback) + self._component.loadPlugins(callback) # now load the plugins according to the recorded configuration if self.config_parser.has_section(self.CONFIG_SECTION_NAME): # browse all the categories From 36c3fe9e18c936ca1a8e3cb2cda95c7d3137b3f7 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 23 Mar 2014 19:31:24 +0100 Subject: [PATCH 31/34] propagate version fix to the 2+3 bundle setup script --HG-- branch : python3-transition --- utils/2n3bundle/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/2n3bundle/setup.py b/utils/2n3bundle/setup.py index a76913c..72100e8 100644 --- a/utils/2n3bundle/setup.py +++ b/utils/2n3bundle/setup.py @@ -45,7 +45,7 @@ setup( name = "Yapsy", - version = yapsy.__version__+"23", + version = yapsy.__version__, packages = ['yapsy'], package_dir = {'yapsy':package_par_dir+"/yapsy"}, From 1217d61564375ee68cc30134ab6819872824e916 Mon Sep 17 00:00:00 2001 From: Thibauld Nion Date: Sun, 23 Mar 2014 19:52:55 +0100 Subject: [PATCH 32/34] Added tag release_Yapsy-1.10.323-python3 for changeset 777d3daf4648 --HG-- branch : python3-transition --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 323bc0b..a893ab0 100644 --- a/.hgtags +++ b/.hgtags @@ -18,3 +18,4 @@ a5c62d9f560fa44bbf41fdd0d2c9ddc85144f637 release_Yapsy-1.10.1 f59fd5772939d6779725d0bb55b612ba5b254534 release_Yapsy-1.10.1-python3 5c0ff8646c2e1b6e0a4676b57676ed5adbe6b479 release_Yapsy-1.10.2 c1f8228a9fd08bbffbfd8b6b8ecd1de5c2d9236f release_Yapsy-1.10.2-python3 +777d3daf4648d8395be90a2059c749bca191cbcd release_Yapsy-1.10.323-python3 From ae18fdb5206834a34def88c81e4d900909cee493 Mon Sep 17 00:00:00 2001 From: Josip Delic Date: Mon, 29 Sep 2014 10:14:10 +0200 Subject: [PATCH 33/34] added compat to support python2 and python3 from one branch pep8 with 'autopep8 -aaa -i -r .' --- .gitignore | 9 + package/doc/conf.py | 31 +- package/runtests.py | 67 +- package/setup.py | 63 +- package/test/plugins/ConfigPlugin.py | 88 +- package/test/plugins/ErroneousPlugin.py | 56 +- package/test/plugins/SimplePlugin.py | 56 +- package/test/plugins/VersionedPlugin10.py | 60 +- package/test/plugins/VersionedPlugin11.py | 57 +- package/test/plugins/VersionedPlugin111.py | 57 +- package/test/plugins/VersionedPlugin12.py | 60 +- package/test/plugins/VersionedPlugin12a1.py | 60 +- .../pluginsasdirs/SimplePlugin/__init__.py | 55 +- .../pluginstoinstall/AutoInstallPlugin.py | 56 +- .../autoinstalldirplugin/__init__.py | 56 +- package/test/test_All.py | 27 +- package/test/test_AutoInstallPlugin.py | 520 ++++---- package/test/test_ConfigPlugin.py | 268 ++-- package/test/test_ErrorInPlugin.py | 102 +- package/test/test_FilterPlugin.py | 456 +++---- package/test/test_PluginFileLocator.py | 959 ++++++++------- package/test/test_PluginInfo.py | 85 +- package/test/test_SimplePlugin.py | 572 ++++----- package/test/test_Singleton.py | 237 ++-- package/test/test_VersionedPlugin.py | 200 +-- package/test/test_settings.py | 25 +- package/yapsy/AutoInstallPluginManager.py | 398 +++--- package/yapsy/ConfigurablePluginManager.py | 504 ++++---- package/yapsy/FilteredPluginManager.py | 212 ++-- package/yapsy/IPlugin.py | 42 +- package/yapsy/IPluginLocator.py | 179 +-- package/yapsy/PluginFileLocator.py | 997 ++++++++------- package/yapsy/PluginInfo.py | 386 +++--- package/yapsy/PluginManager.py | 1077 +++++++++-------- package/yapsy/PluginManagerDecorator.py | 133 +- package/yapsy/VersionedPluginManager.py | 222 ++-- package/yapsy/__init__.py | 40 +- package/yapsy/compat.py | 93 ++ 38 files changed, 4515 insertions(+), 4050 deletions(-) create mode 100644 .gitignore create mode 100644 package/yapsy/compat.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d691f93 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.pyc +*.idea +*.swp +.coverage +env*/ +dist/ +build/ +*.egg-info/ +.idea/ diff --git a/package/doc/conf.py b/package/doc/conf.py index 88f09cf..82db1df 100644 --- a/package/doc/conf.py +++ b/package/doc/conf.py @@ -11,7 +11,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import sys +import os SRC_DIR = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) sys.path = [SRC_DIR] + sys.path @@ -19,9 +20,9 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) +# sys.path.append(os.path.abspath('.')) -# -- General configuration ----------------------------------------------------- +# -- General configuration ----------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -49,7 +50,7 @@ # import sys -sys.path.insert(0,os.path.dirname(__file__)) +sys.path.insert(0, os.path.dirname(__file__)) import yapsy # The short X.Y version. version = yapsy.__version__ @@ -94,7 +95,7 @@ #modindex_common_prefix = [] -# -- Options for HTML output --------------------------------------------------- +# -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. @@ -104,14 +105,14 @@ # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - "sidebarbgcolor" : "#777", + "sidebarbgcolor": "#777", "sidebarlinkcolor": "#e0cede", - "relbarbgcolor" : "#999", + "relbarbgcolor": "#999", "relbarlinkcolor": "#e0cede", - "footerbgcolor" : "#777", - "headtextcolor" : "#5c3566", + "footerbgcolor": "#777", + "headtextcolor": "#5c3566", "linkcolor": "#5c3566", - } +} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] @@ -125,12 +126,12 @@ # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = os.path.join(SRC_DIR,"artwork","yapsy-big.png") +html_logo = os.path.join(SRC_DIR, "artwork", "yapsy-big.png") # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = os.path.join(SRC_DIR,"artwork","yapsy-favicon.ico") +html_favicon = os.path.join(SRC_DIR, "artwork", "yapsy-favicon.ico") # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -176,7 +177,7 @@ htmlhelp_basename = 'Yapsydoc' -# -- Options for LaTeX output -------------------------------------------------- +# -- Options for LaTeX output -------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' @@ -187,8 +188,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Yapsy.tex', 'Yapsy Documentation', - 'Thibauld Nion', 'manual'), + ('index', 'Yapsy.tex', 'Yapsy Documentation', + 'Thibauld Nion', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/package/runtests.py b/package/runtests.py index 142b003..a2c6676 100644 --- a/package/runtests.py +++ b/package/runtests.py @@ -14,11 +14,12 @@ from test.test_All import MainTestSuite + def usage(): - """ - Show/explain the options. - """ - return """python main.py [OPTIONS] + """ + Show/explain the options. + """ + return """python main.py [OPTIONS] Options: @@ -31,34 +32,30 @@ def usage(): def main(argv): - """ - Launch all the test. - """ - try: - opts, args = getopt.getopt(argv[1:], "vdh", ["help"]) - except getopt.GetoptError: - print(usage()) - sys.exit(2) - loglevel = logging.ERROR - test_verbosity = 1 - for o,a in opts: - if o in ("-h","--help"): - print(usage()) - sys.exit(0) - elif o == "-d": - loglevel = logging.DEBUG - elif o == "-v": - test_verbosity = 2 - logging.basicConfig(level= loglevel, - format='%(asctime)s %(levelname)s %(message)s') - - # launch the testing process - unittest.TextTestRunner(verbosity=test_verbosity).run(MainTestSuite) - - - -if __name__=="__main__": - main(sys.argv) - - - + """ + Launch all the test. + """ + try: + opts, args = getopt.getopt(argv[1:], "vdh", ["help"]) + except getopt.GetoptError: + print(usage()) + sys.exit(2) + loglevel = logging.ERROR + test_verbosity = 1 + for o, a in opts: + if o in ("-h", "--help"): + print(usage()) + sys.exit(0) + elif o == "-d": + loglevel = logging.DEBUG + elif o == "-v": + test_verbosity = 2 + logging.basicConfig(level=loglevel, + format='%(asctime)s %(levelname)s %(message)s') + + # launch the testing process + unittest.TextTestRunner(verbosity=test_verbosity).run(MainTestSuite) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/package/setup.py b/package/setup.py index 58705e1..0c75dd1 100644 --- a/package/setup.py +++ b/package/setup.py @@ -25,44 +25,45 @@ python setup.py sdist bdist_egg upload - build the documentation - + python setup.py build_sphinx """ import os from setuptools import setup -# just in case setup.py is launched from elsewhere that the containing directory +# just in case setup.py is launched from elsewhere that the containing +# directory originalDir = os.getcwd() os.chdir(os.path.dirname(os.path.abspath(__file__))) try: - setup( - name = "Yapsy", - version = __import__("yapsy").__version__, - packages = ['yapsy'], - package_dir = {'yapsy':'yapsy'}, - - # the unit tests - test_suite = "test.test_All.MainTestSuite", - - # metadata for upload to PyPI - author = "Thibauld Nion", - author_email = "thibauld@tibonihoo.net", - description = "Yet another plugin system", - license = "BSD", - keywords = "plugin manager", - url = "http://yapsy.sourceforge.net", - # more details - long_description = open("README.txt").read(), - classifiers=['Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Topic :: Software Development :: Libraries :: Python Modules'], - platforms='All', - ) - + setup( + name="Yapsy", + version=__import__("yapsy").__version__, + packages=['yapsy'], + package_dir={'yapsy': 'yapsy'}, + + # the unit tests + test_suite="test.test_All.MainTestSuite", + + # metadata for upload to PyPI + author="Thibauld Nion", + author_email="thibauld@tibonihoo.net", + description="Yet another plugin system", + license="BSD", + keywords="plugin manager", + url="http://yapsy.sourceforge.net", + # more details + long_description=open("README.txt").read(), + classifiers=['Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: Software Development :: Libraries :: Python Modules'], + platforms='All', + ) + finally: - os.chdir(originalDir) + os.chdir(originalDir) diff --git a/package/test/plugins/ConfigPlugin.py b/package/test/plugins/ConfigPlugin.py index 656e145..1ebf22c 100644 --- a/package/test/plugins/ConfigPlugin.py +++ b/package/test/plugins/ConfigPlugin.py @@ -2,57 +2,53 @@ # -*- coding: utf-8 -*- - """ This is certainly the second simplest plugin ever. """ from yapsy.IPlugin import IPlugin -class ConfigPlugin(IPlugin): - """ - Try to use the methods with which it has been decorated. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - - - def activate(self): - """ - Call the parent class's acivation method - """ - IPlugin.activate(self) - return - - - def deactivate(self): - """ - Just call the parent class's method - """ - IPlugin.deactivate(self) - - - def choseTestOption(self, value): - """ - Set an option to a given value. - """ - self.setConfigOption("Test",value) - - def checkTestOption(self): - """ - Test if the test option is here. - """ - return self.hasConfigOption("Test") - - def getTestOption(self): - """ - Return the value of the test option. - """ - return self.getConfigOption("Test") +class ConfigPlugin(IPlugin): + """ + Try to use the methods with which it has been decorated. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + + def activate(self): + """ + Call the parent class's acivation method + """ + IPlugin.activate(self) + return + + def deactivate(self): + """ + Just call the parent class's method + """ + IPlugin.deactivate(self) + + def choseTestOption(self, value): + """ + Set an option to a given value. + """ + self.setConfigOption("Test", value) + + def checkTestOption(self): + """ + Test if the test option is here. + """ + return self.hasConfigOption("Test") + + def getTestOption(self): + """ + Return the value of the test option. + """ + return self.getConfigOption("Test") diff --git a/package/test/plugins/ErroneousPlugin.py b/package/test/plugins/ErroneousPlugin.py index 94fc0dd..924106c 100644 --- a/package/test/plugins/ErroneousPlugin.py +++ b/package/test/plugins/ErroneousPlugin.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- - """ This is certainly the second simplest plugin ever. """ @@ -11,34 +10,31 @@ from import_error import the_error_is_here -class ErrorenousPlugin(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - +class ErrorenousPlugin(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) diff --git a/package/test/plugins/SimplePlugin.py b/package/test/plugins/SimplePlugin.py index 64625a3..5ff5d09 100644 --- a/package/test/plugins/SimplePlugin.py +++ b/package/test/plugins/SimplePlugin.py @@ -2,41 +2,37 @@ # -*- coding: utf-8 -*- - """ This is certainly the second simplest plugin ever. """ from yapsy.IPlugin import IPlugin -class SimplePlugin(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - +class SimplePlugin(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) diff --git a/package/test/plugins/VersionedPlugin10.py b/package/test/plugins/VersionedPlugin10.py index f673cee..795f7d5 100644 --- a/package/test/plugins/VersionedPlugin10.py +++ b/package/test/plugins/VersionedPlugin10.py @@ -2,7 +2,6 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- - """ This is certainly the second simplest plugin ever. """ @@ -10,35 +9,34 @@ from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin -class VersionedPlugin10(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - TEST_MESSAGE("Version 1.0") - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - TEST_MESSAGE("Activated Version 1.0!") - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - TEST_MESSAGE("Deactivated Version 1.0!") +class VersionedPlugin10(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + TEST_MESSAGE("Version 1.0") + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + TEST_MESSAGE("Activated Version 1.0!") + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) + TEST_MESSAGE("Deactivated Version 1.0!") diff --git a/package/test/plugins/VersionedPlugin11.py b/package/test/plugins/VersionedPlugin11.py index 3c62290..8133bc6 100644 --- a/package/test/plugins/VersionedPlugin11.py +++ b/package/test/plugins/VersionedPlugin11.py @@ -2,7 +2,6 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- - """ This is certainly the second simplest plugin ever. """ @@ -10,34 +9,32 @@ from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin -class VersionedPlugin11(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - TEST_MESSAGE("Version 1.1") - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - +class VersionedPlugin11(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + TEST_MESSAGE("Version 1.1") + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) diff --git a/package/test/plugins/VersionedPlugin111.py b/package/test/plugins/VersionedPlugin111.py index 5e39d91..ca8ed77 100644 --- a/package/test/plugins/VersionedPlugin111.py +++ b/package/test/plugins/VersionedPlugin111.py @@ -2,7 +2,6 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- - """ This is certainly the second simplest plugin ever. """ @@ -10,34 +9,32 @@ from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin -class VersionedPlugin111(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - TEST_MESSAGE("Version 1.1.1") - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - +class VersionedPlugin111(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + TEST_MESSAGE("Version 1.1.1") + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) diff --git a/package/test/plugins/VersionedPlugin12.py b/package/test/plugins/VersionedPlugin12.py index 56c649f..88c2a70 100644 --- a/package/test/plugins/VersionedPlugin12.py +++ b/package/test/plugins/VersionedPlugin12.py @@ -2,7 +2,6 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- - """ This is certainly the second simplest plugin ever. """ @@ -10,33 +9,34 @@ from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin + class VersionedPlugin12(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - TEST_MESSAGE("Version 1.2") - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - TEST_MESSAGE("Activated Version 1.2!") - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - TEST_MESSAGE("Deactivated Version 1.2!") + + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + TEST_MESSAGE("Version 1.2") + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + TEST_MESSAGE("Activated Version 1.2!") + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) + TEST_MESSAGE("Deactivated Version 1.2!") diff --git a/package/test/plugins/VersionedPlugin12a1.py b/package/test/plugins/VersionedPlugin12a1.py index 420baa4..b9e72da 100644 --- a/package/test/plugins/VersionedPlugin12a1.py +++ b/package/test/plugins/VersionedPlugin12a1.py @@ -2,7 +2,6 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*- - """ This is certainly the second simplest plugin ever. """ @@ -10,33 +9,34 @@ from test_settings import TEST_MESSAGE from yapsy.IPlugin import IPlugin + class VersionedPlugin12a1(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - TEST_MESSAGE("Version 1.2a1") - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - TEST_MESSAGE("Activated Version 1.2a1!") - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - TEST_MESSAGE("Deactivated Version 1.2a1!") + + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + TEST_MESSAGE("Version 1.2a1") + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + TEST_MESSAGE("Activated Version 1.2a1!") + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) + TEST_MESSAGE("Deactivated Version 1.2a1!") diff --git a/package/test/pluginsasdirs/SimplePlugin/__init__.py b/package/test/pluginsasdirs/SimplePlugin/__init__.py index 3547c9d..5ff5d09 100644 --- a/package/test/pluginsasdirs/SimplePlugin/__init__.py +++ b/package/test/pluginsasdirs/SimplePlugin/__init__.py @@ -8,34 +8,31 @@ from yapsy.IPlugin import IPlugin -class SimplePlugin(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - +class SimplePlugin(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) diff --git a/package/test/pluginstoinstall/AutoInstallPlugin.py b/package/test/pluginstoinstall/AutoInstallPlugin.py index dbe2a03..fe95a81 100644 --- a/package/test/pluginstoinstall/AutoInstallPlugin.py +++ b/package/test/pluginstoinstall/AutoInstallPlugin.py @@ -2,41 +2,37 @@ # -*- coding: utf-8 -*- - """ This is certainly the second simplest plugin ever. """ from yapsy.IPlugin import IPlugin -class AutoInstallPlugin(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - +class AutoInstallPlugin(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) diff --git a/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py b/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py index f00f90a..4c38f69 100644 --- a/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py +++ b/package/test/pluginstoinstall/autoinstalldirplugin/__init__.py @@ -2,41 +2,37 @@ # -*- coding: utf-8 -*- - """ This is certainly the second simplest plugin ever. """ from yapsy.IPlugin import IPlugin -class AutoInstallDirPlugin(IPlugin): - """ - Only trigger the expected test results. - """ - - def __init__(self): - """ - init - """ - # initialise parent class - IPlugin.__init__(self) - - - def activate(self): - """ - On activation tell that this has been successfull. - """ - # get the automatic procedure from IPlugin - IPlugin.activate(self) - return - - - def deactivate(self): - """ - On deactivation check that the 'activated' flag was on then - tell everything's ok to the test procedure. - """ - IPlugin.deactivate(self) - +class AutoInstallDirPlugin(IPlugin): + """ + Only trigger the expected test results. + """ + + def __init__(self): + """ + init + """ + # initialise parent class + IPlugin.__init__(self) + + def activate(self): + """ + On activation tell that this has been successfull. + """ + # get the automatic procedure from IPlugin + IPlugin.activate(self) + return + + def deactivate(self): + """ + On deactivation check that the 'activated' flag was on then + tell everything's ok to the test procedure. + """ + IPlugin.deactivate(self) diff --git a/package/test/test_All.py b/package/test/test_All.py index 42c88cf..560f961 100644 --- a/package/test/test_All.py +++ b/package/test/test_All.py @@ -11,8 +11,8 @@ # set correct loading path for test files sys.path.append( - os.path.dirname( - os.path.abspath(__file__))) + os.path.dirname( + os.path.abspath(__file__))) # load the tests @@ -29,15 +29,14 @@ # add them to a common test suite MainTestSuite = unittest.TestSuite( - [ # add the tests suites below - test_SimplePlugin.suite, - test_Singleton.suite, - test_ConfigPlugin.suite, - test_VersionedPlugin.suite, - test_AutoInstallPlugin.suite, - test_FilterPlugin.suite, - test_ErrorInPlugin.suite, - test_PluginFileLocator.suite, - test_PluginInfo.suite, - ]) - + [ # add the tests suites below + test_SimplePlugin.suite, + test_Singleton.suite, + test_ConfigPlugin.suite, + test_VersionedPlugin.suite, + test_AutoInstallPlugin.suite, + test_FilterPlugin.suite, + test_ErrorInPlugin.suite, + test_PluginFileLocator.suite, + test_PluginInfo.suite, + ]) diff --git a/package/test/test_AutoInstallPlugin.py b/package/test/test_AutoInstallPlugin.py index 1a6b291..fba6da7 100644 --- a/package/test/test_AutoInstallPlugin.py +++ b/package/test/test_AutoInstallPlugin.py @@ -4,284 +4,308 @@ from . import test_settings import unittest import sys -import os +import os import shutil from yapsy.AutoInstallPluginManager import AutoInstallPluginManager class AutoInstallTestsCase(unittest.TestCase): - """ - Test the correct installation and loading of a simple plugin. - """ + """ + Test the correct installation and loading of a simple plugin. + """ - def setUp(self): - """ - init - """ - # create the plugin manager - self.storing_dir = os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins") - self.pluginManager = AutoInstallPluginManager( - self.storing_dir, - directories_list=[self.storing_dir], - plugin_info_ext="yapsy-autoinstall-plugin") - # load the plugins that may be found - self.pluginManager.collectPlugins() - # Will be used later - self.plugin_info = None - self.new_plugins_waiting_dir = os.path.join( - os.path.dirname(os.path.abspath(__file__)),"pluginstoinstall") + def setUp(self): + """ + init + """ + # create the plugin manager + self.storing_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins") + self.pluginManager = AutoInstallPluginManager( + self.storing_dir, + directories_list=[self.storing_dir], + plugin_info_ext="yapsy-autoinstall-plugin") + # load the plugins that may be found + self.pluginManager.collectPlugins() + # Will be used later + self.plugin_info = None + self.new_plugins_waiting_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "pluginstoinstall") + def tearDown(self): + """ + Clean the plugin installation directory. + """ + try: + os.remove( + os.path.join( + self.pluginManager.plugins_places[0], + "autoinstallplugin.yapsy-autoinstall-plugin")) + except OSError: + pass + try: + os.remove(os.path.join(self.pluginManager.plugins_places[0], + "AutoInstallPlugin.py")) + except OSError: + pass + try: + os.remove( + os.path.join( + self.pluginManager.plugins_places[0], + "autoinstalldirplugin.yapsy-autoinstall-plugin")) + except OSError: + pass + try: + shutil.rmtree(os.path.join(self.pluginManager.plugins_places[0], + "autoinstalldirplugin")) + except OSError: + pass - def tearDown(self): - """ - Clean the plugin installation directory. - """ - try: - os.remove(os.path.join(self.pluginManager.plugins_places[0], - "autoinstallplugin.yapsy-autoinstall-plugin")) - except OSError: - pass - try: - os.remove(os.path.join(self.pluginManager.plugins_places[0], - "AutoInstallPlugin.py")) - except OSError: - pass - try: - os.remove(os.path.join(self.pluginManager.plugins_places[0], - "autoinstalldirplugin.yapsy-autoinstall-plugin")) - except OSError: - pass - try: - shutil.rmtree(os.path.join(self.pluginManager.plugins_places[0], - "autoinstalldirplugin")) - except OSError: - pass - + def plugin_loading_check_none(self): + """ + Test that no plugin has been loaded. + """ + # check nb of categories + self.assertEqual(len(self.pluginManager.getCategories()), 1) + sole_category = self.pluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.pluginManager.getPluginsOfCategory(sole_category)), 0) - def plugin_loading_check_none(self): - """ - Test that no plugin has been loaded. - """ - # check nb of categories - self.assertEqual(len(self.pluginManager.getCategories()),1) - sole_category = self.pluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.pluginManager.getPluginsOfCategory(sole_category)),0) + def plugin_loading_check(self, new_plugin_name): + """ + Test if the correct plugin has been loaded. + """ + if self.plugin_info is None: + # check nb of categories + self.assertEqual(len(self.pluginManager.getCategories()), 1) + sole_category = self.pluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.pluginManager.getPluginsOfCategory(sole_category)), 1) + self.plugin_info = self.pluginManager.getPluginsOfCategory( + sole_category)[0] + # test that the name of the plugin has been correctly defined + self.assertEqual(self.plugin_info.name, new_plugin_name) + self.assertEqual(sole_category, self.plugin_info.category) + else: + self.assertTrue(True) - def plugin_loading_check(self,new_plugin_name): - """ - Test if the correct plugin has been loaded. - """ - if self.plugin_info is None: - # check nb of categories - self.assertEqual(len(self.pluginManager.getCategories()),1) - sole_category = self.pluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.pluginManager.getPluginsOfCategory(sole_category)),1) - self.plugin_info = self.pluginManager.getPluginsOfCategory(sole_category)[0] - # test that the name of the plugin has been correctly defined - self.assertEqual(self.plugin_info.name,new_plugin_name) - self.assertEqual(sole_category,self.plugin_info.category) - else: - self.assertTrue(True) + def testGetSetInstallDir(self): + """ + Test getting and setting install dir. + """ + self.assertEqual(self.storing_dir, self.pluginManager.getInstallDir()) + self.pluginManager.setInstallDir("mouf/bla") + self.assertEqual("mouf/bla", self.pluginManager.getInstallDir()) - def testGetSetInstallDir(self): - """ - Test getting and setting install dir. - """ - self.assertEqual(self.storing_dir,self.pluginManager.getInstallDir()) - self.pluginManager.setInstallDir("mouf/bla") - self.assertEqual("mouf/bla",self.pluginManager.getInstallDir()) - - - def testNoneLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check_none() + def testNoneLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check_none() - def testInstallFile(self): - """ - Test if the correct plugin (defined by a file) can be installed and loaded. - """ - install_success = self.pluginManager.install(self.new_plugins_waiting_dir, - "autoinstallplugin.yapsy-autoinstall-plugin") - self.assertTrue(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check("Auto Install Plugin") + def testInstallFile(self): + """ + Test if the correct plugin (defined by a file) can be installed and loaded. + """ + install_success = self.pluginManager.install( + self.new_plugins_waiting_dir, + "autoinstallplugin.yapsy-autoinstall-plugin") + self.assertTrue(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check("Auto Install Plugin") + def testInstallDir(self): + """ + Test if the correct plugin (define by a directory) can be installed and loaded. + """ + install_success = self.pluginManager.install( + self.new_plugins_waiting_dir, + "autoinstalldirplugin.yapsy-autoinstall-plugin") + self.assertTrue(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check("Auto Install Dir Plugin") - def testInstallDir(self): - """ - Test if the correct plugin (define by a directory) can be installed and loaded. - """ - install_success = self.pluginManager.install(self.new_plugins_waiting_dir, - "autoinstalldirplugin.yapsy-autoinstall-plugin") - self.assertTrue(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check("Auto Install Dir Plugin") - + def testActivationAndDeactivation(self): + """ + Test if the activation procedure works. + """ + install_success = self.pluginManager.install( + self.new_plugins_waiting_dir, + "autoinstallplugin.yapsy-autoinstall-plugin") + self.assertTrue(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check("Auto Install Plugin") + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + self.pluginManager.activatePluginByName(self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(self.plugin_info.plugin_object.is_activated) + self.pluginManager.deactivatePluginByName(self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(not self.plugin_info.plugin_object.is_activated) - def testActivationAndDeactivation(self): - """ - Test if the activation procedure works. - """ - install_success = self.pluginManager.install(self.new_plugins_waiting_dir, - "autoinstallplugin.yapsy-autoinstall-plugin") - self.assertTrue(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check("Auto Install Plugin") - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - self.pluginManager.activatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(self.plugin_info.plugin_object.is_activated) - self.pluginManager.deactivatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(not self.plugin_info.plugin_object.is_activated) class AutoInstallZIPTestsCase(unittest.TestCase): - """ - Test the correct installation and loading of a zipped plugin. - """ + """ + Test the correct installation and loading of a zipped plugin. + """ - def setUp(self): - """ - init - """ - # create the plugin manager - storing_dir = os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins") - self.pluginManager = AutoInstallPluginManager( - storing_dir, - directories_list=[storing_dir], - plugin_info_ext="yapsy-autoinstall-plugin") - # load the plugins that may be found - self.pluginManager.collectPlugins() - # Will be used later - self.plugin_info = None - self.new_plugins_waiting_dir = os.path.join( - os.path.dirname(os.path.abspath(__file__)),"pluginstoinstall") + def setUp(self): + """ + init + """ + # create the plugin manager + storing_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins") + self.pluginManager = AutoInstallPluginManager( + storing_dir, + directories_list=[storing_dir], + plugin_info_ext="yapsy-autoinstall-plugin") + # load the plugins that may be found + self.pluginManager.collectPlugins() + # Will be used later + self.plugin_info = None + self.new_plugins_waiting_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "pluginstoinstall") + def tearDown(self): + """ + Clean the plugin installation directory. + """ + try: + os.remove( + os.path.join( + self.pluginManager.plugins_places[0], + "autoinstallzipplugin.yapsy-autoinstall-plugin")) + except OSError: + pass + try: + shutil.rmtree(os.path.join(self.pluginManager.plugins_places[0], + "autoinstallzipplugin")) + except OSError: + pass - def tearDown(self): - """ - Clean the plugin installation directory. - """ - try: - os.remove(os.path.join(self.pluginManager.plugins_places[0], - "autoinstallzipplugin.yapsy-autoinstall-plugin")) - except OSError: - pass - try: - shutil.rmtree(os.path.join(self.pluginManager.plugins_places[0], - "autoinstallzipplugin")) - except OSError: - pass - + def plugin_loading_check_none(self): + """ + Test that no plugin has been loaded. + """ + # check nb of categories + self.assertEqual(len(self.pluginManager.getCategories()), 1) + sole_category = self.pluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.pluginManager.getPluginsOfCategory(sole_category)), 0) - def plugin_loading_check_none(self): - """ - Test that no plugin has been loaded. - """ - # check nb of categories - self.assertEqual(len(self.pluginManager.getCategories()),1) - sole_category = self.pluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.pluginManager.getPluginsOfCategory(sole_category)),0) + def plugin_loading_check(self, new_plugin_name): + """ + Test if the correct plugin has been loaded. + """ + if self.plugin_info is None: + # check nb of categories + self.assertEqual(len(self.pluginManager.getCategories()), 1) + sole_category = self.pluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.pluginManager.getPluginsOfCategory(sole_category)), 1) + self.plugin_info = self.pluginManager.getPluginsOfCategory( + sole_category)[0] + # test that the name of the plugin has been correctly defined + self.assertEqual(self.plugin_info.name, new_plugin_name) + self.assertEqual(sole_category, self.plugin_info.category) + else: + self.assertTrue(True) - def plugin_loading_check(self,new_plugin_name): - """ - Test if the correct plugin has been loaded. - """ - if self.plugin_info is None: - # check nb of categories - self.assertEqual(len(self.pluginManager.getCategories()),1) - sole_category = self.pluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.pluginManager.getPluginsOfCategory(sole_category)),1) - self.plugin_info = self.pluginManager.getPluginsOfCategory(sole_category)[0] - # test that the name of the plugin has been correctly defined - self.assertEqual(self.plugin_info.name,new_plugin_name) - self.assertEqual(sole_category,self.plugin_info.category) - else: - self.assertTrue(True) + def testNoneLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check_none() - def testNoneLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check_none() + def testInstallZIP(self): + """ + Test if the correct plugin (define by a zip file) can be installed and loaded. + """ + test_file = os.path.join( + self.new_plugins_waiting_dir, + "autoinstallZIPplugin.zip") + install_success = self.pluginManager.installFromZIP(test_file) + self.assertTrue(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check("Auto Install ZIP Plugin") - def testInstallZIP(self): - """ - Test if the correct plugin (define by a zip file) can be installed and loaded. - """ - test_file = os.path.join(self.new_plugins_waiting_dir,"autoinstallZIPplugin.zip") - install_success = self.pluginManager.installFromZIP(test_file) - self.assertTrue(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check("Auto Install ZIP Plugin") - - def testInstallZIPFailOnWrongZip(self): - """ - Test if, when the zip file does not contain what is required the installation fails. - """ - test_file = os.path.join(self.new_plugins_waiting_dir,"autoinstallWRONGzipplugin.zip") - install_success = self.pluginManager.installFromZIP(test_file) - self.assertFalse(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check_none() + def testInstallZIPFailOnWrongZip(self): + """ + Test if, when the zip file does not contain what is required the installation fails. + """ + test_file = os.path.join( + self.new_plugins_waiting_dir, + "autoinstallWRONGzipplugin.zip") + install_success = self.pluginManager.installFromZIP(test_file) + self.assertFalse(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check_none() - def testInstallZIPFailOnUnexistingFile(self): - """ - Test if, when the zip file is not a file. - """ - test_file = os.path.join(self.new_plugins_waiting_dir,"doesNotExists.zip") - if sys.version_info < (2, 6): - self.assertRaises(NotImplementedError,self.pluginManager.installFromZIP,test_file) - return - install_success = self.pluginManager.installFromZIP(test_file) - self.assertFalse(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check_none() + def testInstallZIPFailOnUnexistingFile(self): + """ + Test if, when the zip file is not a file. + """ + test_file = os.path.join( + self.new_plugins_waiting_dir, + "doesNotExists.zip") + if sys.version_info < (2, 6): + self.assertRaises( + NotImplementedError, + self.pluginManager.installFromZIP, + test_file) + return + install_success = self.pluginManager.installFromZIP(test_file) + self.assertFalse(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check_none() - def testInstallZIPFailOnNotAZipFile(self): - """ - Test if, when the zip file is not a valid zip. - """ - test_file = os.path.join(self.new_plugins_waiting_dir,"AutoInstallPlugin.py") - if sys.version_info < (2, 6): - self.assertRaises(NotImplementedError,self.pluginManager.installFromZIP,test_file) - return - install_success = self.pluginManager.installFromZIP(test_file) - self.assertFalse(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check_none() - - def testActivationAndDeactivation(self): - """ - Test if the activation procedure works. - """ - test_file = os.path.join(self.new_plugins_waiting_dir,"autoinstallZIPplugin.zip") - install_success = self.pluginManager.installFromZIP(test_file) - self.assertTrue(install_success) - self.pluginManager.collectPlugins() - self.plugin_loading_check("Auto Install ZIP Plugin") - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - self.pluginManager.activatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(self.plugin_info.plugin_object.is_activated) - self.pluginManager.deactivatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(not self.plugin_info.plugin_object.is_activated) + def testInstallZIPFailOnNotAZipFile(self): + """ + Test if, when the zip file is not a valid zip. + """ + test_file = os.path.join( + self.new_plugins_waiting_dir, + "AutoInstallPlugin.py") + if sys.version_info < (2, 6): + self.assertRaises( + NotImplementedError, + self.pluginManager.installFromZIP, + test_file) + return + install_success = self.pluginManager.installFromZIP(test_file) + self.assertFalse(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check_none() + def testActivationAndDeactivation(self): + """ + Test if the activation procedure works. + """ + test_file = os.path.join( + self.new_plugins_waiting_dir, + "autoinstallZIPplugin.zip") + install_success = self.pluginManager.installFromZIP(test_file) + self.assertTrue(install_success) + self.pluginManager.collectPlugins() + self.plugin_loading_check("Auto Install ZIP Plugin") + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + self.pluginManager.activatePluginByName(self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(self.plugin_info.plugin_object.is_activated) + self.pluginManager.deactivatePluginByName(self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(not self.plugin_info.plugin_object.is_activated) suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(AutoInstallTestsCase), - unittest.TestLoader().loadTestsFromTestCase(AutoInstallZIPTestsCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(AutoInstallTestsCase), + unittest.TestLoader().loadTestsFromTestCase(AutoInstallZIPTestsCase), +]) diff --git a/package/test/test_ConfigPlugin.py b/package/test/test_ConfigPlugin.py index 7aaac1d..cfd9085 100644 --- a/package/test/test_ConfigPlugin.py +++ b/package/test/test_ConfigPlugin.py @@ -3,145 +3,147 @@ from . import test_settings -import os +import os import unittest -import configparser +from yapsy.compat import ConfigParser from yapsy.ConfigurablePluginManager import ConfigurablePluginManager + class ConfigTestCase(unittest.TestCase): - """ - Test the correct loading of a plugin that uses a configuration - file through a ConfigurablePluginManager as well as basic - commands. - """ - - CONFIG_FILE = test_settings.TEMP_CONFIG_FILE_NAME - - def setUp(self): - """ - init - """ - # create a config file - self.config_file = self.CONFIG_FILE - self.config_parser = configparser.ConfigParser() - self.plugin_info = None - # create the plugin manager - self.pluginManager = ConfigurablePluginManager( - directories_list=[os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")], - plugin_info_ext="yapsy-config-plugin", - configparser_instance=self.config_parser, - config_change_trigger=self.update_config) - # load the plugins that may be found - self.pluginManager.collectPlugins() - - def tearDown(self): - """ - When the test has been performed erase the temp file. - """ - if os.path.isfile(self.config_file): - os.remove(self.config_file) - - def testConfigurationFileExistence(self): - """ - Test if the configuration file has been properly written. - """ - # activate the only loaded plugin - self.plugin_activate() - # get rid of the plugin manager and create a new one - del self.pluginManager - del self.config_parser - self.config_parser = configparser.ConfigParser() - self.config_parser.read(self.config_file) - self.assertTrue(self.config_parser.has_section("Plugin Management")) - self.assertTrue(self.config_parser.has_option("Plugin Management", - "default_plugins_to_load")) - self.pluginManager = ConfigurablePluginManager( - directories_list=[os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")], - plugin_info_ext="yapsy-config-plugin", - configparser_instance=self.config_parser, - config_change_trigger=self.update_config) - self.pluginManager.collectPlugins() - self.plugin_loading_check() - self.assertTrue(self.plugin_info.plugin_object.is_activated) - self.pluginManager.deactivatePluginByName(self.plugin_info.name, - self.plugin_info.category) - # check that activating the plugin once again, won't cause an error - self.pluginManager.activatePluginByName(self.plugin_info.name, - self.plugin_info.category) - # Will be used later - self.plugin_info = None - - - def testLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - - def testActivationAndDeactivation(self): - """ - Test if the activation/deactivaion procedures work. - """ - self.plugin_activate() - self.pluginManager.deactivatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - - def testPluginOptions(self): - """ - Test is the plugin can register and access options from the - ConfigParser. - """ - self.plugin_activate() - plugin = self.plugin_info.plugin_object - plugin.choseTestOption("voila") - self.assertTrue(plugin.checkTestOption()) - self.assertEqual(plugin.getTestOption(),"voila") - - - #--- UTILITIES - - def plugin_loading_check(self): - """ - Test if the correct plugin has been loaded. - """ - if self.plugin_info is None: - # check nb of categories - self.assertEqual(len(self.pluginManager.getCategories()),1) - sole_category = self.pluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.pluginManager.getPluginsOfCategory(sole_category)),1) - self.plugin_info = self.pluginManager.getPluginsOfCategory(sole_category)[0] - # test that the name of the plugin has been correctly defined - self.assertEqual(self.plugin_info.name,"Config Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - else: - self.assertTrue(True) - - def plugin_activate(self): - """ - Activate the plugin with basic checking - """ - self.plugin_loading_check() - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - self.pluginManager.activatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(self.plugin_info.plugin_object.is_activated) - - - def update_config(self): - """ - Write the content of the ConfigParser in a file. - """ - cf = open(self.config_file,"a") - self.config_parser.write(cf) - cf.close() + """ + Test the correct loading of a plugin that uses a configuration + file through a ConfigurablePluginManager as well as basic + commands. + """ + + CONFIG_FILE = test_settings.TEMP_CONFIG_FILE_NAME + + def setUp(self): + """ + init + """ + # create a config file + self.config_file = self.CONFIG_FILE + self.config_parser = ConfigParser() + self.plugin_info = None + # create the plugin manager + self.pluginManager = ConfigurablePluginManager( + directories_list=[os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")], + plugin_info_ext="yapsy-config-plugin", + configparser_instance=self.config_parser, + config_change_trigger=self.update_config) + # load the plugins that may be found + self.pluginManager.collectPlugins() + + def tearDown(self): + """ + When the test has been performed erase the temp file. + """ + if os.path.isfile(self.config_file): + os.remove(self.config_file) + + def testConfigurationFileExistence(self): + """ + Test if the configuration file has been properly written. + """ + # activate the only loaded plugin + self.plugin_activate() + # get rid of the plugin manager and create a new one + del self.pluginManager + del self.config_parser + self.config_parser = ConfigParser() + self.config_parser.read(self.config_file) + self.assertTrue(self.config_parser.has_section("Plugin Management")) + self.assertTrue( + self.config_parser.has_option( + "Plugin Management", + "default_plugins_to_load")) + self.pluginManager = ConfigurablePluginManager( + directories_list=[os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")], + plugin_info_ext="yapsy-config-plugin", + configparser_instance=self.config_parser, + config_change_trigger=self.update_config) + self.pluginManager.collectPlugins() + self.plugin_loading_check() + self.assertTrue(self.plugin_info.plugin_object.is_activated) + self.pluginManager.deactivatePluginByName(self.plugin_info.name, + self.plugin_info.category) + # check that activating the plugin once again, won't cause an error + self.pluginManager.activatePluginByName(self.plugin_info.name, + self.plugin_info.category) + # Will be used later + self.plugin_info = None + + def testLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + + def testActivationAndDeactivation(self): + """ + Test if the activation/deactivaion procedures work. + """ + self.plugin_activate() + self.pluginManager.deactivatePluginByName(self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + + def testPluginOptions(self): + """ + Test is the plugin can register and access options from the + ConfigParser. + """ + self.plugin_activate() + plugin = self.plugin_info.plugin_object + plugin.choseTestOption("voila") + self.assertTrue(plugin.checkTestOption()) + self.assertEqual(plugin.getTestOption(), "voila") + + #--- UTILITIES + + def plugin_loading_check(self): + """ + Test if the correct plugin has been loaded. + """ + if self.plugin_info is None: + # check nb of categories + self.assertEqual(len(self.pluginManager.getCategories()), 1) + sole_category = self.pluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.pluginManager.getPluginsOfCategory(sole_category)), 1) + self.plugin_info = self.pluginManager.getPluginsOfCategory( + sole_category)[0] + # test that the name of the plugin has been correctly defined + self.assertEqual(self.plugin_info.name, "Config Plugin") + self.assertEqual(sole_category, self.plugin_info.category) + else: + self.assertTrue(True) + + def plugin_activate(self): + """ + Activate the plugin with basic checking + """ + self.plugin_loading_check() + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + self.pluginManager.activatePluginByName(self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(self.plugin_info.plugin_object.is_activated) + + def update_config(self): + """ + Write the content of the ConfigParser in a file. + """ + cf = open(self.config_file, "a") + self.config_parser.write(cf) + cf.close() suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(ConfigTestCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(ConfigTestCase), +]) diff --git a/package/test/test_ErrorInPlugin.py b/package/test/test_ErrorInPlugin.py index ded24b3..f696b48 100644 --- a/package/test/test_ErrorInPlugin.py +++ b/package/test/test_ErrorInPlugin.py @@ -3,63 +3,69 @@ from . import test_settings -import os +import os import unittest import logging from yapsy.PluginManager import PluginManager from yapsy import log + class ErrorTestCase(unittest.TestCase): - """ - Test the handling of errors during plugin load. - """ - def testTwoStepsLoadWithError(self): - """ - Test loading the plugins in two steps in order to collect more - deltailed informations and take care of an erroneous plugin. - """ - spm = PluginManager(directories_list=[ - os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins") - ], plugin_info_ext="yapsy-error-plugin") - # trigger the first step to look up for plugins - spm.locatePlugins() - # make full use of the "feedback" the loadPlugins can give - # - set-up the callback function that will be called *before* - # loading each plugin - callback_infos = [] - def preload_cbk(i_plugin_info): - callback_infos.append(i_plugin_info) - # - gather infos about the processed plugins (loaded or not) - # and for the test, monkey patch the logger - originalLogLevel = log.getEffectiveLevel() - log.setLevel(logging.ERROR) - errorLogCallFlag = [False] - def errorMock(*args,**kwargs): - errorLogCallFlag[0]=True - originalErrorMethod = log.error - log.error = errorMock - try: - loadedPlugins = spm.loadPlugins(callback=preload_cbk) - finally: - log.setLevel(originalLogLevel) - log.error = originalErrorMethod - self.assertTrue(errorLogCallFlag[0]) - self.assertEqual(len(loadedPlugins),1) - self.assertEqual(len(callback_infos),1) - self.assertTrue(isinstance(callback_infos[0].error,tuple)) - self.assertEqual(loadedPlugins[0],callback_infos[0]) - self.assertEqual(callback_infos[0].error[0],ImportError) - # check that the getCategories works - self.assertEqual(len(spm.getCategories()),1) - sole_category = spm.getCategories()[0] - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),0) + """ + Test the handling of errors during plugin load. + """ + + def testTwoStepsLoadWithError(self): + """ + Test loading the plugins in two steps in order to collect more + deltailed informations and take care of an erroneous plugin. + """ + spm = PluginManager( + directories_list=[ + os.path.join( + os.path.dirname( + os.path.abspath(__file__)), + "plugins")], + plugin_info_ext="yapsy-error-plugin") + # trigger the first step to look up for plugins + spm.locatePlugins() + # make full use of the "feedback" the loadPlugins can give + # - set-up the callback function that will be called *before* + # loading each plugin + callback_infos = [] + + def preload_cbk(i_plugin_info): + callback_infos.append(i_plugin_info) + # - gather infos about the processed plugins (loaded or not) + # and for the test, monkey patch the logger + originalLogLevel = log.getEffectiveLevel() + log.setLevel(logging.ERROR) + errorLogCallFlag = [False] + def errorMock(*args, **kwargs): + errorLogCallFlag[0] = True + originalErrorMethod = log.error + log.error = errorMock + try: + loadedPlugins = spm.loadPlugins(callback=preload_cbk) + finally: + log.setLevel(originalLogLevel) + log.error = originalErrorMethod + self.assertTrue(errorLogCallFlag[0]) + self.assertEqual(len(loadedPlugins), 1) + self.assertEqual(len(callback_infos), 1) + self.assertTrue(isinstance(callback_infos[0].error, tuple)) + self.assertEqual(loadedPlugins[0], callback_infos[0]) + self.assertEqual(callback_infos[0].error[0], ImportError) + # check that the getCategories works + self.assertEqual(len(spm.getCategories()), 1) + sole_category = spm.getCategories()[0] + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 0) suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(ErrorTestCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(ErrorTestCase), +]) diff --git a/package/test/test_FilterPlugin.py b/package/test/test_FilterPlugin.py index be66e44..adada56 100644 --- a/package/test/test_FilterPlugin.py +++ b/package/test/test_FilterPlugin.py @@ -4,242 +4,254 @@ from . import test_settings from .test_settings import TEST_MESSAGE import unittest -import os +import os import re from yapsy.FilteredPluginManager import FilteredPluginManager class testFilter(FilteredPluginManager): - """ - Test filter class. - Refused to load plugins whose Name starts with 'C'. - """ - _bannednames = re.compile("^C") - def isPluginOk(self,info): - return not self._bannednames.match(info.name) + """ + Test filter class. + Refused to load plugins whose Name starts with 'C'. + """ + _bannednames = re.compile("^C") + + def isPluginOk(self, info): + return not self._bannednames.match(info.name) class FilteredTestsCase(unittest.TestCase): - """ - Test the correct loading of a simple plugin as well as basic - commands. - """ - - def setUp(self): - """ - init - """ - # create the plugin manager + + """ + Test the correct loading of a simple plugin as well as basic + commands. + """ + + def setUp(self): + """ + init + """ + # create the plugin manager # print os.path.join(os.path.dirname(os.path.abspath(__file__)),"plugins") - self.filteredPluginManager = testFilter( - directories_list=[os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")], - plugin_info_ext="yapsy-filter-plugin", - ) - # load the plugins that may be found - self.filteredPluginManager.collectPlugins() - # Will be used later - self.plugin_info = None - - def plugin_loading_check(self): - """ - Test if the correct plugins have been loaded. - """ - # check nb of categories - self.assertEqual(len(self.filteredPluginManager.getCategories()),1) - sole_category = self.filteredPluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.filteredPluginManager.getPluginsOfCategory(sole_category)),1) - plugins = self.filteredPluginManager.getPluginsOfCategory(sole_category) - for plugin_info in plugins: - TEST_MESSAGE("plugin info: %s" % plugin_info) - self.plugin_info = plugin_info - self.assertTrue(self.plugin_info) - self.assertEqual(self.plugin_info.name,"Simple Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - - def testLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - - - def testActivationAndDeactivation(self): - """ - Test if the activation procedure works. - """ - self.plugin_loading_check() - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) - self.plugin_info.plugin_object.activate() - self.assertTrue(self.plugin_info.plugin_object.is_activated) - self.plugin_info.plugin_object.deactivate() - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - - - def testRejectedList(self): - """ - Test if the list of rejected plugins is correct. - """ - for plugin in self.filteredPluginManager.getRejectedPlugins(): - TEST_MESSAGE("plugin info: %s" % plugin[2]) - self.assertEqual(plugin[2].name,"Config Plugin") - - def testRejectedStable(self): - reject1 = list(self.filteredPluginManager.getRejectedPlugins()) - self.filteredPluginManager.collectPlugins() - reject2 = list(self.filteredPluginManager.getRejectedPlugins()) - self.assertEqual(len(reject1),len(reject2)) - - - def testRejectPlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.rejectedPlugins - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - nrRejected = len(rejected) - for plugin in rejected: - self.filteredPluginManager.rejectPluginCandidate(plugin) - self.assertEqual(nrRejected,len(self.filteredPluginManager.rejectedPlugins)) - - def testRemovePlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.rejectedPlugins - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - for plugin in rejected: - self.filteredPluginManager.removePluginCandidate(plugin) - self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual( nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) - - def testAppendRejectedPlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.getRejectedPlugins() - nrRejected = len(rejected) - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - #Remove the rejected plugins into out own list. - for plugin in rejected: - self.filteredPluginManager.removePluginCandidate(plugin) - self.assertEqual(len(self.filteredPluginManager.getRejectedPlugins()),0) - - ##Now Actually test Append. - for plugin in rejected: - self.filteredPluginManager.appendPluginCandidate(plugin) - self.assertEqual(nrRejected ,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual(nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) - - def testAppendOkPlugins(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.getRejectedPlugins() - nrRejected = len(rejected) - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - #Remove the rejected plugins again. - for plugin in rejected: - self.filteredPluginManager.removePluginCandidate(plugin) - self.assertEqual(len(self.filteredPluginManager.getRejectedPlugins()),0) - - for plugin in rejected: - #change the name so it is acceptable. - plugin[2].name = "X" + plugin[2].name[1:] - self.filteredPluginManager.appendPluginCandidate(plugin) - self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual(nrRejected + nrCandidates , len(self.filteredPluginManager.getPluginCandidates())) - - - - - def testUnrejectPlugin(self): - self.filteredPluginManager.locatePlugins() - rejected = self.filteredPluginManager.rejectedPlugins - nrRejected = len(rejected) - nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) - #If this fails the test in not meaningful.. - self.assertTrue(len(rejected) > 0) - for plugin in rejected: - self.filteredPluginManager.unrejectPluginCandidate(plugin) - self.assertEqual(0,len(self.filteredPluginManager.rejectedPlugins)) - self.assertEqual( nrRejected + nrCandidates , - len(self.filteredPluginManager.getPluginCandidates())) + self.filteredPluginManager = testFilter( + directories_list=[os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")], + plugin_info_ext="yapsy-filter-plugin", + ) + # load the plugins that may be found + self.filteredPluginManager.collectPlugins() + # Will be used later + self.plugin_info = None + + def plugin_loading_check(self): + """ + Test if the correct plugins have been loaded. + """ + # check nb of categories + self.assertEqual(len(self.filteredPluginManager.getCategories()), 1) + sole_category = self.filteredPluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.filteredPluginManager.getPluginsOfCategory(sole_category)), 1) + plugins = self.filteredPluginManager.getPluginsOfCategory( + sole_category) + for plugin_info in plugins: + TEST_MESSAGE("plugin info: %s" % plugin_info) + self.plugin_info = plugin_info + self.assertTrue(self.plugin_info) + self.assertEqual(self.plugin_info.name, "Simple Plugin") + self.assertEqual(sole_category, self.plugin_info.category) + + def testLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + + def testActivationAndDeactivation(self): + """ + Test if the activation procedure works. + """ + self.plugin_loading_check() + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) + self.plugin_info.plugin_object.activate() + self.assertTrue(self.plugin_info.plugin_object.is_activated) + self.plugin_info.plugin_object.deactivate() + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + + def testRejectedList(self): + """ + Test if the list of rejected plugins is correct. + """ + for plugin in self.filteredPluginManager.getRejectedPlugins(): + TEST_MESSAGE("plugin info: %s" % plugin[2]) + self.assertEqual(plugin[2].name, "Config Plugin") + + def testRejectedStable(self): + reject1 = list(self.filteredPluginManager.getRejectedPlugins()) + self.filteredPluginManager.collectPlugins() + reject2 = list(self.filteredPluginManager.getRejectedPlugins()) + self.assertEqual(len(reject1), len(reject2)) + + def testRejectPlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.rejectedPlugins + # If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + nrRejected = len(rejected) + for plugin in rejected: + self.filteredPluginManager.rejectPluginCandidate(plugin) + self.assertEqual( + nrRejected, len( + self.filteredPluginManager.rejectedPlugins)) + + def testRemovePlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.rejectedPlugins + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + # If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + for plugin in rejected: + self.filteredPluginManager.removePluginCandidate(plugin) + self.assertEqual(0, len(self.filteredPluginManager.rejectedPlugins)) + self.assertEqual( + nrCandidates, len( + self.filteredPluginManager.getPluginCandidates())) + + def testAppendRejectedPlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.getRejectedPlugins() + nrRejected = len(rejected) + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + + # If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + # Remove the rejected plugins into out own list. + for plugin in rejected: + self.filteredPluginManager.removePluginCandidate(plugin) + self.assertEqual( + len(self.filteredPluginManager.getRejectedPlugins()), 0) + + # Now Actually test Append. + for plugin in rejected: + self.filteredPluginManager.appendPluginCandidate(plugin) + self.assertEqual( + nrRejected, len( + self.filteredPluginManager.rejectedPlugins)) + self.assertEqual( + nrCandidates, len( + self.filteredPluginManager.getPluginCandidates())) + + def testAppendOkPlugins(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.getRejectedPlugins() + nrRejected = len(rejected) + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + + # If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + # Remove the rejected plugins again. + for plugin in rejected: + self.filteredPluginManager.removePluginCandidate(plugin) + self.assertEqual( + len(self.filteredPluginManager.getRejectedPlugins()), 0) + + for plugin in rejected: + # change the name so it is acceptable. + plugin[2].name = "X" + plugin[2].name[1:] + self.filteredPluginManager.appendPluginCandidate(plugin) + self.assertEqual(0, len(self.filteredPluginManager.rejectedPlugins)) + self.assertEqual(nrRejected + nrCandidates, + len(self.filteredPluginManager.getPluginCandidates())) + + def testUnrejectPlugin(self): + self.filteredPluginManager.locatePlugins() + rejected = self.filteredPluginManager.rejectedPlugins + nrRejected = len(rejected) + nrCandidates = len(self.filteredPluginManager.getPluginCandidates()) + # If this fails the test in not meaningful.. + self.assertTrue(len(rejected) > 0) + for plugin in rejected: + self.filteredPluginManager.unrejectPluginCandidate(plugin) + self.assertEqual(0, len(self.filteredPluginManager.rejectedPlugins)) + self.assertEqual(nrRejected + nrCandidates, + len(self.filteredPluginManager.getPluginCandidates())) class FilteredWithMonkeyPathTestsCase(unittest.TestCase): - """ - Test the correct loading oand filtering of plugins when the FilteredPluginManager is just monkey-patched - """ - - def setUp(self): - """ - init - """ - # create the plugin manager + + """ + Test the correct loading oand filtering of plugins when the FilteredPluginManager is just monkey-patched + """ + + def setUp(self): + """ + init + """ + # create the plugin manager # print os.path.join(os.path.dirname(os.path.abspath(__file__)),"plugins") - self.filteredPluginManager = FilteredPluginManager( - directories_list=[os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")], - plugin_info_ext="yapsy-filter-plugin", - ) - self.filteredPluginManager.isPluginOk = lambda info:not re.match("^C",info.name) - # load the plugins that may be found - self.filteredPluginManager.collectPlugins() - # Will be used later - self.plugin_info = None - - def plugin_loading_check(self): - """ - Test if the correct plugins have been loaded. - """ - # check nb of categories - self.assertEqual(len(self.filteredPluginManager.getCategories()),1) - sole_category = self.filteredPluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.filteredPluginManager.getPluginsOfCategory(sole_category)),1) - plugins = self.filteredPluginManager.getPluginsOfCategory(sole_category) - for plugin_info in plugins: - TEST_MESSAGE("plugin info: %s" % plugin_info) - self.plugin_info = plugin_info - self.assertTrue(self.plugin_info) - self.assertEqual(self.plugin_info.name,"Simple Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - - def testLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - - - def testActivationAndDeactivation(self): - """ - Test if the activation procedure works. - """ - self.plugin_loading_check() - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) - self.plugin_info.plugin_object.activate() - self.assertTrue(self.plugin_info.plugin_object.is_activated) - self.plugin_info.plugin_object.deactivate() - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - - - def testRejectedList(self): - """ - Test if the list of rejected plugins is correct. - """ - for plugin in self.filteredPluginManager.getRejectedPlugins(): - TEST_MESSAGE("plugin info: %s" % plugin[2]) - self.assertEqual(plugin[2].name,"Config Plugin") + self.filteredPluginManager = FilteredPluginManager( + directories_list=[os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")], + plugin_info_ext="yapsy-filter-plugin", + ) + self.filteredPluginManager.isPluginOk = lambda info: not re.match( + "^C", + info.name) + # load the plugins that may be found + self.filteredPluginManager.collectPlugins() + # Will be used later + self.plugin_info = None + + def plugin_loading_check(self): + """ + Test if the correct plugins have been loaded. + """ + # check nb of categories + self.assertEqual(len(self.filteredPluginManager.getCategories()), 1) + sole_category = self.filteredPluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.filteredPluginManager.getPluginsOfCategory(sole_category)), 1) + plugins = self.filteredPluginManager.getPluginsOfCategory( + sole_category) + for plugin_info in plugins: + TEST_MESSAGE("plugin info: %s" % plugin_info) + self.plugin_info = plugin_info + self.assertTrue(self.plugin_info) + self.assertEqual(self.plugin_info.name, "Simple Plugin") + self.assertEqual(sole_category, self.plugin_info.category) + + def testLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + + def testActivationAndDeactivation(self): + """ + Test if the activation procedure works. + """ + self.plugin_loading_check() + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) + self.plugin_info.plugin_object.activate() + self.assertTrue(self.plugin_info.plugin_object.is_activated) + self.plugin_info.plugin_object.deactivate() + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + + def testRejectedList(self): + """ + Test if the list of rejected plugins is correct. + """ + for plugin in self.filteredPluginManager.getRejectedPlugins(): + TEST_MESSAGE("plugin info: %s" % plugin[2]) + self.assertEqual(plugin[2].name, "Config Plugin") suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(FilteredTestsCase), - unittest.TestLoader().loadTestsFromTestCase(FilteredWithMonkeyPathTestsCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(FilteredTestsCase), + unittest.TestLoader().loadTestsFromTestCase(FilteredWithMonkeyPathTestsCase), +]) diff --git a/package/test/test_PluginFileLocator.py b/package/test/test_PluginFileLocator.py index fde19a6..d75bf5b 100644 --- a/package/test/test_PluginFileLocator.py +++ b/package/test/test_PluginFileLocator.py @@ -5,10 +5,9 @@ import unittest import sys import os -from configparser import ConfigParser +from yapsy.compat import ConfigParser, StringIO import tempfile import shutil -from io import StringIO from yapsy import PLUGIN_NAME_FORBIDEN_STRING from yapsy.PluginManager import PluginManager @@ -22,467 +21,555 @@ class IPluginLocatorTest(unittest.TestCase): + def test_deprecated_method_dont_raise_notimplemetederror(self): + class DummyPluginLocator(IPluginLocator): + pass + dpl = DummyPluginLocator() + self.assertEqual( + (None, + None, + None), + dpl.getPluginNameAndModuleFromStream(None)) + dpl.setPluginInfoClass(PluginInfo) + self.assertEqual(None, dpl.getPluginInfoClass()) + dpl.setPluginPlaces([]) + dpl.updatePluginPlaces([]) + - def test_deprecated_method_dont_raise_notimplemetederror(self): - class DummyPluginLocator(IPluginLocator): - pass - dpl = DummyPluginLocator() - self.assertEqual((None,None,None),dpl.getPluginNameAndModuleFromStream(None)) - dpl.setPluginInfoClass(PluginInfo) - self.assertEqual(None,dpl.getPluginInfoClass()) - dpl.setPluginPlaces([]) - dpl.updatePluginPlaces([]) - class PluginFileAnalyzerWithInfoFileTest(unittest.TestCase): - """ - Test that the "info file" analyzer enforces the correct policy. - """ - - def setUp(self): - """ - init - """ - self.plugin_directory = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "plugins") - self.yapsy_plugin_path = os.path.join(self.plugin_directory,"simpleplugin.yapsy-plugin") - self.version_plugin_path = os.path.join(self.plugin_directory,"versioned11.version-plugin") - self.yapsy_filter_plugin_path = os.path.join(self.plugin_directory,"simpleplugin.yapsy-filter-plugin") - - def test_Contruction(self): - analyzer = PluginFileAnalyzerWithInfoFile("mouf") - self.assertEqual(analyzer.name,"mouf") - - def test_isValid(self): - analyzer = PluginFileAnalyzerWithInfoFile("mouf") - self.assertTrue(analyzer.isValidPlugin(self.yapsy_plugin_path)) - self.assertFalse(analyzer.isValidPlugin(self.version_plugin_path)) - - def test_getInfosDictFromPlugin(self): - analyzer = PluginFileAnalyzerWithInfoFile("mouf") - info_dict,cf_parser = analyzer.getInfosDictFromPlugin(self.plugin_directory, - os.path.basename(self.yapsy_plugin_path)) - self.assertEqual(info_dict,{'website': 'http://mathbench.sourceforge.net', 'description': 'A simple plugin usefull for basic testing', 'author': 'Thibauld Nion', 'version': '0.1', 'path': '%s/SimplePlugin' % self.plugin_directory, 'name': 'Simple Plugin', 'copyright': '2014'}) - self.assertTrue(isinstance(cf_parser,ConfigParser)) - - def test_isValid_WithMultiExtensions(self): - analyzer = PluginFileAnalyzerWithInfoFile("mouf",("yapsy-plugin","yapsy-filter-plugin")) - self.assertTrue(analyzer.isValidPlugin(self.yapsy_plugin_path)) - self.assertFalse(analyzer.isValidPlugin(self.version_plugin_path)) - self.assertTrue(analyzer.isValidPlugin(self.yapsy_filter_plugin_path)) - - def test__extractCorePluginInfo_with_minimal_description(self): - plugin_desc_content = StringIO("""\ + + """ + Test that the "info file" analyzer enforces the correct policy. + """ + + def setUp(self): + """ + init + """ + self.plugin_directory = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "plugins") + self.yapsy_plugin_path = os.path.join( + self.plugin_directory, + "simpleplugin.yapsy-plugin") + self.version_plugin_path = os.path.join( + self.plugin_directory, + "versioned11.version-plugin") + self.yapsy_filter_plugin_path = os.path.join( + self.plugin_directory, + "simpleplugin.yapsy-filter-plugin") + + def test_Contruction(self): + analyzer = PluginFileAnalyzerWithInfoFile("mouf") + self.assertEqual(analyzer.name, "mouf") + + def test_isValid(self): + analyzer = PluginFileAnalyzerWithInfoFile("mouf") + self.assertTrue(analyzer.isValidPlugin(self.yapsy_plugin_path)) + self.assertFalse(analyzer.isValidPlugin(self.version_plugin_path)) + + def test_getInfosDictFromPlugin(self): + analyzer = PluginFileAnalyzerWithInfoFile("mouf") + info_dict, cf_parser = analyzer.getInfosDictFromPlugin( + self.plugin_directory, os.path.basename( + self.yapsy_plugin_path)) + self.assertEqual(info_dict, + {'website': 'http://mathbench.sourceforge.net', + 'description': 'A simple plugin usefull for basic testing', + 'author': 'Thibauld Nion', + 'version': '0.1', + 'path': '%s/SimplePlugin' % self.plugin_directory, + 'name': 'Simple Plugin', + 'copyright': '2014'}) + self.assertTrue(isinstance(cf_parser, ConfigParser)) + + def test_isValid_WithMultiExtensions(self): + analyzer = PluginFileAnalyzerWithInfoFile( + "mouf", ("yapsy-plugin", "yapsy-filter-plugin")) + self.assertTrue(analyzer.isValidPlugin(self.yapsy_plugin_path)) + self.assertFalse(analyzer.isValidPlugin(self.version_plugin_path)) + self.assertTrue(analyzer.isValidPlugin(self.yapsy_filter_plugin_path)) + + def test__extractCorePluginInfo_with_minimal_description(self): + plugin_desc_content = StringIO("""\ [Core] Name = Simple Plugin Module = SimplePlugin """) - analyzer = PluginFileAnalyzerWithInfoFile("mouf", - ("yapsy-plugin")) - infos, parser = analyzer._extractCorePluginInfo("bla",plugin_desc_content) - self.assertEqual("Simple Plugin", infos["name"]) - self.assertEqual(os.path.join("bla","SimplePlugin"), infos["path"]) - self.assertTrue(isinstance(parser,ConfigParser)) - - def test_getPluginNameAndModuleFromStream_with_invalid_descriptions(self): - plugin_desc_content = StringIO("""\ + analyzer = PluginFileAnalyzerWithInfoFile("mouf", + ("yapsy-plugin")) + infos, parser = analyzer._extractCorePluginInfo( + "bla", plugin_desc_content) + self.assertEqual("Simple Plugin", infos["name"]) + self.assertEqual(os.path.join("bla", "SimplePlugin"), infos["path"]) + self.assertTrue(isinstance(parser, ConfigParser)) + + def test_getPluginNameAndModuleFromStream_with_invalid_descriptions(self): + plugin_desc_content = StringIO("""\ [Core] Name = Bla{0}Bli Module = SimplePlugin """.format(PLUGIN_NAME_FORBIDEN_STRING)) - analyzer = PluginFileAnalyzerWithInfoFile("mouf", - ("yapsy-plugin")) - res = analyzer._extractCorePluginInfo("bla",plugin_desc_content) - self.assertEqual((None, None), res) - plugin_desc_content = StringIO("""\ + analyzer = PluginFileAnalyzerWithInfoFile("mouf", + ("yapsy-plugin")) + res = analyzer._extractCorePluginInfo("bla", plugin_desc_content) + self.assertEqual((None, None), res) + plugin_desc_content = StringIO("""\ [Core] Name = Simple Plugin """) - analyzer = PluginFileAnalyzerWithInfoFile("mouf", - ("yapsy-plugin")) - res = analyzer._extractCorePluginInfo("bla",plugin_desc_content) - self.assertEqual((None, None), res) - plugin_desc_content = StringIO("""\ + analyzer = PluginFileAnalyzerWithInfoFile("mouf", + ("yapsy-plugin")) + res = analyzer._extractCorePluginInfo("bla", plugin_desc_content) + self.assertEqual((None, None), res) + plugin_desc_content = StringIO("""\ [Core] Module = Simple Plugin """) - res = analyzer._extractCorePluginInfo("bla",plugin_desc_content) - self.assertEqual((None, None), res) - plugin_desc_content = StringIO("""\ + res = analyzer._extractCorePluginInfo("bla", plugin_desc_content) + self.assertEqual((None, None), res) + plugin_desc_content = StringIO("""\ [Mouf] Bla = Simple Plugin """) - res = analyzer._extractCorePluginInfo("bla",plugin_desc_content) - self.assertEqual((None, None), res) + res = analyzer._extractCorePluginInfo("bla", plugin_desc_content) + self.assertEqual((None, None), res) class PluginFileAnalyzerMathingRegexTest(unittest.TestCase): - """ - Test that the "regex" analyzer enforces the correct policy. - """ - - def setUp(self): - """ - init - """ - self.plugin_directory = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "plugins") - self.yapsy_plugin_path = os.path.join(self.plugin_directory,"SimplePlugin.py") - self.version_plugin_10_path = os.path.join(self.plugin_directory,"VersionedPlugin10.py") - self.version_plugin_12_path = os.path.join(self.plugin_directory,"VersionedPlugin12.py") - - def test_Contruction(self): - analyzer = PluginFileAnalyzerMathingRegex("mouf",".*") - self.assertEqual(analyzer.name,"mouf") - - def test_isValid(self): - analyzer = PluginFileAnalyzerMathingRegex("mouf",r".*VersionedPlugin\d+\.py$") - self.assertFalse(analyzer.isValidPlugin(self.yapsy_plugin_path)) - self.assertTrue(analyzer.isValidPlugin(self.version_plugin_10_path)) - self.assertTrue(analyzer.isValidPlugin(self.version_plugin_12_path)) - - def test_getInfosDictFromPlugin(self): - analyzer = PluginFileAnalyzerMathingRegex("mouf",r".*VersionedPlugin\d+\.py$") - info_dict,cf_parser = analyzer.getInfosDictFromPlugin(self.plugin_directory, - os.path.basename(self.version_plugin_10_path)) - self.assertEqual(info_dict,{'path': self.version_plugin_10_path, 'name': 'VersionedPlugin10'}) - self.assertTrue(isinstance(cf_parser,ConfigParser)) + + """ + Test that the "regex" analyzer enforces the correct policy. + """ + + def setUp(self): + """ + init + """ + self.plugin_directory = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "plugins") + self.yapsy_plugin_path = os.path.join( + self.plugin_directory, + "SimplePlugin.py") + self.version_plugin_10_path = os.path.join( + self.plugin_directory, + "VersionedPlugin10.py") + self.version_plugin_12_path = os.path.join( + self.plugin_directory, + "VersionedPlugin12.py") + + def test_Contruction(self): + analyzer = PluginFileAnalyzerMathingRegex("mouf", ".*") + self.assertEqual(analyzer.name, "mouf") + + def test_isValid(self): + analyzer = PluginFileAnalyzerMathingRegex( + "mouf", + r".*VersionedPlugin\d+\.py$") + self.assertFalse(analyzer.isValidPlugin(self.yapsy_plugin_path)) + self.assertTrue(analyzer.isValidPlugin(self.version_plugin_10_path)) + self.assertTrue(analyzer.isValidPlugin(self.version_plugin_12_path)) + + def test_getInfosDictFromPlugin(self): + analyzer = PluginFileAnalyzerMathingRegex( + "mouf", + r".*VersionedPlugin\d+\.py$") + info_dict, cf_parser = analyzer.getInfosDictFromPlugin( + self.plugin_directory, os.path.basename( + self.version_plugin_10_path)) + self.assertEqual( + info_dict, { + 'path': self.version_plugin_10_path, 'name': 'VersionedPlugin10'}) + self.assertTrue(isinstance(cf_parser, ConfigParser)) + class PluginFileLocatorTest(unittest.TestCase): - """ - Test that the "file" locator. - - NB: backward compatible methods are not directly tested here. We - rely only on the 'indirect' tests made for the classes that still - depend on them. - """ - - def setUp(self): - """ - init - """ - self.plugin_directory = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "plugins") - self.plugin_as_dir_directory = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "pluginsasdirs") - self.plugin_info_file = "simpleplugin.yapsy-plugin" - self.plugin_name = "SimplePlugin" - self.plugin_impl_file = self.plugin_name+".py" - - def test_default_plugins_place_is_parent_dir(self): - """Test a non-trivial default behaviour introduced some time ago :S""" - pl = PluginFileLocator() - self.assertTrue("package/yapsy" in pl.plugins_places[0]) - - def test_locatePlugins(self): - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_directory]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,1) - self.assertEqual(len(candidates),num) - self.assertEqual(os.path.join(self.plugin_directory,self.plugin_info_file), - candidates[0][0]) - self.assertEqual(os.path.join(self.plugin_directory,self.plugin_name), - candidates[0][1]) - self.assertTrue(isinstance(candidates[0][2],PluginInfo)) - - def test_locatePlugins_when_plugin_is_symlinked(self): - if "win" in sys.platform: - return - temp_dir = tempfile.mkdtemp() - try: - plugin_info_file = "simpleplugin.yapsy-plugin" - plugin_impl_file = "SimplePlugin.py" - os.symlink(os.path.join(self.plugin_directory,plugin_info_file), - os.path.join(temp_dir,plugin_info_file)) - os.symlink(os.path.join(self.plugin_directory,plugin_impl_file), - os.path.join(temp_dir,plugin_impl_file)) - pl = PluginFileLocator() - pl.setPluginPlaces([temp_dir]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,1) - self.assertEqual(len(candidates),num) - self.assertEqual(os.path.join(temp_dir,self.plugin_info_file), - candidates[0][0]) - self.assertEqual(os.path.join(temp_dir,self.plugin_name), - candidates[0][1]) - self.assertTrue(isinstance(candidates[0][2],PluginInfo)) - finally: - shutil.rmtree(temp_dir) - - def test_locatePlugins_when_plugin_is_a_directory(self): - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_as_dir_directory]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,1) - self.assertEqual(len(candidates),num) - self.assertEqual(os.path.join(self.plugin_as_dir_directory,self.plugin_info_file), - candidates[0][0]) - self.assertEqual(os.path.join(self.plugin_as_dir_directory,self.plugin_name, - "__init__"), - candidates[0][1]) - self.assertTrue(isinstance(candidates[0][2],PluginInfo)) - - def test_locatePlugins_when_plugin_is_a_symlinked_directory(self): - if "win" in sys.platform: - return - temp_dir = tempfile.mkdtemp() - try: - plugin_info_file = "simpleplugin.yapsy-plugin" - plugin_impl_dir = "SimplePlugin" - os.symlink(os.path.join(self.plugin_as_dir_directory,plugin_info_file), - os.path.join(temp_dir,plugin_info_file)) - os.symlink(os.path.join(self.plugin_as_dir_directory,plugin_impl_dir), - os.path.join(temp_dir,plugin_impl_dir)) - pl = PluginFileLocator() - pl.setPluginPlaces([temp_dir]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,1) - self.assertEqual(len(candidates),num) - self.assertEqual(os.path.join(temp_dir,self.plugin_info_file), - candidates[0][0]) - self.assertEqual(os.path.join(temp_dir,self.plugin_name,"__init__"), - candidates[0][1]) - self.assertTrue(isinstance(candidates[0][2],PluginInfo)) - finally: - shutil.rmtree(temp_dir) - - def test_locatePlugins_recursively_when_plugin_is_a_directory(self): - temp_dir = tempfile.mkdtemp() - try: - temp_sub_dir = os.path.join(temp_dir,"plugins") - shutil.copytree(self.plugin_as_dir_directory,temp_sub_dir) - pl = PluginFileLocator() - pl.setPluginPlaces([temp_dir]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,1) - self.assertEqual(len(candidates),num) - self.assertEqual(os.path.join(temp_sub_dir,self.plugin_info_file), - candidates[0][0]) - self.assertEqual(os.path.join(temp_sub_dir,self.plugin_name, - "__init__"), - candidates[0][1]) - self.assertTrue(isinstance(candidates[0][2],PluginInfo)) - finally: - shutil.rmtree(temp_dir) - - def test_locatePlugins_recursively_fails_when_recursion_is_disabled(self): - temp_dir = tempfile.mkdtemp() - try: - temp_sub_dir = os.path.join(temp_dir,"plugins") - shutil.copytree(self.plugin_as_dir_directory,temp_sub_dir) - pl = PluginFileLocator() - pl.disableRecursiveScan() - pl.setPluginPlaces([temp_dir]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,0) - self.assertEqual(len(candidates),num) - finally: - shutil.rmtree(temp_dir) - - def test_locatePlugins_recursively_when_plugin_is_a_symlinked_directory(self): - temp_dir = tempfile.mkdtemp() - try: - temp_sub_dir = os.path.join(temp_dir,"plugins") - os.mkdir(temp_sub_dir) - plugin_info_file = "simpleplugin.yapsy-plugin" - plugin_impl_dir = "SimplePlugin" - os.symlink(os.path.join(self.plugin_as_dir_directory,plugin_info_file), - os.path.join(temp_sub_dir,plugin_info_file)) - os.symlink(os.path.join(self.plugin_as_dir_directory,plugin_impl_dir), - os.path.join(temp_sub_dir,plugin_impl_dir)) - pl = PluginFileLocator() - pl.setPluginPlaces([temp_dir]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,1) - self.assertEqual(len(candidates),num) - self.assertEqual(os.path.join(temp_sub_dir,self.plugin_info_file), - candidates[0][0]) - self.assertEqual(os.path.join(temp_sub_dir,self.plugin_name, - "__init__"), - candidates[0][1]) - self.assertTrue(isinstance(candidates[0][2],PluginInfo)) - finally: - shutil.rmtree(temp_dir) - - def test_locatePlugins_recursively_when_plugin_parent_dir_is_a_symlinked_directory(self): - # This actually reproduced the "Plugin detection doesn't follow symlinks" bug - # at http://sourceforge.net/p/yapsy/bugs/19/ - temp_dir = tempfile.mkdtemp() - try: - temp_sub_dir = os.path.join(temp_dir,"plugins") - os.symlink(self.plugin_as_dir_directory,temp_sub_dir) - pl = PluginFileLocator() - pl.setPluginPlaces([temp_dir]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,1) - self.assertEqual(len(candidates),num) - self.assertEqual(os.path.join(temp_sub_dir,self.plugin_info_file), - candidates[0][0]) - self.assertEqual(os.path.join(temp_sub_dir,self.plugin_name, - "__init__"), - candidates[0][1]) - self.assertTrue(isinstance(candidates[0][2],PluginInfo)) - finally: - shutil.rmtree(temp_dir) - - def test_gatherCorePluginInfo(self): - pl = PluginFileLocator() - plugin_info,cf_parser = pl.gatherCorePluginInfo(self.plugin_directory,"simpleplugin.yapsy-plugin") - self.assertTrue(plugin_info.name,"Simple Plugin") - self.assertTrue(isinstance(cf_parser,ConfigParser)) - plugin_info,cf_parser = pl.gatherCorePluginInfo(self.plugin_directory,"notaplugin.atall") - self.assertEqual(plugin_info,None) - self.assertEqual(cf_parser,None) - - def test_setAnalyzer(self): - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_directory]) - newAnalyzer = PluginFileAnalyzerMathingRegex("mouf",r".*VersionedPlugin\d+\.py$") - pl.setAnalyzers([newAnalyzer]) - candidates, num = pl.locatePlugins() - self.assertEqual(num,4) - self.assertEqual(len(candidates),num) - - def test_appendAnalyzer(self): - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_directory]) - newAnalyzer = PluginFileAnalyzerMathingRegex("mouf",r".*VersionedPlugin\d+\.py$") - pl.appendAnalyzer(newAnalyzer) - candidates, num = pl.locatePlugins() - self.assertEqual(num,5) - self.assertEqual(len(candidates),num) - - def test_removeAnalyzers_when_analyzer_is_unknown(self): - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_directory]) - pl.removeAnalyzers("nogo") - - def test_removeAnalyzers(self): - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_directory]) - newAnalyzer = PluginFileAnalyzerMathingRegex("mouf",r".*VersionedPlugin\d+\.py$") - pl.appendAnalyzer(newAnalyzer) - pl.removeAnalyzers("info_ext") - candidates, num = pl.locatePlugins() - self.assertEqual(num,4) - self.assertEqual(len(candidates),num) - - def test_removeAllAnalyzers(self): - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_directory]) - pl.removeAllAnalyzer() - candidates, num = pl.locatePlugins() - self.assertEqual(num,0) - self.assertEqual(len(candidates),num) - - def test_setPluginInfoClass_for_named_analyzer(self): - class SpecificPluginInfo(PluginInfo): - pass - pl = PluginFileLocator() - pl.setPluginPlaces([self.plugin_directory]) - newAnalyzer = PluginFileAnalyzerMathingRegex("mouf",r".*VersionedPlugin\d+\.py$") - pl.appendAnalyzer(newAnalyzer) - pl.setPluginInfoClass(SpecificPluginInfo,"info_ext") - candidates, num = pl.locatePlugins() - self.assertEqual(num,5) - self.assertEqual(len(candidates),num) - versioned_plugins = [c for c in candidates if "VersionedPlugin" in c[0]] - self.assertEqual(4,len(versioned_plugins)) - for p in versioned_plugins: - self.assertTrue(isinstance(p[2],PluginInfo)) - simple_plugins = [c for c in candidates if "VersionedPlugin" not in c[0]] - self.assertEqual(1,len(simple_plugins)) - for p in simple_plugins: - self.assertTrue(isinstance(p[2],SpecificPluginInfo)) - + + """ + Test that the "file" locator. + + NB: backward compatible methods are not directly tested here. We + rely only on the 'indirect' tests made for the classes that still + depend on them. + """ + + def setUp(self): + """ + init + """ + self.plugin_directory = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "plugins") + self.plugin_as_dir_directory = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "pluginsasdirs") + self.plugin_info_file = "simpleplugin.yapsy-plugin" + self.plugin_name = "SimplePlugin" + self.plugin_impl_file = self.plugin_name + ".py" + + def test_default_plugins_place_is_parent_dir(self): + """Test a non-trivial default behaviour introduced some time ago :S""" + pl = PluginFileLocator() + self.assertTrue("package/yapsy" in pl.plugins_places[0]) + + def test_locatePlugins(self): + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_directory]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 1) + self.assertEqual(len(candidates), num) + self.assertEqual( + os.path.join( + self.plugin_directory, + self.plugin_info_file), + candidates[0][0]) + self.assertEqual(os.path.join(self.plugin_directory, self.plugin_name), + candidates[0][1]) + self.assertTrue(isinstance(candidates[0][2], PluginInfo)) + + def test_locatePlugins_when_plugin_is_symlinked(self): + if "win" in sys.platform: + return + temp_dir = tempfile.mkdtemp() + try: + plugin_info_file = "simpleplugin.yapsy-plugin" + plugin_impl_file = "SimplePlugin.py" + os.symlink(os.path.join(self.plugin_directory, plugin_info_file), + os.path.join(temp_dir, plugin_info_file)) + os.symlink(os.path.join(self.plugin_directory, plugin_impl_file), + os.path.join(temp_dir, plugin_impl_file)) + pl = PluginFileLocator() + pl.setPluginPlaces([temp_dir]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 1) + self.assertEqual(len(candidates), num) + self.assertEqual(os.path.join(temp_dir, self.plugin_info_file), + candidates[0][0]) + self.assertEqual(os.path.join(temp_dir, self.plugin_name), + candidates[0][1]) + self.assertTrue(isinstance(candidates[0][2], PluginInfo)) + finally: + shutil.rmtree(temp_dir) + + def test_locatePlugins_when_plugin_is_a_directory(self): + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_as_dir_directory]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 1) + self.assertEqual(len(candidates), num) + self.assertEqual( + os.path.join( + self.plugin_as_dir_directory, + self.plugin_info_file), + candidates[0][0]) + self.assertEqual( + os.path.join( + self.plugin_as_dir_directory, + self.plugin_name, + "__init__"), + candidates[0][1]) + self.assertTrue(isinstance(candidates[0][2], PluginInfo)) + + def test_locatePlugins_when_plugin_is_a_symlinked_directory(self): + if "win" in sys.platform: + return + temp_dir = tempfile.mkdtemp() + try: + plugin_info_file = "simpleplugin.yapsy-plugin" + plugin_impl_dir = "SimplePlugin" + os.symlink( + os.path.join( + self.plugin_as_dir_directory, + plugin_info_file), + os.path.join( + temp_dir, + plugin_info_file)) + os.symlink( + os.path.join( + self.plugin_as_dir_directory, + plugin_impl_dir), + os.path.join( + temp_dir, + plugin_impl_dir)) + pl = PluginFileLocator() + pl.setPluginPlaces([temp_dir]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 1) + self.assertEqual(len(candidates), num) + self.assertEqual(os.path.join(temp_dir, self.plugin_info_file), + candidates[0][0]) + self.assertEqual( + os.path.join( + temp_dir, + self.plugin_name, + "__init__"), + candidates[0][1]) + self.assertTrue(isinstance(candidates[0][2], PluginInfo)) + finally: + shutil.rmtree(temp_dir) + + def test_locatePlugins_recursively_when_plugin_is_a_directory(self): + temp_dir = tempfile.mkdtemp() + try: + temp_sub_dir = os.path.join(temp_dir, "plugins") + shutil.copytree(self.plugin_as_dir_directory, temp_sub_dir) + pl = PluginFileLocator() + pl.setPluginPlaces([temp_dir]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 1) + self.assertEqual(len(candidates), num) + self.assertEqual(os.path.join(temp_sub_dir, self.plugin_info_file), + candidates[0][0]) + self.assertEqual(os.path.join(temp_sub_dir, self.plugin_name, + "__init__"), + candidates[0][1]) + self.assertTrue(isinstance(candidates[0][2], PluginInfo)) + finally: + shutil.rmtree(temp_dir) + + def test_locatePlugins_recursively_fails_when_recursion_is_disabled(self): + temp_dir = tempfile.mkdtemp() + try: + temp_sub_dir = os.path.join(temp_dir, "plugins") + shutil.copytree(self.plugin_as_dir_directory, temp_sub_dir) + pl = PluginFileLocator() + pl.disableRecursiveScan() + pl.setPluginPlaces([temp_dir]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 0) + self.assertEqual(len(candidates), num) + finally: + shutil.rmtree(temp_dir) + + def test_locatePlugins_recursively_when_plugin_is_a_symlinked_directory( + self): + temp_dir = tempfile.mkdtemp() + try: + temp_sub_dir = os.path.join(temp_dir, "plugins") + os.mkdir(temp_sub_dir) + plugin_info_file = "simpleplugin.yapsy-plugin" + plugin_impl_dir = "SimplePlugin" + os.symlink( + os.path.join( + self.plugin_as_dir_directory, + plugin_info_file), + os.path.join( + temp_sub_dir, + plugin_info_file)) + os.symlink( + os.path.join( + self.plugin_as_dir_directory, + plugin_impl_dir), + os.path.join( + temp_sub_dir, + plugin_impl_dir)) + pl = PluginFileLocator() + pl.setPluginPlaces([temp_dir]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 1) + self.assertEqual(len(candidates), num) + self.assertEqual(os.path.join(temp_sub_dir, self.plugin_info_file), + candidates[0][0]) + self.assertEqual(os.path.join(temp_sub_dir, self.plugin_name, + "__init__"), + candidates[0][1]) + self.assertTrue(isinstance(candidates[0][2], PluginInfo)) + finally: + shutil.rmtree(temp_dir) + + def test_locatePlugins_recursively_when_plugin_parent_dir_is_a_symlinked_directory( + self): + # This actually reproduced the "Plugin detection doesn't follow symlinks" bug + # at http://sourceforge.net/p/yapsy/bugs/19/ + temp_dir = tempfile.mkdtemp() + try: + temp_sub_dir = os.path.join(temp_dir, "plugins") + os.symlink(self.plugin_as_dir_directory, temp_sub_dir) + pl = PluginFileLocator() + pl.setPluginPlaces([temp_dir]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 1) + self.assertEqual(len(candidates), num) + self.assertEqual(os.path.join(temp_sub_dir, self.plugin_info_file), + candidates[0][0]) + self.assertEqual(os.path.join(temp_sub_dir, self.plugin_name, + "__init__"), + candidates[0][1]) + self.assertTrue(isinstance(candidates[0][2], PluginInfo)) + finally: + shutil.rmtree(temp_dir) + + def test_gatherCorePluginInfo(self): + pl = PluginFileLocator() + plugin_info, cf_parser = pl.gatherCorePluginInfo( + self.plugin_directory, "simpleplugin.yapsy-plugin") + self.assertTrue(plugin_info.name, "Simple Plugin") + self.assertTrue(isinstance(cf_parser, ConfigParser)) + plugin_info, cf_parser = pl.gatherCorePluginInfo( + self.plugin_directory, "notaplugin.atall") + self.assertEqual(plugin_info, None) + self.assertEqual(cf_parser, None) + + def test_setAnalyzer(self): + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_directory]) + newAnalyzer = PluginFileAnalyzerMathingRegex( + "mouf", + r".*VersionedPlugin\d+\.py$") + pl.setAnalyzers([newAnalyzer]) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 4) + self.assertEqual(len(candidates), num) + + def test_appendAnalyzer(self): + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_directory]) + newAnalyzer = PluginFileAnalyzerMathingRegex( + "mouf", + r".*VersionedPlugin\d+\.py$") + pl.appendAnalyzer(newAnalyzer) + candidates, num = pl.locatePlugins() + self.assertEqual(num, 5) + self.assertEqual(len(candidates), num) + + def test_removeAnalyzers_when_analyzer_is_unknown(self): + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_directory]) + pl.removeAnalyzers("nogo") + + def test_removeAnalyzers(self): + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_directory]) + newAnalyzer = PluginFileAnalyzerMathingRegex( + "mouf", + r".*VersionedPlugin\d+\.py$") + pl.appendAnalyzer(newAnalyzer) + pl.removeAnalyzers("info_ext") + candidates, num = pl.locatePlugins() + self.assertEqual(num, 4) + self.assertEqual(len(candidates), num) + + def test_removeAllAnalyzers(self): + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_directory]) + pl.removeAllAnalyzer() + candidates, num = pl.locatePlugins() + self.assertEqual(num, 0) + self.assertEqual(len(candidates), num) + + def test_setPluginInfoClass_for_named_analyzer(self): + class SpecificPluginInfo(PluginInfo): + pass + pl = PluginFileLocator() + pl.setPluginPlaces([self.plugin_directory]) + newAnalyzer = PluginFileAnalyzerMathingRegex( + "mouf", + r".*VersionedPlugin\d+\.py$") + pl.appendAnalyzer(newAnalyzer) + pl.setPluginInfoClass(SpecificPluginInfo, "info_ext") + candidates, num = pl.locatePlugins() + self.assertEqual(num, 5) + self.assertEqual(len(candidates), num) + versioned_plugins = [ + c for c in candidates if "VersionedPlugin" in c[0]] + self.assertEqual(4, len(versioned_plugins)) + for p in versioned_plugins: + self.assertTrue(isinstance(p[2], PluginInfo)) + simple_plugins = [ + c for c in candidates if "VersionedPlugin" not in c[0]] + self.assertEqual(1, len(simple_plugins)) + for p in simple_plugins: + self.assertTrue(isinstance(p[2], SpecificPluginInfo)) + class PluginManagerSetUpTest(unittest.TestCase): - def test_default_init(self): - pm = PluginManager() - self.assertEqual(["Default"],pm.getCategories()) - self.assertTrue(isinstance(pm.getPluginLocator(),PluginFileLocator)) - - def test_init_with_category_filter(self): - pm = PluginManager(categories_filter={"Mouf": IPlugin}) - self.assertEqual(["Mouf"],pm.getCategories()) - self.assertTrue(isinstance(pm.getPluginLocator(),PluginFileLocator)) - - def test_init_with_plugin_info_ext(self): - pm = PluginManager(plugin_info_ext="bla") - self.assertEqual(["Default"],pm.getCategories()) - self.assertTrue(isinstance(pm.getPluginLocator(),PluginFileLocator)) - - def test_init_with_plugin_locator(self): - class SpecificLocator(IPluginLocator): - pass - pm = PluginManager(plugin_locator=SpecificLocator()) - self.assertEqual(["Default"],pm.getCategories()) - self.assertTrue(isinstance(pm.getPluginLocator(),SpecificLocator)) - - def test_init_with_plugin_info_ext_and_locator(self): - class SpecificLocator(IPluginLocator): - pass - self.assertRaises(ValueError, - PluginManager,plugin_info_ext="bla", - plugin_locator=SpecificLocator()) - - def test_updatePluginPlaces(self): - class SpecificLocator(IPluginLocator): - pass - pm = PluginManager() - pm.setPluginPlaces(["bla/bli"]) - pm.updatePluginPlaces(["mif/maf"]) - self.assertEqual(set(["bla/bli","mif/maf"]),set(pm.getPluginLocator().plugins_places)) - - def test_getPluginCandidates_too_early(self): - pm = PluginManager() - self.assertRaises(RuntimeError,pm.getPluginCandidates) - - def test_setPluginLocator_with_plugin_info_class(self): - class SpecificLocator(IPluginLocator): - - def getPluginInfoClass(self): - return self.picls - - def setPluginInfoClass(self,picls): - self.picls = picls - - class SpecificPluginInfo(PluginInfo): - pass - pm = PluginManager() - pm.setPluginLocator(SpecificLocator(),picls=SpecificPluginInfo) - self.assertEqual(SpecificPluginInfo,pm.getPluginInfoClass()) - - def test_setPluginLocator_with_invalid_locator(self): - class SpecificLocator: - pass - pm = PluginManager() - self.assertRaises(TypeError, - pm.setPluginLocator,SpecificLocator()) - - def test_setPluginInfoClass_with_strategies(self): - class SpecificPluginInfo(PluginInfo): - pass - class SpecificLocator(IPluginLocator): - def setPluginInfoClass(self,cls,name): - if not hasattr(self,"icls"): - self.icls = {} - self.icls[name] = cls - loc = SpecificLocator() - pm = PluginManager(plugin_locator=loc) - pm.setPluginInfoClass(SpecificPluginInfo,["mouf","hop"]) - self.assertEqual({"mouf":SpecificPluginInfo,"hop":SpecificPluginInfo},loc.icls) + def test_default_init(self): + pm = PluginManager() + self.assertEqual(["Default"], pm.getCategories()) + self.assertTrue(isinstance(pm.getPluginLocator(), PluginFileLocator)) + + def test_init_with_category_filter(self): + pm = PluginManager(categories_filter={"Mouf": IPlugin}) + self.assertEqual(["Mouf"], pm.getCategories()) + self.assertTrue(isinstance(pm.getPluginLocator(), PluginFileLocator)) + + def test_init_with_plugin_info_ext(self): + pm = PluginManager(plugin_info_ext="bla") + self.assertEqual(["Default"], pm.getCategories()) + self.assertTrue(isinstance(pm.getPluginLocator(), PluginFileLocator)) + + def test_init_with_plugin_locator(self): + class SpecificLocator(IPluginLocator): + pass + pm = PluginManager(plugin_locator=SpecificLocator()) + self.assertEqual(["Default"], pm.getCategories()) + self.assertTrue(isinstance(pm.getPluginLocator(), SpecificLocator)) + + def test_init_with_plugin_info_ext_and_locator(self): + class SpecificLocator(IPluginLocator): + pass + self.assertRaises(ValueError, + PluginManager, plugin_info_ext="bla", + plugin_locator=SpecificLocator()) + + def test_updatePluginPlaces(self): + class SpecificLocator(IPluginLocator): + pass + pm = PluginManager() + pm.setPluginPlaces(["bla/bli"]) + pm.updatePluginPlaces(["mif/maf"]) + self.assertEqual( + set(["bla/bli", "mif/maf"]), set(pm.getPluginLocator().plugins_places)) + + def test_getPluginCandidates_too_early(self): + pm = PluginManager() + self.assertRaises(RuntimeError, pm.getPluginCandidates) + + def test_setPluginLocator_with_plugin_info_class(self): + class SpecificLocator(IPluginLocator): + + def getPluginInfoClass(self): + return self.picls + + def setPluginInfoClass(self, picls): + self.picls = picls + + class SpecificPluginInfo(PluginInfo): + pass + pm = PluginManager() + pm.setPluginLocator(SpecificLocator(), picls=SpecificPluginInfo) + self.assertEqual(SpecificPluginInfo, pm.getPluginInfoClass()) + + def test_setPluginLocator_with_invalid_locator(self): + class SpecificLocator: + pass + pm = PluginManager() + self.assertRaises(TypeError, + pm.setPluginLocator, SpecificLocator()) + + def test_setPluginInfoClass_with_strategies(self): + class SpecificPluginInfo(PluginInfo): + pass + + class SpecificLocator(IPluginLocator): + + def setPluginInfoClass(self, cls, name): + if not hasattr(self, "icls"): + self.icls = {} + self.icls[name] = cls + loc = SpecificLocator() + pm = PluginManager(plugin_locator=loc) + pm.setPluginInfoClass(SpecificPluginInfo, ["mouf", "hop"]) + self.assertEqual( + {"mouf": SpecificPluginInfo, "hop": SpecificPluginInfo}, loc.icls) suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(IPluginLocatorTest), - unittest.TestLoader().loadTestsFromTestCase(PluginFileAnalyzerWithInfoFileTest), - unittest.TestLoader().loadTestsFromTestCase(PluginFileAnalyzerMathingRegexTest), - unittest.TestLoader().loadTestsFromTestCase(PluginFileLocatorTest), - unittest.TestLoader().loadTestsFromTestCase(PluginManagerSetUpTest), - ]) + unittest.TestLoader().loadTestsFromTestCase(IPluginLocatorTest), + unittest.TestLoader().loadTestsFromTestCase(PluginFileAnalyzerWithInfoFileTest), + unittest.TestLoader().loadTestsFromTestCase(PluginFileAnalyzerMathingRegexTest), + unittest.TestLoader().loadTestsFromTestCase(PluginFileLocatorTest), + unittest.TestLoader().loadTestsFromTestCase(PluginManagerSetUpTest), +]) diff --git a/package/test/test_PluginInfo.py b/package/test/test_PluginInfo.py index bd6ca6d..8a1ec7f 100644 --- a/package/test/test_PluginInfo.py +++ b/package/test/test_PluginInfo.py @@ -1,8 +1,8 @@ #!/usr/bin/python # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t; python-indent: 4 -*- -import test_settings -from configparser import ConfigParser +from . import test_settings +from yapsy.compat import ConfigParser import unittest @@ -10,44 +10,45 @@ class PluginInfoTest(unittest.TestCase): - """ - Test basic manipulations of PluginInfo. - """ - - def testDefaultValuesAndAccessors(self): - pi = PluginInfo("mouf","/bla/mouf") - self.assertEqual("mouf",pi.name) - self.assertEqual("/bla/mouf",pi.path) - self.assertEqual(None,pi.plugin_object) - self.assertEqual([],pi.categories) - self.assertEqual(None,pi.error) - self.assertEqual("0.0",pi.version) - self.assertEqual("Unknown",pi.author) - self.assertEqual("Unknown",pi.copyright) - self.assertEqual("None",pi.website) - self.assertEqual("",pi.description) - self.assertEqual("UnknownCategory",pi.category) - - def testDetailsAccessors(self): - pi = PluginInfo("mouf","/bla/mouf") - details = ConfigParser() - details.add_section("Core") - details.set("Core","Name","hop") - details.set("Core","Module","/greuh") - details.add_section("Documentation") - details.set("Documentation","Author","me") - pi.details = details - # Beware this is not so obvious: the plugin info still points - # (and possibly modifies) the same instance of ConfigParser - self.assertEqual(details,pi.details) - # also the name and path are kept to their original value when - # the details is set in one go. - self.assertEqual("mouf",pi.name) - self.assertEqual("/bla/mouf",pi.path) - # check that some other info do change... - self.assertEqual("me",pi.author) - - + + """ + Test basic manipulations of PluginInfo. + """ + + def testDefaultValuesAndAccessors(self): + pi = PluginInfo("mouf", "/bla/mouf") + self.assertEqual("mouf", pi.name) + self.assertEqual("/bla/mouf", pi.path) + self.assertEqual(None, pi.plugin_object) + self.assertEqual([], pi.categories) + self.assertEqual(None, pi.error) + self.assertEqual("0.0", pi.version) + self.assertEqual("Unknown", pi.author) + self.assertEqual("Unknown", pi.copyright) + self.assertEqual("None", pi.website) + self.assertEqual("", pi.description) + self.assertEqual("UnknownCategory", pi.category) + + def testDetailsAccessors(self): + pi = PluginInfo("mouf", "/bla/mouf") + details = ConfigParser() + details.add_section("Core") + details.set("Core", "Name", "hop") + details.set("Core", "Module", "/greuh") + details.add_section("Documentation") + details.set("Documentation", "Author", "me") + pi.details = details + # Beware this is not so obvious: the plugin info still points + # (and possibly modifies) the same instance of ConfigParser + self.assertEqual(details, pi.details) + # also the name and path are kept to their original value when + # the details is set in one go. + self.assertEqual("mouf", pi.name) + self.assertEqual("/bla/mouf", pi.path) + # check that some other info do change... + self.assertEqual("me", pi.author) + + suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(PluginInfoTest), - ]) + unittest.TestLoader().loadTestsFromTestCase(PluginInfoTest), +]) diff --git a/package/test/test_SimplePlugin.py b/package/test/test_SimplePlugin.py index 34bc2d1..2304503 100644 --- a/package/test/test_SimplePlugin.py +++ b/package/test/test_SimplePlugin.py @@ -3,294 +3,320 @@ from . import test_settings import unittest -import os +import os from yapsy.PluginManager import PluginManager from yapsy.IPlugin import IPlugin from yapsy.PluginFileLocator import PluginFileLocator from yapsy import NormalizePluginNameForModuleName + class YapsyUtils(unittest.TestCase): - def test_NormalizePluginNameForModuleName_on_ok_name(self): - self.assertEqual("moufGlop2",NormalizePluginNameForModuleName("moufGlop2")) + def test_NormalizePluginNameForModuleName_on_ok_name(self): + self.assertEqual( + "moufGlop2", + NormalizePluginNameForModuleName("moufGlop2")) + + def test_NormalizePluginNameForModuleName_on_empty_name(self): + self.assertEqual("_", NormalizePluginNameForModuleName("")) - def test_NormalizePluginNameForModuleName_on_empty_name(self): - self.assertEqual("_",NormalizePluginNameForModuleName("")) - - def test_NormalizePluginNameForModuleName_on_name_with_space(self): - self.assertEqual("mouf_glop",NormalizePluginNameForModuleName("mouf glop")) + def test_NormalizePluginNameForModuleName_on_name_with_space(self): + self.assertEqual( + "mouf_glop", + NormalizePluginNameForModuleName("mouf glop")) - def test_NormalizePluginNameForModuleName_on_name_with_nonalphanum(self): - self.assertEqual("mouf__glop_a_é",NormalizePluginNameForModuleName("mouf+?glop:a/é")) + def test_NormalizePluginNameForModuleName_on_name_with_nonalphanum(self): + self.assertEqual( + "mouf__glop_a_é", + NormalizePluginNameForModuleName("mouf+?glop:a/é")) - class SimpleTestCase(unittest.TestCase): - """ - Test the correct loading of a simple plugin as well as basic - commands. - """ - - def setUp(self): - """ - init - """ - # create the plugin manager - self.simplePluginManager = PluginManager(directories_list=[ - os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")]) - # load the plugins that may be found - self.simplePluginManager.collectPlugins() - # Will be used later - self.plugin_info = None - - def plugin_loading_check(self): - """ - Test if the correct plugin has been loaded. - """ - if self.plugin_info is None: - # check nb of categories - self.assertEqual(len(self.simplePluginManager.getCategories()),1) - sole_category = self.simplePluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(self.simplePluginManager.getPluginsOfCategory(sole_category)),1) - self.plugin_info = self.simplePluginManager.getPluginsOfCategory(sole_category)[0] - # test that the name of the plugin has been correctly defined - self.assertEqual(self.plugin_info.name,"Simple Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - else: - self.assertTrue(True) - - def testLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - - def testGetAll(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - self.assertEqual(len(self.simplePluginManager.getAllPlugins()),1) - self.assertEqual(self.simplePluginManager.getAllPlugins()[0],self.plugin_info) - - - def testActivationAndDeactivation(self): - """ - Test if the activation procedure works. - """ - self.plugin_loading_check() - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - self.simplePluginManager.activatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(self.plugin_info.plugin_object.is_activated) - self.simplePluginManager.deactivatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(not self.plugin_info.plugin_object.is_activated) + + """ + Test the correct loading of a simple plugin as well as basic + commands. + """ + + def setUp(self): + """ + init + """ + # create the plugin manager + self.simplePluginManager = PluginManager(directories_list=[ + os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")]) + # load the plugins that may be found + self.simplePluginManager.collectPlugins() + # Will be used later + self.plugin_info = None + + def plugin_loading_check(self): + """ + Test if the correct plugin has been loaded. + """ + if self.plugin_info is None: + # check nb of categories + self.assertEqual(len(self.simplePluginManager.getCategories()), 1) + sole_category = self.simplePluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(self.simplePluginManager.getPluginsOfCategory(sole_category)), 1) + self.plugin_info = self.simplePluginManager.getPluginsOfCategory( + sole_category)[0] + # test that the name of the plugin has been correctly defined + self.assertEqual(self.plugin_info.name, "Simple Plugin") + self.assertEqual(sole_category, self.plugin_info.category) + else: + self.assertTrue(True) + + def testLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + + def testGetAll(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + self.assertEqual(len(self.simplePluginManager.getAllPlugins()), 1) + self.assertEqual( + self.simplePluginManager.getAllPlugins()[0], + self.plugin_info) + + def testActivationAndDeactivation(self): + """ + Test if the activation procedure works. + """ + self.plugin_loading_check() + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + self.simplePluginManager.activatePluginByName( + self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(self.plugin_info.plugin_object.is_activated) + self.simplePluginManager.deactivatePluginByName( + self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(not self.plugin_info.plugin_object.is_activated) class SimplePluginAdvancedManipulationTestsCase(unittest.TestCase): - """ - Test some advanced manipulation on the core data of a PluginManager. - """ - - - def testCategoryManipulation(self): - """ - Test querying, removing and adding plugins from/to a category. - """ - spm = PluginManager(directories_list=[ - os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")]) - # load the plugins that may be found - spm.collectPlugins() - # check that the getCategories works - self.assertEqual(len(spm.getCategories()),1) - sole_category = spm.getCategories()[0] - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) - plugin_info = spm.getPluginsOfCategory(sole_category)[0] - # try to remove it and check that is worked - spm.removePluginFromCategory(plugin_info,sole_category) - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),0) - # now re-add this plugin the to same category - spm.appendPluginToCategory(plugin_info,sole_category) - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) - - def testCandidatesManipulation(self): - """ - Test querying, removing and adding plugins from/to the lkist - of plugins to load. - """ - spm = PluginManager(directories_list=[ - os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")]) - # locate the plugins that should be loaded - spm.locatePlugins() - # check nb of candidatesx - self.assertEqual(len(spm.getPluginCandidates()),1) - # get the description of the plugin candidate - candidate = spm.getPluginCandidates()[0] - self.assertTrue(isinstance(candidate,tuple)) - # try removing the candidate - spm.removePluginCandidate(candidate) - self.assertEqual(len(spm.getPluginCandidates()),0) - # try re-adding it - spm.appendPluginCandidate(candidate) - self.assertEqual(len(spm.getPluginCandidates()),1) - - def testTwoStepsLoad(self): - """ - Test loading the plugins in two steps in order to collect more - deltailed informations. - """ - spm = PluginManager(directories_list=[ - os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")]) - # trigger the first step to look up for plugins - spm.locatePlugins() - # make full use of the "feedback" the loadPlugins can give - # - set-up the callback function that will be called *before* - # loading each plugin - callback_infos = [] - def preload_cbk(plugin_info): - callback_infos.append(plugin_info) - # - gather infos about the processed plugins (loaded or not) - loadedPlugins = spm.loadPlugins(callback=preload_cbk) - self.assertEqual(len(loadedPlugins),1) - self.assertEqual(len(callback_infos),1) - self.assertEqual(loadedPlugins[0].error,None) - self.assertEqual(loadedPlugins[0],callback_infos[0]) - # check that the getCategories works - self.assertEqual(len(spm.getCategories()),1) - sole_category = spm.getCategories()[0] - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) - plugin_info = spm.getPluginsOfCategory(sole_category)[0] - # try to remove it and check that is worked - spm.removePluginFromCategory(plugin_info,sole_category) - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),0) - # now re-add this plugin the to same category - spm.appendPluginToCategory(plugin_info,sole_category) - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) - - def testMultipleCategoriesForASamePlugin(self): - """ - Test that associating a plugin to multiple categories works as expected. - """ - class AnotherPluginIfce(object): - def __init__(self): - pass - def activate(self): - pass - def deactivate(self): - pass - - spm = PluginManager( - categories_filter = { - "Default": IPlugin, - "IP": IPlugin, - "Other": AnotherPluginIfce, - }, - directories_list=[ - os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")]) - # load the plugins that may be found - spm.collectPlugins() - # check that the getCategories works - self.assertEqual(len(spm.getCategories()),3) - categories = spm.getCategories() - self.assertTrue("Default" in categories) - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory("Default")), 1) - plugin_info = spm.getPluginsOfCategory("Default")[0] - self.assertTrue("Default" in plugin_info.categories) - self.assertTrue("IP" in plugin_info.categories) - self.assertTrue("IP" in categories) - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory("IP")),1) - self.assertTrue("Other" in categories) - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory("Other")),0) - # try to remove the plugin from one category and check the - # other category - spm.removePluginFromCategory(plugin_info, "Default") - self.assertEqual(len(spm.getPluginsOfCategory("Default")), 0) - self.assertEqual(len(spm.getPluginsOfCategory("IP")), 1) - # now re-add this plugin the to same category - spm.appendPluginToCategory(plugin_info, "Default") - self.assertEqual(len(spm.getPluginsOfCategory("Default")),1) - self.assertEqual(len(spm.getPluginsOfCategory("IP")),1) - + + """ + Test some advanced manipulation on the core data of a PluginManager. + """ + + def testCategoryManipulation(self): + """ + Test querying, removing and adding plugins from/to a category. + """ + spm = PluginManager( + directories_list=[ + os.path.join( + os.path.dirname( + os.path.abspath(__file__)), + "plugins")]) + # load the plugins that may be found + spm.collectPlugins() + # check that the getCategories works + self.assertEqual(len(spm.getCategories()), 1) + sole_category = spm.getCategories()[0] + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 1) + plugin_info = spm.getPluginsOfCategory(sole_category)[0] + # try to remove it and check that is worked + spm.removePluginFromCategory(plugin_info, sole_category) + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 0) + # now re-add this plugin the to same category + spm.appendPluginToCategory(plugin_info, sole_category) + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 1) + + def testCandidatesManipulation(self): + """ + Test querying, removing and adding plugins from/to the lkist + of plugins to load. + """ + spm = PluginManager( + directories_list=[ + os.path.join( + os.path.dirname( + os.path.abspath(__file__)), + "plugins")]) + # locate the plugins that should be loaded + spm.locatePlugins() + # check nb of candidatesx + self.assertEqual(len(spm.getPluginCandidates()), 1) + # get the description of the plugin candidate + candidate = spm.getPluginCandidates()[0] + self.assertTrue(isinstance(candidate, tuple)) + # try removing the candidate + spm.removePluginCandidate(candidate) + self.assertEqual(len(spm.getPluginCandidates()), 0) + # try re-adding it + spm.appendPluginCandidate(candidate) + self.assertEqual(len(spm.getPluginCandidates()), 1) + + def testTwoStepsLoad(self): + """ + Test loading the plugins in two steps in order to collect more + deltailed informations. + """ + spm = PluginManager( + directories_list=[ + os.path.join( + os.path.dirname( + os.path.abspath(__file__)), + "plugins")]) + # trigger the first step to look up for plugins + spm.locatePlugins() + # make full use of the "feedback" the loadPlugins can give + # - set-up the callback function that will be called *before* + # loading each plugin + callback_infos = [] + + def preload_cbk(plugin_info): + callback_infos.append(plugin_info) + # - gather infos about the processed plugins (loaded or not) + loadedPlugins = spm.loadPlugins(callback=preload_cbk) + self.assertEqual(len(loadedPlugins), 1) + self.assertEqual(len(callback_infos), 1) + self.assertEqual(loadedPlugins[0].error, None) + self.assertEqual(loadedPlugins[0], callback_infos[0]) + # check that the getCategories works + self.assertEqual(len(spm.getCategories()), 1) + sole_category = spm.getCategories()[0] + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 1) + plugin_info = spm.getPluginsOfCategory(sole_category)[0] + # try to remove it and check that is worked + spm.removePluginFromCategory(plugin_info, sole_category) + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 0) + # now re-add this plugin the to same category + spm.appendPluginToCategory(plugin_info, sole_category) + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 1) + + def testMultipleCategoriesForASamePlugin(self): + """ + Test that associating a plugin to multiple categories works as expected. + """ + class AnotherPluginIfce(object): + + def __init__(self): + pass + + def activate(self): + pass + + def deactivate(self): + pass + + spm = PluginManager( + categories_filter={ + "Default": IPlugin, + "IP": IPlugin, + "Other": AnotherPluginIfce, + }, + directories_list=[ + os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")]) + # load the plugins that may be found + spm.collectPlugins() + # check that the getCategories works + self.assertEqual(len(spm.getCategories()), 3) + categories = spm.getCategories() + self.assertTrue("Default" in categories) + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory("Default")), 1) + plugin_info = spm.getPluginsOfCategory("Default")[0] + self.assertTrue("Default" in plugin_info.categories) + self.assertTrue("IP" in plugin_info.categories) + self.assertTrue("IP" in categories) + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory("IP")), 1) + self.assertTrue("Other" in categories) + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory("Other")), 0) + # try to remove the plugin from one category and check the + # other category + spm.removePluginFromCategory(plugin_info, "Default") + self.assertEqual(len(spm.getPluginsOfCategory("Default")), 0) + self.assertEqual(len(spm.getPluginsOfCategory("IP")), 1) + # now re-add this plugin the to same category + spm.appendPluginToCategory(plugin_info, "Default") + self.assertEqual(len(spm.getPluginsOfCategory("Default")), 1) + self.assertEqual(len(spm.getPluginsOfCategory("IP")), 1) + + class SimplePluginDetectionTestsCase(unittest.TestCase): - """ - Test particular aspects of plugin detection - """ - - def testRecursivePluginlocation(self): - """ - Test detection of plugins which by default must be - recusrive. Here we give the test directory as a plugin place - whereas we expect the plugins to be in test/plugins. - """ - spm = PluginManager(directories_list=[ - os.path.dirname(os.path.abspath(__file__))]) - # load the plugins that may be found - spm.collectPlugins() - # check that the getCategories works - self.assertEqual(len(spm.getCategories()),1) - sole_category = spm.getCategories()[0] - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),2) - - def testDisablingRecursivePluginLocationIsEnforced(self): - """ - Test detection of plugins when the detection is non recursive. - Here we test that it cannot look into subdirectories of the - test directory. - """ - pluginLocator = PluginFileLocator() - pluginLocator.setPluginPlaces([ - os.path.dirname(os.path.abspath(__file__))]) - pluginLocator.disableRecursiveScan() - spm = PluginManager() - spm.setPluginLocator(pluginLocator) - # load the plugins that may be found - spm.collectPlugins() - # check that the getCategories works - self.assertEqual(len(spm.getCategories()),1) - sole_category = spm.getCategories()[0] - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),0) - - - def testDisablingRecursivePluginLocationAllowsFindingTopLevelPlugins(self): - """ - Test detection of plugins when the detection is non - recursive. Here we test that if we give test/plugin as the - directory to scan it can find the plugin. - """ - pluginLocator = PluginFileLocator() - pluginLocator.setPluginPlaces([ - os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")]) - pluginLocator.disableRecursiveScan() - spm = PluginManager() - spm.setPluginLocator(pluginLocator) - # load the plugins that may be found - spm.collectPlugins() - # check that the getCategories works - self.assertEqual(len(spm.getCategories()),1) - sole_category = spm.getCategories()[0] - # check the getPluginsOfCategory - self.assertEqual(len(spm.getPluginsOfCategory(sole_category)),1) - - + + """ + Test particular aspects of plugin detection + """ + + def testRecursivePluginlocation(self): + """ + Test detection of plugins which by default must be + recusrive. Here we give the test directory as a plugin place + whereas we expect the plugins to be in test/plugins. + """ + spm = PluginManager(directories_list=[ + os.path.dirname(os.path.abspath(__file__))]) + # load the plugins that may be found + spm.collectPlugins() + # check that the getCategories works + self.assertEqual(len(spm.getCategories()), 1) + sole_category = spm.getCategories()[0] + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 2) + + def testDisablingRecursivePluginLocationIsEnforced(self): + """ + Test detection of plugins when the detection is non recursive. + Here we test that it cannot look into subdirectories of the + test directory. + """ + pluginLocator = PluginFileLocator() + pluginLocator.setPluginPlaces([ + os.path.dirname(os.path.abspath(__file__))]) + pluginLocator.disableRecursiveScan() + spm = PluginManager() + spm.setPluginLocator(pluginLocator) + # load the plugins that may be found + spm.collectPlugins() + # check that the getCategories works + self.assertEqual(len(spm.getCategories()), 1) + sole_category = spm.getCategories()[0] + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 0) + + def testDisablingRecursivePluginLocationAllowsFindingTopLevelPlugins(self): + """ + Test detection of plugins when the detection is non + recursive. Here we test that if we give test/plugin as the + directory to scan it can find the plugin. + """ + pluginLocator = PluginFileLocator() + pluginLocator.setPluginPlaces([ + os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")]) + pluginLocator.disableRecursiveScan() + spm = PluginManager() + spm.setPluginLocator(pluginLocator) + # load the plugins that may be found + spm.collectPlugins() + # check that the getCategories works + self.assertEqual(len(spm.getCategories()), 1) + sole_category = spm.getCategories()[0] + # check the getPluginsOfCategory + self.assertEqual(len(spm.getPluginsOfCategory(sole_category)), 1) + + suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(YapsyUtils), - unittest.TestLoader().loadTestsFromTestCase(SimpleTestCase), - unittest.TestLoader().loadTestsFromTestCase(SimplePluginAdvancedManipulationTestsCase), - unittest.TestLoader().loadTestsFromTestCase(SimplePluginDetectionTestsCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(YapsyUtils), + unittest.TestLoader().loadTestsFromTestCase(SimpleTestCase), + unittest.TestLoader().loadTestsFromTestCase(SimplePluginAdvancedManipulationTestsCase), + unittest.TestLoader().loadTestsFromTestCase(SimplePluginDetectionTestsCase), +]) diff --git a/package/test/test_Singleton.py b/package/test/test_Singleton.py index b955640..22f776c 100644 --- a/package/test/test_Singleton.py +++ b/package/test/test_Singleton.py @@ -3,134 +3,141 @@ from . import test_settings -import os +import os import unittest -import configparser from yapsy.ConfigurablePluginManager import ConfigurablePluginManager from yapsy.VersionedPluginManager import VersionedPluginManager from yapsy.PluginManager import PluginManagerSingleton +from yapsy.compat import ConfigParser """ There can be only one series of tests for the singleton, guess why ... """ + class ConfigSingletonTestsCase(unittest.TestCase): - """ - Test the correct loading of a simple plugin as well as basic - commands, use the Singleton version of the ConfigurablePluginManager. - """ - - CONFIG_FILE = test_settings.TEMP_CONFIG_FILE_NAME - - def setUp(self): - """ - init - """ - # create a config file - self.config_file = self.CONFIG_FILE - self.config_parser = configparser.ConfigParser() - self.plugin_info = None - - # create the plugin manager - PluginManagerSingleton.setBehaviour([ConfigurablePluginManager,VersionedPluginManager]) - pluginManager = PluginManagerSingleton.get() - pluginManager.setPluginPlaces(directories_list=[os.path.dirname(os.path.abspath(__file__))]) - pluginManager.setPluginInfoExtension("yapsy-config-plugin") - pluginManager.setConfigParser(self.config_parser,self.update_config) - # load the plugins that may be found - pluginManager.collectPlugins() - - def tearDown(self): - """ - When the test has been performed erase the temp file. - """ - if os.path.isfile(self.config_file): - os.remove(self.config_file) - - - def testConfigurationFileExistence(self): - """ - Test if the configuration file has been properly written. - """ - # activate the only loaded plugin - self.plugin_activate() - # get rid of the plugin manager and create a new one - self.config_parser.read(self.config_file) - self.assertTrue(self.config_parser.has_section("Plugin Management")) - self.assertTrue(self.config_parser.has_option("Plugin Management", - "default_plugins_to_load")) - - - def testLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - - def testActivationAndDeactivation(self): - """ - Test if the activation/deactivaion procedures work. - """ - self.plugin_activate() - PluginManagerSingleton.get().deactivatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - - def testPluginOptions(self): - """ - Test is the plugin can register and access options from the - ConfigParser. - """ - self.plugin_activate() - plugin = self.plugin_info.plugin_object - plugin.choseTestOption("voila") - self.assertTrue(plugin.checkTestOption()) - self.assertEqual(plugin.getTestOption(),"voila") - - - #--- UTILITIES - - def plugin_loading_check(self): - """ - Test if the correct plugin has been loaded. - """ - if self.plugin_info is None: - pluginManager = PluginManagerSingleton.get() - # check nb of categories - self.assertEqual(len(pluginManager.getCategories()),1) - sole_category = pluginManager.getCategories()[0] - # check the number of plugins - self.assertEqual(len(pluginManager.getPluginsOfCategory(sole_category)),1) - self.plugin_info = pluginManager.getPluginsOfCategory(sole_category)[0] - # test that the name of the plugin has been correctly defined - self.assertEqual(self.plugin_info.name,"Config Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - else: - self.assertTrue(True) - - def plugin_activate(self): - """ - Activate the plugin with basic checking - """ - self.plugin_loading_check() - if not self.plugin_info.plugin_object.is_activated: - PluginManagerSingleton.get().activatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(self.plugin_info.plugin_object.is_activated) - - - def update_config(self): - """ - Write the content of the ConfigParser in a file. - """ - cf = open(self.config_file,"a") - self.config_parser.write(cf) - cf.close() + """ + Test the correct loading of a simple plugin as well as basic + commands, use the Singleton version of the ConfigurablePluginManager. + """ + + CONFIG_FILE = test_settings.TEMP_CONFIG_FILE_NAME + + def setUp(self): + """ + init + """ + # create a config file + self.config_file = self.CONFIG_FILE + self.config_parser = ConfigParser() + self.plugin_info = None + + # create the plugin manager + PluginManagerSingleton.setBehaviour( + [ConfigurablePluginManager, VersionedPluginManager]) + pluginManager = PluginManagerSingleton.get() + pluginManager.setPluginPlaces( + directories_list=[ + os.path.dirname( + os.path.abspath(__file__))]) + pluginManager.setPluginInfoExtension("yapsy-config-plugin") + pluginManager.setConfigParser(self.config_parser, self.update_config) + # load the plugins that may be found + pluginManager.collectPlugins() + + def tearDown(self): + """ + When the test has been performed erase the temp file. + """ + if os.path.isfile(self.config_file): + os.remove(self.config_file) + + def testConfigurationFileExistence(self): + """ + Test if the configuration file has been properly written. + """ + # activate the only loaded plugin + self.plugin_activate() + # get rid of the plugin manager and create a new one + self.config_parser.read(self.config_file) + self.assertTrue(self.config_parser.has_section("Plugin Management")) + self.assertTrue( + self.config_parser.has_option( + "Plugin Management", + "default_plugins_to_load")) + + def testLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + + def testActivationAndDeactivation(self): + """ + Test if the activation/deactivaion procedures work. + """ + self.plugin_activate() + PluginManagerSingleton.get().deactivatePluginByName( + self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + + def testPluginOptions(self): + """ + Test is the plugin can register and access options from the + ConfigParser. + """ + self.plugin_activate() + plugin = self.plugin_info.plugin_object + plugin.choseTestOption("voila") + self.assertTrue(plugin.checkTestOption()) + self.assertEqual(plugin.getTestOption(), "voila") + + #--- UTILITIES + + def plugin_loading_check(self): + """ + Test if the correct plugin has been loaded. + """ + if self.plugin_info is None: + pluginManager = PluginManagerSingleton.get() + # check nb of categories + self.assertEqual(len(pluginManager.getCategories()), 1) + sole_category = pluginManager.getCategories()[0] + # check the number of plugins + self.assertEqual( + len(pluginManager.getPluginsOfCategory(sole_category)), 1) + self.plugin_info = pluginManager.getPluginsOfCategory( + sole_category)[0] + # test that the name of the plugin has been correctly defined + self.assertEqual(self.plugin_info.name, "Config Plugin") + self.assertEqual(sole_category, self.plugin_info.category) + else: + self.assertTrue(True) + + def plugin_activate(self): + """ + Activate the plugin with basic checking + """ + self.plugin_loading_check() + if not self.plugin_info.plugin_object.is_activated: + PluginManagerSingleton.get().activatePluginByName( + self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(self.plugin_info.plugin_object.is_activated) + + def update_config(self): + """ + Write the content of the ConfigParser in a file. + """ + cf = open(self.config_file, "a") + self.config_parser.write(cf) + cf.close() suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(ConfigSingletonTestsCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(ConfigSingletonTestsCase), +]) diff --git a/package/test/test_VersionedPlugin.py b/package/test/test_VersionedPlugin.py index 2cdbfc9..711722e 100644 --- a/package/test/test_VersionedPlugin.py +++ b/package/test/test_VersionedPlugin.py @@ -4,113 +4,117 @@ from . import test_settings from .test_settings import TEST_MESSAGE import unittest -import os +import os from yapsy.VersionedPluginManager import VersionedPluginManager class VersionedTestsCase(unittest.TestCase): - """ - Test the correct loading of a simple plugin as well as basic - commands. - """ - - def setUp(self): - """ - init - """ - # create the plugin manager - self.versionedPluginManager = VersionedPluginManager( - directories_list=[os.path.join( - os.path.dirname(os.path.abspath(__file__)),"plugins")], - plugin_info_ext="version-plugin", - ) - # load the plugins that may be found - self.versionedPluginManager.collectPlugins() - # Will be used later - self.plugin_info = None - def plugin_loading_check(self): - """ - Test if the correct plugin has been loaded. - """ - if self.plugin_info is None: - # check nb of categories - self.assertEqual(len(self.versionedPluginManager.getCategories()),1) - sole_category = self.versionedPluginManager.getCategories()[0] - # check the number of plugins (the older versions of the - # plugins should not be there) - self.assertEqual(len(self.versionedPluginManager.getPluginsOfCategory(sole_category)),1) - # older versions of the plugin should be found in the attic - self.assertEqual(len(self.versionedPluginManager.getPluginsOfCategoryFromAttic(sole_category)),4) - plugins = self.versionedPluginManager.getPluginsOfCategory(sole_category) - self.plugin_info = None - for plugin_info in plugins: - TEST_MESSAGE("plugin info: %s" % plugin_info) - if plugin_info.name == "Versioned Plugin": - self.plugin_info = plugin_info - break - self.assertTrue(self.plugin_info) - # test that the name of the plugin has been correctly defined - self.assertEqual(self.plugin_info.name,"Versioned Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - else: - self.assertTrue(True) + """ + Test the correct loading of a simple plugin as well as basic + commands. + """ - def testLoaded(self): - """ - Test if the correct plugin has been loaded. - """ - self.plugin_loading_check() - sole_category = self.versionedPluginManager.getCategories()[0] - self.assertEqual(len(self.versionedPluginManager.getLatestPluginsOfCategory(sole_category)),1) - self.plugin_info = self.versionedPluginManager.getLatestPluginsOfCategory(sole_category)[0] - TEST_MESSAGE("plugin info: %s" % self.plugin_info) - # test that the name of the plugin has been correctly defined - self.assertEqual(self.plugin_info.name,"Versioned Plugin") - self.assertEqual(sole_category,self.plugin_info.category) - self.assertEqual("1.2",str(self.plugin_info.version)) + def setUp(self): + """ + init + """ + # create the plugin manager + self.versionedPluginManager = VersionedPluginManager( + directories_list=[os.path.join( + os.path.dirname(os.path.abspath(__file__)), "plugins")], + plugin_info_ext="version-plugin", + ) + # load the plugins that may be found + self.versionedPluginManager.collectPlugins() + # Will be used later + self.plugin_info = None - - def testLatestPluginOfCategory(self): - self.plugin_loading_check() - - def testActivationAndDeactivation(self): - """ - Test if the activation procedure works. - """ - self.plugin_loading_check() - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - self.versionedPluginManager.activatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(self.plugin_info.plugin_object.is_activated) - self.versionedPluginManager.deactivatePluginByName(self.plugin_info.name, - self.plugin_info.category) - self.assertTrue(not self.plugin_info.plugin_object.is_activated) - # also check that this is the plugin of the latest version - # that has been activated (ok the following test is already - # ensured by the plugin_loading_check method, but this is to - # make the things clear: the plugin chosen for activation is - # the one with the latest version) - self.assertEqual("1.2",str(self.plugin_info.version)) - - - # def testDirectActivationAndDeactivation(self): - # """ - # Test if the activation procedure works when directly activating a plugin. - # """ - # self.plugin_loading_check() - # self.assertTrue(not self.plugin_info.plugin_object.is_activated) - # TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) - # self.plugin_info.plugin_object.activate() - # self.assertTrue(self.plugin_info.plugin_object.is_activated) - # self.plugin_info.plugin_object.deactivate() - # self.assertTrue(not self.plugin_info.plugin_object.is_activated) + def plugin_loading_check(self): + """ + Test if the correct plugin has been loaded. + """ + if self.plugin_info is None: + # check nb of categories + self.assertEqual( + len(self.versionedPluginManager.getCategories()), 1) + sole_category = self.versionedPluginManager.getCategories()[0] + # check the number of plugins (the older versions of the + # plugins should not be there) + self.assertEqual( + len(self.versionedPluginManager.getPluginsOfCategory(sole_category)), 1) + # older versions of the plugin should be found in the attic + self.assertEqual( + len(self.versionedPluginManager.getPluginsOfCategoryFromAttic(sole_category)), 4) + plugins = self.versionedPluginManager.getPluginsOfCategory( + sole_category) + self.plugin_info = None + for plugin_info in plugins: + TEST_MESSAGE("plugin info: %s" % plugin_info) + if plugin_info.name == "Versioned Plugin": + self.plugin_info = plugin_info + break + self.assertTrue(self.plugin_info) + # test that the name of the plugin has been correctly defined + self.assertEqual(self.plugin_info.name, "Versioned Plugin") + self.assertEqual(sole_category, self.plugin_info.category) + else: + self.assertTrue(True) + def testLoaded(self): + """ + Test if the correct plugin has been loaded. + """ + self.plugin_loading_check() + sole_category = self.versionedPluginManager.getCategories()[0] + self.assertEqual( + len(self.versionedPluginManager.getLatestPluginsOfCategory(sole_category)), 1) + self.plugin_info = self.versionedPluginManager.getLatestPluginsOfCategory( + sole_category)[0] + TEST_MESSAGE("plugin info: %s" % self.plugin_info) + # test that the name of the plugin has been correctly defined + self.assertEqual(self.plugin_info.name, "Versioned Plugin") + self.assertEqual(sole_category, self.plugin_info.category) + self.assertEqual("1.2", str(self.plugin_info.version)) + + def testLatestPluginOfCategory(self): + self.plugin_loading_check() + + def testActivationAndDeactivation(self): + """ + Test if the activation procedure works. + """ + self.plugin_loading_check() + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + self.versionedPluginManager.activatePluginByName( + self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(self.plugin_info.plugin_object.is_activated) + self.versionedPluginManager.deactivatePluginByName( + self.plugin_info.name, + self.plugin_info.category) + self.assertTrue(not self.plugin_info.plugin_object.is_activated) + # also check that this is the plugin of the latest version + # that has been activated (ok the following test is already + # ensured by the plugin_loading_check method, but this is to + # make the things clear: the plugin chosen for activation is + # the one with the latest version) + self.assertEqual("1.2", str(self.plugin_info.version)) + + # def testDirectActivationAndDeactivation(self): + # """ + # Test if the activation procedure works when directly activating a plugin. + # """ + # self.plugin_loading_check() + # self.assertTrue(not self.plugin_info.plugin_object.is_activated) + # TEST_MESSAGE("plugin object = %s" % self.plugin_info.plugin_object) + # self.plugin_info.plugin_object.activate() + # self.assertTrue(self.plugin_info.plugin_object.is_activated) + # self.plugin_info.plugin_object.deactivate() + # self.assertTrue(not self.plugin_info.plugin_object.is_activated) - - suite = unittest.TestSuite([ - unittest.TestLoader().loadTestsFromTestCase(VersionedTestsCase), - ]) + unittest.TestLoader().loadTestsFromTestCase(VersionedTestsCase), +]) diff --git a/package/test/test_settings.py b/package/test/test_settings.py index d4cab00..94f0ccb 100644 --- a/package/test/test_settings.py +++ b/package/test/test_settings.py @@ -7,22 +7,19 @@ import logging TEST_MESSAGE = logging.debug -TEMP_CONFIG_FILE_NAME=os.path.join( - os.path.dirname( - os.path.abspath(__file__)), - "tempconfig") +TEMP_CONFIG_FILE_NAME = os.path.join( + os.path.dirname( + os.path.abspath(__file__)), + "tempconfig") # set correct loading path for yapsy's files sys.path.insert(0, - os.path.dirname( - os.path.dirname( - os.path.abspath(__file__)))) + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__)))) sys.path.insert(0, - os.path.dirname( - os.path.dirname( - os.path.dirname( - os.path.abspath(__file__))))) - - - + os.path.dirname( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__))))) diff --git a/package/yapsy/AutoInstallPluginManager.py b/package/yapsy/AutoInstallPluginManager.py index d8ded98..2c1d721 100644 --- a/package/yapsy/AutoInstallPluginManager.py +++ b/package/yapsy/AutoInstallPluginManager.py @@ -13,189 +13,235 @@ === """ -import sys import os import shutil import zipfile -import io from yapsy.IPlugin import IPlugin from yapsy.PluginManagerDecorator import PluginManagerDecorator from yapsy import log +from yapsy.compat import StringIO, str class AutoInstallPluginManager(PluginManagerDecorator): - """ - A plugin manager that also manages the installation of the plugin - files into the appropriate directory. - """ - - - def __init__(self, - plugin_install_dir=None, - decorated_manager=None, - # The following args will only be used if we need to - # create a default PluginManager - categories_filter={"Default":IPlugin}, - directories_list=None, - plugin_info_ext="yapsy-plugin"): - """ - Create the plugin manager and set up the directory where to - install new plugins. - - Arguments - - ``plugin_install_dir`` - The directory where new plugins to be installed will be copied. - - .. warning:: If ``plugin_install_dir`` does not correspond to - an element of the ``directories_list``, it is - appended to the later. - - """ - # Create the base decorator class - PluginManagerDecorator.__init__(self, - decorated_manager, - categories_filter, - directories_list, - plugin_info_ext) - # set the directory for new plugins - self.plugins_places=[] - self.setInstallDir(plugin_install_dir) - - def setInstallDir(self,plugin_install_dir): - """ - Set the directory where to install new plugins. - """ - if not (plugin_install_dir in self.plugins_places): - self.plugins_places.append(plugin_install_dir) - self.install_dir = plugin_install_dir - - def getInstallDir(self): - """ - Return the directory where new plugins should be installed. - """ - return self.install_dir - - def install(self, directory, plugin_info_filename): - """ - Giving the plugin's info file (e.g. ``myplugin.yapsy-plugin``), - and the directory where it is located, get all the files that - define the plugin and copy them into the correct directory. - - Return ``True`` if the installation is a success, ``False`` if - it is a failure. - """ - # start collecting essential info about the new plugin - plugin_info, config_parser = self._gatherCorePluginInfo(directory, plugin_info_filename) - # now determine the path of the file to execute, - # depending on wether the path indicated is a - # directory or a file - if not (os.path.exists(plugin_info.path) or os.path.exists(plugin_info.path+".py") ): - log.warning("Could not find the plugin's implementation for %s." % plugin_info.name) - return False - if os.path.isdir(plugin_info.path): - try: - shutil.copytree(plugin_info.path, - os.path.join(self.install_dir,os.path.basename(plugin_info.path))) - shutil.copy(os.path.join(directory, plugin_info_filename), - self.install_dir) - except: - log.error("Could not install plugin: %s." % plugin_info.name) - return False - else: - return True - elif os.path.isfile(plugin_info.path+".py"): - try: - shutil.copy(plugin_info.path+".py", - self.install_dir) - shutil.copy(os.path.join(directory, plugin_info_filename), - self.install_dir) - except: - log.error("Could not install plugin: %s." % plugin_info.name) - return False - else: - return True - else: - return False - - - def installFromZIP(self, plugin_ZIP_filename): - """ - Giving the plugin's zip file (e.g. ``myplugin.zip``), check - that their is a valid info file in it and correct all the - plugin files into the correct directory. - - .. warning:: Only available for python 2.6 and later. - - Return ``True`` if the installation is a success, ``False`` if - it is a failure. - """ - if not os.path.isfile(plugin_ZIP_filename): - log.warning("Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename) - return False - try: - candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename) - first_bad_file = candidateZipFile.testzip() - if first_bad_file: - raise Exception("Corrupted ZIP with first bad file '%s'" % first_bad_file) - except Exception as e: - log.warning("Invalid zip file '%s' (error: %s)." % (plugin_ZIP_filename,e)) - return False - zipContent = candidateZipFile.namelist() - log.info("Investigating the content of a zip file containing: '%s'" % zipContent) - log.info("Sanity checks on zip's contained files (looking for hazardous path symbols).") - # check absence of root path and ".." shortcut that would - # send the file oustide the desired directory - for containedFileName in zipContent: - # WARNING: the sanity checks below are certainly not - # exhaustive (maybe we could do something a bit smarter by - # using os.path.expanduser, os.path.expandvars and - # os.path.normpath) - if containedFileName.startswith("/"): - log.warning("Unsecure zip file, rejected because one of its file paths ('%s') starts with '/'" % containedFileName) - return False - if containedFileName.startswith(r"\\") or containedFileName.startswith("//"): - log.warning(r"Unsecure zip file, rejected because one of its file paths ('%s') starts with '\\'" % containedFileName) - return False - if os.path.splitdrive(containedFileName)[0]: - log.warning("Unsecure zip file, rejected because one of its file paths ('%s') starts with a drive letter" % containedFileName) - return False - if os.path.isabs(containedFileName): - log.warning("Unsecure zip file, rejected because one of its file paths ('%s') is absolute" % containedFileName) - return False - pathComponent = os.path.split(containedFileName) - if ".." in pathComponent: - log.warning("Unsecure zip file, rejected because one of its file paths ('%s') contains '..'" % containedFileName) - return False - if "~" in pathComponent: - log.warning("Unsecure zip file, rejected because one of its file paths ('%s') contains '~'" % containedFileName) - return False - infoFileCandidates = [filename for filename in zipContent if os.path.dirname(filename)==""] - if not infoFileCandidates: - log.warning("Zip file structure seems wrong in '%s', no info file found." % plugin_ZIP_filename) - return False - isValid = False - log.info("Looking for the zipped plugin's info file among '%s'" % infoFileCandidates) - for infoFileName in infoFileCandidates: - infoFile = candidateZipFile.read(infoFileName) - log.info("Assuming the zipped plugin info file to be '%s'" % infoFileName) - pluginName,moduleName,_ = self._getPluginNameAndModuleFromStream(io.StringIO(str(infoFile,encoding="utf-8"))) - if moduleName is None: - continue - log.info("Checking existence of the expected module '%s' in the zip file" % moduleName) - if moduleName in zipContent or os.path.join(moduleName,"__init__.py") in zipContent: - isValid = True - break - if not isValid: - log.warning("Zip file structure seems wrong in '%s', " - "could not match info file with the implementation of plugin '%s'." % (plugin_ZIP_filename,pluginName)) - return False - else: - try: - candidateZipFile.extractall(self.install_dir) - return True - except Exception as e: - log.error("Could not install plugin '%s' from zip file '%s' (exception: '%s')." % (pluginName,plugin_ZIP_filename,e)) - return False - + + """ + A plugin manager that also manages the installation of the plugin + files into the appropriate directory. + """ + + def __init__(self, + plugin_install_dir=None, + decorated_manager=None, + # The following args will only be used if we need to + # create a default PluginManager + categories_filter={"Default": IPlugin}, + directories_list=None, + plugin_info_ext="yapsy-plugin"): + """ + Create the plugin manager and set up the directory where to + install new plugins. + + Arguments + + ``plugin_install_dir`` + The directory where new plugins to be installed will be copied. + + .. warning:: If ``plugin_install_dir`` does not correspond to + an element of the ``directories_list``, it is + appended to the later. + + """ + # Create the base decorator class + PluginManagerDecorator.__init__(self, + decorated_manager, + categories_filter, + directories_list, + plugin_info_ext) + # set the directory for new plugins + self.plugins_places = [] + self.setInstallDir(plugin_install_dir) + + def setInstallDir(self, plugin_install_dir): + """ + Set the directory where to install new plugins. + """ + if not (plugin_install_dir in self.plugins_places): + self.plugins_places.append(plugin_install_dir) + self.install_dir = plugin_install_dir + + def getInstallDir(self): + """ + Return the directory where new plugins should be installed. + """ + return self.install_dir + + def install(self, directory, plugin_info_filename): + """ + Giving the plugin's info file (e.g. ``myplugin.yapsy-plugin``), + and the directory where it is located, get all the files that + define the plugin and copy them into the correct directory. + + Return ``True`` if the installation is a success, ``False`` if + it is a failure. + """ + # start collecting essential info about the new plugin + plugin_info, config_parser = self._gatherCorePluginInfo( + directory, plugin_info_filename) + # now determine the path of the file to execute, + # depending on wether the path indicated is a + # directory or a file + if not ( + os.path.exists( + plugin_info.path) or os.path.exists( + plugin_info.path + + ".py")): + log.warning( + "Could not find the plugin's implementation for %s." % + plugin_info.name) + return False + if os.path.isdir(plugin_info.path): + try: + shutil.copytree( + plugin_info.path, + os.path.join( + self.install_dir, + os.path.basename( + plugin_info.path))) + shutil.copy(os.path.join(directory, plugin_info_filename), + self.install_dir) + except: + log.error("Could not install plugin: %s." % plugin_info.name) + return False + else: + return True + elif os.path.isfile(plugin_info.path + ".py"): + try: + shutil.copy(plugin_info.path + ".py", + self.install_dir) + shutil.copy(os.path.join(directory, plugin_info_filename), + self.install_dir) + except: + log.error("Could not install plugin: %s." % plugin_info.name) + return False + else: + return True + else: + return False + + def installFromZIP(self, plugin_ZIP_filename): + """ + Giving the plugin's zip file (e.g. ``myplugin.zip``), check + that their is a valid info file in it and correct all the + plugin files into the correct directory. + + .. warning:: Only available for python 2.6 and later. + + Return ``True`` if the installation is a success, ``False`` if + it is a failure. + """ + if not os.path.isfile(plugin_ZIP_filename): + log.warning( + "Could not find the plugin's zip file at '%s'." % + plugin_ZIP_filename) + return False + try: + candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename) + first_bad_file = candidateZipFile.testzip() + if first_bad_file: + raise Exception( + "Corrupted ZIP with first bad file '%s'" % + first_bad_file) + except Exception as e: + log.warning( + "Invalid zip file '%s' (error: %s)." % + (plugin_ZIP_filename, e)) + return False + zipContent = candidateZipFile.namelist() + log.info( + "Investigating the content of a zip file containing: '%s'" % + zipContent) + log.info( + "Sanity checks on zip's contained files (looking for hazardous path symbols).") + # check absence of root path and ".." shortcut that would + # send the file oustide the desired directory + for containedFileName in zipContent: + # WARNING: the sanity checks below are certainly not + # exhaustive (maybe we could do something a bit smarter by + # using os.path.expanduser, os.path.expandvars and + # os.path.normpath) + if containedFileName.startswith("/"): + log.warning( + "Unsecure zip file, rejected because one of its file paths ('%s') starts with '/'" % + containedFileName) + return False + if containedFileName.startswith( + r"\\") or containedFileName.startswith("//"): + log.warning( + r"Unsecure zip file, rejected because one of its file paths ('%s') starts with '\\'" % + containedFileName) + return False + if os.path.splitdrive(containedFileName)[0]: + log.warning( + "Unsecure zip file, rejected because one of its file paths ('%s') starts with a drive letter" % + containedFileName) + return False + if os.path.isabs(containedFileName): + log.warning( + "Unsecure zip file, rejected because one of its file paths ('%s') is absolute" % + containedFileName) + return False + pathComponent = os.path.split(containedFileName) + if ".." in pathComponent: + log.warning( + "Unsecure zip file, rejected because one of its file paths ('%s') contains '..'" % + containedFileName) + return False + if "~" in pathComponent: + log.warning( + "Unsecure zip file, rejected because one of its file paths ('%s') contains '~'" % + containedFileName) + return False + infoFileCandidates = [ + filename for filename in zipContent if os.path.dirname(filename) == ""] + if not infoFileCandidates: + log.warning( + "Zip file structure seems wrong in '%s', no info file found." % + plugin_ZIP_filename) + return False + isValid = False + log.info( + "Looking for the zipped plugin's info file among '%s'" % + infoFileCandidates) + for infoFileName in infoFileCandidates: + infoFile = candidateZipFile.read(infoFileName) + log.info( + "Assuming the zipped plugin info file to be '%s'" % + infoFileName) + pluginName, moduleName, _ = self._getPluginNameAndModuleFromStream( + StringIO(str(infoFile, encoding="utf-8"))) + if moduleName is None: + continue + log.info( + "Checking existence of the expected module '%s' in the zip file" % + moduleName) + if moduleName in zipContent or os.path.join( + moduleName, + "__init__.py") in zipContent: + isValid = True + break + if not isValid: + log.warning( + "Zip file structure seems wrong in '%s', " + "could not match info file with the implementation of plugin '%s'." % + (plugin_ZIP_filename, pluginName)) + return False + else: + try: + candidateZipFile.extractall(self.install_dir) + return True + except Exception as e: + log.error( + "Could not install plugin '%s' from zip file '%s' (exception: '%s')." % + (pluginName, plugin_ZIP_filename, e)) + return False diff --git a/package/yapsy/ConfigurablePluginManager.py b/package/yapsy/ConfigurablePluginManager.py index 57e0387..0a4b65c 100644 --- a/package/yapsy/ConfigurablePluginManager.py +++ b/package/yapsy/ConfigurablePluginManager.py @@ -17,261 +17,297 @@ from yapsy.PluginManagerDecorator import PluginManagerDecorator from yapsy.PluginManager import PLUGIN_NAME_FORBIDEN_STRING - + class ConfigurablePluginManager(PluginManagerDecorator): - """ - A plugin manager that also manages a configuration file. - The configuration file will be accessed through a ``ConfigParser`` - derivated object. The file can be used for other purpose by the - application using this plugin manager as it will only add a new - specific section ``[Plugin Management]`` for itself and also new - sections for some plugins that will start with ``[Plugin:...]`` - (only the plugins that explicitly requires to save configuration - options will have this kind of section). + """ + A plugin manager that also manages a configuration file. - .. warning:: when giving/building the list of plugins to activate - by default, there must not be any space in the list - (neither in the names nor in between) - """ - - CONFIG_SECTION_NAME = "Plugin Management" + The configuration file will be accessed through a ``ConfigParser`` + derivated object. The file can be used for other purpose by the + application using this plugin manager as it will only add a new + specific section ``[Plugin Management]`` for itself and also new + sections for some plugins that will start with ``[Plugin:...]`` + (only the plugins that explicitly requires to save configuration + options will have this kind of section). + .. warning:: when giving/building the list of plugins to activate + by default, there must not be any space in the list + (neither in the names nor in between) + """ - def __init__(self, - configparser_instance=None, - config_change_trigger= lambda x:True, - decorated_manager=None, - # The following args will only be used if we need to - # create a default PluginManager - categories_filter={"Default":IPlugin}, - directories_list=None, - plugin_info_ext="yapsy-plugin"): - """ - Create the plugin manager and record the ConfigParser instance - that will be used afterwards. - - The ``config_change_trigger`` argument can be used to set a - specific method to call when the configuration is - altered. This will let the client application manage the way - they want the configuration to be updated (e.g. write on file - at each change or at precise time intervalls or whatever....) - """ - # Create the base decorator class - PluginManagerDecorator.__init__(self,decorated_manager, - categories_filter, - directories_list, - plugin_info_ext) - self.setConfigParser(configparser_instance, config_change_trigger) + CONFIG_SECTION_NAME = "Plugin Management" + def __init__(self, + configparser_instance=None, + config_change_trigger=lambda x: True, + decorated_manager=None, + # The following args will only be used if we need to + # create a default PluginManager + categories_filter={"Default": IPlugin}, + directories_list=None, + plugin_info_ext="yapsy-plugin"): + """ + Create the plugin manager and record the ConfigParser instance + that will be used afterwards. - def setConfigParser(self,configparser_instance,config_change_trigger): - """ - Set the ConfigParser instance. - """ - self.config_parser = configparser_instance - # set the (optional) fucntion to be called when the - # configuration is changed: - self.config_has_changed = config_change_trigger - - def __getCategoryPluginsListFromConfig(self, plugin_list_str): - """ - Parse the string describing the list of plugins to activate, - to discover their actual names and return them. - """ - return plugin_list_str.strip(" ").split("%s"%PLUGIN_NAME_FORBIDEN_STRING) + The ``config_change_trigger`` argument can be used to set a + specific method to call when the configuration is + altered. This will let the client application manage the way + they want the configuration to be updated (e.g. write on file + at each change or at precise time intervalls or whatever....) + """ + # Create the base decorator class + PluginManagerDecorator.__init__(self, decorated_manager, + categories_filter, + directories_list, + plugin_info_ext) + self.setConfigParser(configparser_instance, config_change_trigger) - def __getCategoryPluginsConfigFromList(self, plugin_list): - """ - Compose a string describing the list of plugins to activate - """ - return PLUGIN_NAME_FORBIDEN_STRING.join(plugin_list) - - def __getCategoryOptionsName(self,category_name): - """ - Return the appropirately formated version of the category's - option. - """ - return "%s_plugins_to_load" % category_name.replace(" ","_") + def setConfigParser(self, configparser_instance, config_change_trigger): + """ + Set the ConfigParser instance. + """ + self.config_parser = configparser_instance + # set the (optional) fucntion to be called when the + # configuration is changed: + self.config_has_changed = config_change_trigger - def __addPluginToConfig(self,category_name, plugin_name): - """ - Utility function to add a plugin to the list of plugin to be - activated. - """ - # check that the section is here - if not self.config_parser.has_section(self.CONFIG_SECTION_NAME): - self.config_parser.add_section(self.CONFIG_SECTION_NAME) - # check that the category's list of activated plugins is here too - option_name = self.__getCategoryOptionsName(category_name) - if not self.config_parser.has_option(self.CONFIG_SECTION_NAME, option_name): - # if there is no list yet add a new one - self.config_parser.set(self.CONFIG_SECTION_NAME,option_name,plugin_name) - return self.config_has_changed() - else: - # get the already existing list and append the new - # activated plugin to it. - past_list_str = self.config_parser.get(self.CONFIG_SECTION_NAME,option_name) - past_list = self.__getCategoryPluginsListFromConfig(past_list_str) - # make sure we don't add it twice - if plugin_name not in past_list: - past_list.append(plugin_name) - new_list_str = self.__getCategoryPluginsConfigFromList(past_list) - self.config_parser.set(self.CONFIG_SECTION_NAME,option_name,new_list_str) - return self.config_has_changed() + def __getCategoryPluginsListFromConfig(self, plugin_list_str): + """ + Parse the string describing the list of plugins to activate, + to discover their actual names and return them. + """ + return plugin_list_str.strip(" ").split( + "%s" % + PLUGIN_NAME_FORBIDEN_STRING) - def __removePluginFromConfig(self,category_name, plugin_name): - """ - Utility function to add a plugin to the list of plugin to be - activated. - """ - # check that the section is here - if not self.config_parser.has_section(self.CONFIG_SECTION_NAME): - # then nothing to remove :) - return - # check that the category's list of activated plugins is here too - option_name = self.__getCategoryOptionsName(category_name) - if not self.config_parser.has_option(self.CONFIG_SECTION_NAME, option_name): - # if there is no list still nothing to do - return - else: - # get the already existing list - past_list_str = self.config_parser.get(self.CONFIG_SECTION_NAME,option_name) - past_list = self.__getCategoryPluginsListFromConfig(past_list_str) - if plugin_name in past_list: - past_list.remove(plugin_name) - new_list_str = self.__getCategoryPluginsConfigFromList(past_list) - self.config_parser.set(self.CONFIG_SECTION_NAME,option_name,new_list_str) - self.config_has_changed() - + def __getCategoryPluginsConfigFromList(self, plugin_list): + """ + Compose a string describing the list of plugins to activate + """ + return PLUGIN_NAME_FORBIDEN_STRING.join(plugin_list) + def __getCategoryOptionsName(self, category_name): + """ + Return the appropirately formated version of the category's + option. + """ + return "%s_plugins_to_load" % category_name.replace(" ", "_") - def registerOptionFromPlugin(self, - category_name, plugin_name, - option_name, option_value): - """ - To be called from a plugin object, register a given option in - the name of a given plugin. - """ - section_name = "%s Plugin: %s" % (category_name,plugin_name) - # if the plugin's section is not here yet, create it - if not self.config_parser.has_section(section_name): - self.config_parser.add_section(section_name) - # set the required option - self.config_parser.set(section_name,option_name,option_value) - self.config_has_changed() + def __addPluginToConfig(self, category_name, plugin_name): + """ + Utility function to add a plugin to the list of plugin to be + activated. + """ + # check that the section is here + if not self.config_parser.has_section(self.CONFIG_SECTION_NAME): + self.config_parser.add_section(self.CONFIG_SECTION_NAME) + # check that the category's list of activated plugins is here too + option_name = self.__getCategoryOptionsName(category_name) + if not self.config_parser.has_option( + self.CONFIG_SECTION_NAME, + option_name): + # if there is no list yet add a new one + self.config_parser.set( + self.CONFIG_SECTION_NAME, + option_name, + plugin_name) + return self.config_has_changed() + else: + # get the already existing list and append the new + # activated plugin to it. + past_list_str = self.config_parser.get( + self.CONFIG_SECTION_NAME, + option_name) + past_list = self.__getCategoryPluginsListFromConfig(past_list_str) + # make sure we don't add it twice + if plugin_name not in past_list: + past_list.append(plugin_name) + new_list_str = self.__getCategoryPluginsConfigFromList( + past_list) + self.config_parser.set( + self.CONFIG_SECTION_NAME, + option_name, + new_list_str) + return self.config_has_changed() - def hasOptionFromPlugin(self, - category_name, plugin_name, option_name): - """ - To be called from a plugin object, return True if the option - has already been registered. - """ - section_name = "%s Plugin: %s" % (category_name,plugin_name) - return self.config_parser.has_section(section_name) and self.config_parser.has_option(section_name,option_name) + def __removePluginFromConfig(self, category_name, plugin_name): + """ + Utility function to add a plugin to the list of plugin to be + activated. + """ + # check that the section is here + if not self.config_parser.has_section(self.CONFIG_SECTION_NAME): + # then nothing to remove :) + return + # check that the category's list of activated plugins is here too + option_name = self.__getCategoryOptionsName(category_name) + if not self.config_parser.has_option( + self.CONFIG_SECTION_NAME, + option_name): + # if there is no list still nothing to do + return + else: + # get the already existing list + past_list_str = self.config_parser.get( + self.CONFIG_SECTION_NAME, + option_name) + past_list = self.__getCategoryPluginsListFromConfig(past_list_str) + if plugin_name in past_list: + past_list.remove(plugin_name) + new_list_str = self.__getCategoryPluginsConfigFromList( + past_list) + self.config_parser.set( + self.CONFIG_SECTION_NAME, + option_name, + new_list_str) + self.config_has_changed() - def readOptionFromPlugin(self, - category_name, plugin_name, option_name): - """ - To be called from a plugin object, read a given option in - the name of a given plugin. - """ - section_name = "%s Plugin: %s" % (category_name,plugin_name) - return self.config_parser.get(section_name,option_name) + def registerOptionFromPlugin(self, + category_name, plugin_name, + option_name, option_value): + """ + To be called from a plugin object, register a given option in + the name of a given plugin. + """ + section_name = "%s Plugin: %s" % (category_name, plugin_name) + # if the plugin's section is not here yet, create it + if not self.config_parser.has_section(section_name): + self.config_parser.add_section(section_name) + # set the required option + self.config_parser.set(section_name, option_name, option_value) + self.config_has_changed() + def hasOptionFromPlugin(self, + category_name, plugin_name, option_name): + """ + To be called from a plugin object, return True if the option + has already been registered. + """ + section_name = "%s Plugin: %s" % (category_name, plugin_name) + return self.config_parser.has_section( + section_name) and self.config_parser.has_option(section_name, option_name) - def __decoratePluginObject(self, category_name, plugin_name, plugin_object): - """ - Add two methods to the plugin objects that will make it - possible for it to benefit from this class's api concerning - the management of the options. - """ - plugin_object.setConfigOption = lambda x,y: self.registerOptionFromPlugin(category_name, - plugin_name, - x,y) - plugin_object.setConfigOption.__doc__ = self.registerOptionFromPlugin.__doc__ - plugin_object.getConfigOption = lambda x: self.readOptionFromPlugin(category_name, - plugin_name, - x) - plugin_object.getConfigOption.__doc__ = self.readOptionFromPlugin.__doc__ - plugin_object.hasConfigOption = lambda x: self.hasOptionFromPlugin(category_name, - plugin_name, - x) - plugin_object.hasConfigOption.__doc__ = self.hasOptionFromPlugin.__doc__ + def readOptionFromPlugin(self, + category_name, plugin_name, option_name): + """ + To be called from a plugin object, read a given option in + the name of a given plugin. + """ + section_name = "%s Plugin: %s" % (category_name, plugin_name) + return self.config_parser.get(section_name, option_name) - def activatePluginByName(self, plugin_name, category_name="Default", save_state=True): - """ - Activate a plugin, , and remember it (in the config file). + def __decoratePluginObject( + self, + category_name, + plugin_name, + plugin_object): + """ + Add two methods to the plugin objects that will make it + possible for it to benefit from this class's api concerning + the management of the options. + """ + plugin_object.setConfigOption = lambda x, y: self.registerOptionFromPlugin( + category_name, plugin_name, x, y) + plugin_object.setConfigOption.__doc__ = self.registerOptionFromPlugin.__doc__ + plugin_object.getConfigOption = lambda x: self.readOptionFromPlugin( + category_name, + plugin_name, + x) + plugin_object.getConfigOption.__doc__ = self.readOptionFromPlugin.__doc__ + plugin_object.hasConfigOption = lambda x: self.hasOptionFromPlugin( + category_name, + plugin_name, + x) + plugin_object.hasConfigOption.__doc__ = self.hasOptionFromPlugin.__doc__ - If you want the plugin to benefit from the configuration - utility defined by this manager, it is crucial to use this - method to activate a plugin and not call the plugin object's - ``activate`` method. In fact, this method will also "decorate" - the plugin object so that it can use this class's methods to - register its own options. - - By default, the plugin's activation is registered in the - config file but if you d'ont want this set the 'save_state' - argument to False. - """ - # first decorate the plugin - pta = self._component.getPluginByName(plugin_name,category_name) - if pta is None: - return None - self.__decoratePluginObject(category_name,plugin_name,pta.plugin_object) - # activate the plugin - plugin_object = self._component.activatePluginByName(plugin_name,category_name) - # check the activation and then optionally set the config option - if plugin_object.is_activated: - if save_state: - self.__addPluginToConfig(category_name,plugin_name) - return plugin_object - return None + def activatePluginByName( + self, + plugin_name, + category_name="Default", + save_state=True): + """ + Activate a plugin, , and remember it (in the config file). - def deactivatePluginByName(self, plugin_name, category_name="Default", save_state=True): - """ - Deactivate a plugin, and remember it (in the config file). + If you want the plugin to benefit from the configuration + utility defined by this manager, it is crucial to use this + method to activate a plugin and not call the plugin object's + ``activate`` method. In fact, this method will also "decorate" + the plugin object so that it can use this class's methods to + register its own options. - By default, the plugin's deactivation is registered in the - config file but if you d'ont want this set the ``save_state`` - argument to False. - """ - # activate the plugin - plugin_object = self._component.deactivatePluginByName(plugin_name,category_name) - if plugin_object is None: - return None - # check the deactivation and then optionnally set the config option - if not plugin_object.is_activated: - if save_state: - self.__removePluginFromConfig(category_name,plugin_name) - return plugin_object - return None + By default, the plugin's activation is registered in the + config file but if you d'ont want this set the 'save_state' + argument to False. + """ + # first decorate the plugin + pta = self._component.getPluginByName(plugin_name, category_name) + if pta is None: + return None + self.__decoratePluginObject( + category_name, + plugin_name, + pta.plugin_object) + # activate the plugin + plugin_object = self._component.activatePluginByName( + plugin_name, + category_name) + # check the activation and then optionally set the config option + if plugin_object.is_activated: + if save_state: + self.__addPluginToConfig(category_name, plugin_name) + return plugin_object + return None - def loadPlugins(self,callback=None): - """ - Walk through the plugins' places and look for plugins. Then - for each plugin candidate look for its category, load it and - stores it in the appropriate slot of the ``category_mapping``. - """ - self._component.loadPlugins(callback) - # now load the plugins according to the recorded configuration - if self.config_parser.has_section(self.CONFIG_SECTION_NAME): - # browse all the categories - for category_name in list(self._component.category_mapping.keys()): - # get the list of plugins to be activated for this - # category - option_name = "%s_plugins_to_load"%category_name - if self.config_parser.has_option(self.CONFIG_SECTION_NAME, - option_name): - plugin_list_str = self.config_parser.get(self.CONFIG_SECTION_NAME, - option_name) - plugin_list = self.__getCategoryPluginsListFromConfig(plugin_list_str) - # activate all the plugins that should be - # activated - for plugin_name in plugin_list: - self.activatePluginByName(plugin_name,category_name) + def deactivatePluginByName( + self, + plugin_name, + category_name="Default", + save_state=True): + """ + Deactivate a plugin, and remember it (in the config file). - + By default, the plugin's deactivation is registered in the + config file but if you d'ont want this set the ``save_state`` + argument to False. + """ + # activate the plugin + plugin_object = self._component.deactivatePluginByName( + plugin_name, + category_name) + if plugin_object is None: + return None + # check the deactivation and then optionnally set the config option + if not plugin_object.is_activated: + if save_state: + self.__removePluginFromConfig(category_name, plugin_name) + return plugin_object + return None - + def loadPlugins(self, callback=None): + """ + Walk through the plugins' places and look for plugins. Then + for each plugin candidate look for its category, load it and + stores it in the appropriate slot of the ``category_mapping``. + """ + self._component.loadPlugins(callback) + # now load the plugins according to the recorded configuration + if self.config_parser.has_section(self.CONFIG_SECTION_NAME): + # browse all the categories + for category_name in list(self._component.category_mapping.keys()): + # get the list of plugins to be activated for this + # category + option_name = "%s_plugins_to_load" % category_name + if self.config_parser.has_option(self.CONFIG_SECTION_NAME, + option_name): + plugin_list_str = self.config_parser.get( + self.CONFIG_SECTION_NAME, + option_name) + plugin_list = self.__getCategoryPluginsListFromConfig( + plugin_list_str) + # activate all the plugins that should be + # activated + for plugin_name in plugin_list: + self.activatePluginByName(plugin_name, category_name) diff --git a/package/yapsy/FilteredPluginManager.py b/package/yapsy/FilteredPluginManager.py index 05b03ef..a7618e5 100644 --- a/package/yapsy/FilteredPluginManager.py +++ b/package/yapsy/FilteredPluginManager.py @@ -25,114 +25,114 @@ API === """ - + from yapsy.IPlugin import IPlugin -from yapsy.PluginManagerDecorator import PluginManagerDecorator +from yapsy.PluginManagerDecorator import PluginManagerDecorator class FilteredPluginManager(PluginManagerDecorator): - """ - Base class for decorators which filter the plugins list - before they are loaded. - """ - - def __init__(self, - decorated_manager=None, - categories_filter={"Default":IPlugin}, - directories_list=None, - plugin_info_ext="yapsy-plugin"): - """ - """ - # Create the base decorator class - PluginManagerDecorator.__init__(self,decorated_manager, - categories_filter, - directories_list, - plugin_info_ext) - # prepare the mapping of the latest version of each plugin - self.rejectedPlugins = [ ] - - - - def filterPlugins(self): - """ - Go through the currently available candidates, and and either - leaves them, or moves them into the list of rejected Plugins. - - Can be overridden if overriding ``isPluginOk`` sentinel is not - powerful enough. - """ - self.rejectedPlugins = [ ] - for candidate_infofile, candidate_filepath, plugin_info in self._component.getPluginCandidates(): - if not self.isPluginOk( plugin_info): - self.rejectPluginCandidate((candidate_infofile, candidate_filepath, plugin_info) ) - - def rejectPluginCandidate(self,pluginTuple): - """ - Move a plugin from the candidates list to the rejected List. - """ - if pluginTuple in self.getPluginCandidates(): - self._component.removePluginCandidate(pluginTuple) - if not pluginTuple in self.rejectedPlugins: - self.rejectedPlugins.append(pluginTuple) - - def unrejectPluginCandidate(self,pluginTuple): - """ - Move a plugin from the rejected list to into the candidates - list. - """ - if not pluginTuple in self.getPluginCandidates(): - self._component.appendPluginCandidate(pluginTuple) - if pluginTuple in self.rejectedPlugins: - self.rejectedPlugins.remove(pluginTuple) - - def removePluginCandidate(self,pluginTuple): - """ - Remove a plugin from the list of candidates. - """ - if pluginTuple in self.getPluginCandidates(): - self._component.removePluginCandidate(pluginTuple) - if pluginTuple in self.rejectedPlugins: - self.rejectedPlugins.remove(pluginTuple) - - - def appendPluginCandidate(self,pluginTuple): - """ - Add a new candidate. - """ - if self.isPluginOk(pluginTuple[2]): - if pluginTuple not in self.getPluginCandidates(): - self._component.appendPluginCandidate(pluginTuple) - else: - if not pluginTuple in self.rejectedPlugins: - self.rejectedPlugins.append(pluginTuple) - - def isPluginOk(self,info): - """ - Sentinel function to detect if a plugin should be filtered. - - ``info`` is an instance of a ``PluginInfo`` and this method is - expected to return True if the corresponding plugin can be - accepted, and False if it must be filtered out. - - Subclasses should override this function and return false for - any plugin which they do not want to be loadable. - """ - return True - - def locatePlugins(self): - """ - locate and filter plugins. - """ - #Reset Catalogue - self.setCategoriesFilter(self._component.categories_interfaces) - #Reread and filter. - self._component.locatePlugins() - self.filterPlugins() - return len(self._component.getPluginCandidates()) - - def getRejectedPlugins(self): - """ - Return the list of rejected plugins. - """ - return self.rejectedPlugins[:] + + """ + Base class for decorators which filter the plugins list + before they are loaded. + """ + + def __init__(self, + decorated_manager=None, + categories_filter={"Default": IPlugin}, + directories_list=None, + plugin_info_ext="yapsy-plugin"): + """ + """ + # Create the base decorator class + PluginManagerDecorator.__init__(self, decorated_manager, + categories_filter, + directories_list, + plugin_info_ext) + # prepare the mapping of the latest version of each plugin + self.rejectedPlugins = [] + + def filterPlugins(self): + """ + Go through the currently available candidates, and and either + leaves them, or moves them into the list of rejected Plugins. + + Can be overridden if overriding ``isPluginOk`` sentinel is not + powerful enough. + """ + self.rejectedPlugins = [] + for candidate_infofile, candidate_filepath, plugin_info in self._component.getPluginCandidates( + ): + if not self.isPluginOk(plugin_info): + self.rejectPluginCandidate( + (candidate_infofile, candidate_filepath, plugin_info)) + + def rejectPluginCandidate(self, pluginTuple): + """ + Move a plugin from the candidates list to the rejected List. + """ + if pluginTuple in self.getPluginCandidates(): + self._component.removePluginCandidate(pluginTuple) + if not pluginTuple in self.rejectedPlugins: + self.rejectedPlugins.append(pluginTuple) + + def unrejectPluginCandidate(self, pluginTuple): + """ + Move a plugin from the rejected list to into the candidates + list. + """ + if not pluginTuple in self.getPluginCandidates(): + self._component.appendPluginCandidate(pluginTuple) + if pluginTuple in self.rejectedPlugins: + self.rejectedPlugins.remove(pluginTuple) + + def removePluginCandidate(self, pluginTuple): + """ + Remove a plugin from the list of candidates. + """ + if pluginTuple in self.getPluginCandidates(): + self._component.removePluginCandidate(pluginTuple) + if pluginTuple in self.rejectedPlugins: + self.rejectedPlugins.remove(pluginTuple) + + def appendPluginCandidate(self, pluginTuple): + """ + Add a new candidate. + """ + if self.isPluginOk(pluginTuple[2]): + if pluginTuple not in self.getPluginCandidates(): + self._component.appendPluginCandidate(pluginTuple) + else: + if not pluginTuple in self.rejectedPlugins: + self.rejectedPlugins.append(pluginTuple) + + def isPluginOk(self, info): + """ + Sentinel function to detect if a plugin should be filtered. + + ``info`` is an instance of a ``PluginInfo`` and this method is + expected to return True if the corresponding plugin can be + accepted, and False if it must be filtered out. + + Subclasses should override this function and return false for + any plugin which they do not want to be loadable. + """ + return True + + def locatePlugins(self): + """ + locate and filter plugins. + """ + # Reset Catalogue + self.setCategoriesFilter(self._component.categories_interfaces) + # Reread and filter. + self._component.locatePlugins() + self.filterPlugins() + return len(self._component.getPluginCandidates()) + + def getRejectedPlugins(self): + """ + Return the list of rejected plugins. + """ + return self.rejectedPlugins[:] diff --git a/package/yapsy/IPlugin.py b/package/yapsy/IPlugin.py index cbe2d32..8bfc31f 100644 --- a/package/yapsy/IPlugin.py +++ b/package/yapsy/IPlugin.py @@ -36,25 +36,25 @@ class IPlugin(object): - """ - The most simple interface to be inherited when creating a plugin. - """ - - def __init__(self): - """ - Set the basic variables. - """ - self.is_activated = False - - def activate(self): - """ - Called at plugin activation. - """ - self.is_activated = True - - def deactivate(self): - """ - Called when the plugin is disabled. - """ - self.is_activated = False + """ + The most simple interface to be inherited when creating a plugin. + """ + + def __init__(self): + """ + Set the basic variables. + """ + self.is_activated = False + + def activate(self): + """ + Called at plugin activation. + """ + self.is_activated = True + + def deactivate(self): + """ + Called when the plugin is disabled. + """ + self.is_activated = False diff --git a/package/yapsy/IPluginLocator.py b/package/yapsy/IPluginLocator.py index fb12892..4a01231 100644 --- a/package/yapsy/IPluginLocator.py +++ b/package/yapsy/IPluginLocator.py @@ -2,7 +2,6 @@ # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t; python-indent: 4 -*- - """ Role ==== @@ -19,87 +18,101 @@ from yapsy import log + class IPluginLocator(object): - """ - Plugin Locator interface with some methods already implemented to - manage the awkward backward compatible stuff. - """ - - def locatePlugins(self): - """ - Walk through the plugins' places and look for plugins. - - Return the discovered plugins as a list of - ``(candidate_infofile_path, candidate_file_path,plugin_info_instance)`` - and their number. - """ - raise NotImplementedError("locatePlugins must be reimplemented by %s" % self) - - def gatherCorePluginInfo(self, directory, filename): - """ - Return a ``PluginInfo`` as well as the ``ConfigParser`` used to build it. - - If filename is a valid plugin discovered by any of the known - strategy in use. Returns None,None otherwise. - """ - raise NotImplementedError("gatherPluginInfo must be reimplemented by %s" % self) - - # -------------------------------------------------------------------- - # Below are backward compatibility methods: if you inherit from - # IPluginLocator it's ok not to reimplement them, there will only - # be a warning message logged if they are called and not - # reimplemented. - # -------------------------------------------------------------------- - - def getPluginNameAndModuleFromStream(self,fileobj): - """ - DEPRECATED(>1.9): kept for backward compatibility - with existing PluginManager child classes. - - Return a 3-uple with the name of the plugin, its - module and the config_parser used to gather the core - data *in a tuple*, if the required info could be - localised, else return ``(None,None,None)``. - """ - log.warn("setPluginInfoClass was called but '%s' doesn't implement it." % self) - return None,None,None - - - def setPluginInfoClass(self, picls, names=None): - """ - DEPRECATED(>1.9): kept for backward compatibility - with existing PluginManager child classes. - - Set the class that holds PluginInfo. The class should inherit - from ``PluginInfo``. - """ - log.warn("setPluginInfoClass was called but '%s' doesn't implement it." % self) - - def getPluginInfoClass(self): - """ - DEPRECATED(>1.9): kept for backward compatibility - with existing PluginManager child classes. - - Get the class that holds PluginInfo. - """ - log.warn("getPluginInfoClass was called but '%s' doesn't implement it." % self) - return None - - def setPluginPlaces(self, directories_list): - """ - DEPRECATED(>1.9): kept for backward compatibility - with existing PluginManager child classes. - - Set the list of directories where to look for plugin places. - """ - log.warn("setPluginPlaces was called but '%s' doesn't implement it." % self) - - def updatePluginPlaces(self, directories_list): - """ - DEPRECATED(>1.9): kept for backward compatibility - with existing PluginManager child classes. - - Updates the list of directories where to look for plugin places. - """ - log.warn("updatePluginPlaces was called but '%s' doesn't implement it." % self) + """ + Plugin Locator interface with some methods already implemented to + manage the awkward backward compatible stuff. + """ + + def locatePlugins(self): + """ + Walk through the plugins' places and look for plugins. + + Return the discovered plugins as a list of + ``(candidate_infofile_path, candidate_file_path,plugin_info_instance)`` + and their number. + """ + raise NotImplementedError( + "locatePlugins must be reimplemented by %s" % + self) + + def gatherCorePluginInfo(self, directory, filename): + """ + Return a ``PluginInfo`` as well as the ``ConfigParser`` used to build it. + + If filename is a valid plugin discovered by any of the known + strategy in use. Returns None,None otherwise. + """ + raise NotImplementedError( + "gatherPluginInfo must be reimplemented by %s" % + self) + + # -------------------------------------------------------------------- + # Below are backward compatibility methods: if you inherit from + # IPluginLocator it's ok not to reimplement them, there will only + # be a warning message logged if they are called and not + # reimplemented. + # -------------------------------------------------------------------- + + def getPluginNameAndModuleFromStream(self, fileobj): + """ + DEPRECATED(>1.9): kept for backward compatibility + with existing PluginManager child classes. + + Return a 3-uple with the name of the plugin, its + module and the config_parser used to gather the core + data *in a tuple*, if the required info could be + localised, else return ``(None,None,None)``. + """ + log.warn( + "setPluginInfoClass was called but '%s' doesn't implement it." % + self) + return None, None, None + + def setPluginInfoClass(self, picls, names=None): + """ + DEPRECATED(>1.9): kept for backward compatibility + with existing PluginManager child classes. + + Set the class that holds PluginInfo. The class should inherit + from ``PluginInfo``. + """ + log.warn( + "setPluginInfoClass was called but '%s' doesn't implement it." % + self) + + def getPluginInfoClass(self): + """ + DEPRECATED(>1.9): kept for backward compatibility + with existing PluginManager child classes. + + Get the class that holds PluginInfo. + """ + log.warn( + "getPluginInfoClass was called but '%s' doesn't implement it." % + self) + return None + + def setPluginPlaces(self, directories_list): + """ + DEPRECATED(>1.9): kept for backward compatibility + with existing PluginManager child classes. + + Set the list of directories where to look for plugin places. + """ + log.warn( + "setPluginPlaces was called but '%s' doesn't implement it." % + self) + + def updatePluginPlaces(self, directories_list): + """ + DEPRECATED(>1.9): kept for backward compatibility + with existing PluginManager child classes. + + Updates the list of directories where to look for plugin places. + """ + log.warn( + "updatePluginPlaces was called but '%s' doesn't implement it." % + self) diff --git a/package/yapsy/PluginFileLocator.py b/package/yapsy/PluginFileLocator.py index 24b3ed9..74914c4 100644 --- a/package/yapsy/PluginFileLocator.py +++ b/package/yapsy/PluginFileLocator.py @@ -31,7 +31,7 @@ look for files matching a regex and considers them as being the plugin itself. -All analyzers must enforce the +All analyzers must enforce the It enforces the ``plugin locator`` policy as defined by ``IPluginLocator`` and used by ``PluginManager``. @@ -52,482 +52,571 @@ import os import re from yapsy import log -import configparser +from yapsy.compat import ConfigParser, is_py2 from yapsy.PluginInfo import PluginInfo from yapsy import PLUGIN_NAME_FORBIDEN_STRING from yapsy.IPluginLocator import IPluginLocator +class IPluginFileAnalyzer(object): + """ + Define the methods expected by PluginFileLocator for its 'analyzer'. + """ -class IPluginFileAnalyzer(object): - """ - Define the methods expected by PluginFileLocator for its 'analyzer'. - """ + def __init__(self, name): + self.name = name - def __init__(self,name): - self.name = name - - def isValidPlugin(self, filename): - """ - Check if the resource found at filename is a valid plugin. - """ - raise NotImplementedError("'isValidPlugin' must be reimplemented by %s" % self) + def isValidPlugin(self, filename): + """ + Check if the resource found at filename is a valid plugin. + """ + raise NotImplementedError( + "'isValidPlugin' must be reimplemented by %s" % + self) + def getInfosDictFromPlugin(self, dirpath, filename): + """ + Returns the extracted plugin informations as a dictionary. + This function ensures that "name" and "path" are provided. - def getInfosDictFromPlugin(self, dirpath, filename): - """ - Returns the extracted plugin informations as a dictionary. - This function ensures that "name" and "path" are provided. + *dirpath* is the full path to the directory where the plugin file is - *dirpath* is the full path to the directory where the plugin file is + *filename* is the name (ie the basename) of the plugin file. - *filename* is the name (ie the basename) of the plugin file. - - If *callback* function has not been provided for this strategy, - we use the filename alone to extract minimal informations. - """ - raise NotImplementedError("'getInfosDictFromPlugin' must be reimplemented by %s" % self) + If *callback* function has not been provided for this strategy, + we use the filename alone to extract minimal informations. + """ + raise NotImplementedError( + "'getInfosDictFromPlugin' must be reimplemented by %s" % + self) class PluginFileAnalyzerWithInfoFile(IPluginFileAnalyzer): - """ - Consider plugins described by a textual description file. - - A plugin is expected to be described by a text file ('ini' format) with a specific extension (.yapsy-plugin by default). - - This file must contain at least the following information:: - - [Core] - Name = name of the module - Module = relative_path/to/python_file_or_directory - - Optionnally the description file may also contain the following section (in addition to the above one):: - - [Documentation] - Author = Author Name - Version = Major.minor - Website = url_for_plugin - Description = A simple one-sentence description - - """ - def __init__(self, name, extensions="yapsy-plugin"): - """ - Creates a new analyzer named *name* and dedicated to check and analyze plugins described by a textual "info file". - - *name* name of the plugin. - - *extensions* the expected extensions for the plugin info file. May be a string or a tuple of strings if several extensions are expected. - """ - IPluginFileAnalyzer.__init__(self,name) - self.setPluginInfoExtension(extensions) - - - def setPluginInfoExtension(self,extensions): - """ - Set the extension that will identify a plugin info file. - - *extensions* May be a string or a tuple of strings if several extensions are expected. - """ - # Make sure extension is a tuple - if not isinstance(extensions, tuple): - extensions = (extensions, ) - self.expectedExtensions = extensions - - - def isValidPlugin(self, filename): - """ - Check if it is a valid plugin based on the given plugin info file extension(s). - If several extensions are provided, the first matching will cause the function - to exit successfully. - """ - res = False - for ext in self.expectedExtensions: - if filename.endswith(".%s" % ext): - res = True - break - return res - - def getPluginNameAndModuleFromStream(self, infoFileObject, candidate_infofile=None): - """ - Extract the name and module of a plugin from the - content of the info file that describes it and which - is stored in ``infoFileObject``. - - .. note:: Prefer using ``_extractCorePluginInfo`` - instead, whenever possible... - - .. warning:: ``infoFileObject`` must be a file-like object: - either an opened file for instance or a string - buffer wrapped in a StringIO instance as another - example. - - .. note:: ``candidate_infofile`` must be provided - whenever possible to get better error messages. - - Return a 3-uple with the name of the plugin, its - module and the config_parser used to gather the core - data *in a tuple*, if the required info could be - localised, else return ``(None,None,None)``. - - .. note:: This is supposed to be used internally by subclasses - and decorators. - """ - # parse the information buffer to get info about the plugin - config_parser = configparser.ConfigParser() - try: - config_parser.read_file(infoFileObject) - except Exception as e: - log.debug("Could not parse the plugin file '%s' (exception raised was '%s')" % (candidate_infofile,e)) - return (None, None, None) - # check if the basic info is available - if not config_parser.has_section("Core"): - log.debug("Plugin info file has no 'Core' section (in '%s')" % candidate_infofile) - return (None, None, None) - if not config_parser.has_option("Core","Name") or not config_parser.has_option("Core","Module"): - log.debug("Plugin info file has no 'Name' or 'Module' section (in '%s')" % candidate_infofile) - return (None, None, None) - # check that the given name is valid - name = config_parser.get("Core", "Name") - name = name.strip() - if PLUGIN_NAME_FORBIDEN_STRING in name: - log.debug("Plugin name contains forbiden character: %s (in '%s')" % (PLUGIN_NAME_FORBIDEN_STRING, - candidate_infofile)) - return (None, None, None) - return (name, config_parser.get("Core", "Module"), config_parser) - - def _extractCorePluginInfo(self,directory, filename): - """ - Gather the core information (name, and module to be loaded) - about a plugin described by it's info file (found at - 'directory/filename'). - - Return a dictionary with name and path of the plugin as well - as the ConfigParser instance used to collect these info. - - .. note:: This is supposed to be used internally by subclasses - and decorators. - """ - # now we can consider the file as a serious candidate - if not isinstance(filename, str): - # filename is a file object: use it - name, moduleName, config_parser = self.getPluginNameAndModuleFromStream(filename) - else: - candidate_infofile_path = os.path.join(directory, filename) - # parse the information file to get info about the plugin - with open(candidate_infofile_path) as candidate_infofile: - name, moduleName, config_parser = self.getPluginNameAndModuleFromStream(candidate_infofile,candidate_infofile_path) - if (name, moduleName, config_parser) == (None, None, None): - return (None,None) - infos = {"name":name, "path":os.path.join(directory, moduleName)} - return infos, config_parser - - def _extractBasicPluginInfo(self,directory, filename): - """ - Gather some basic documentation about the plugin described by - it's info file (found at 'directory/filename'). - - Return a dictionary containing the core information (name and - path) as well as as the 'documentation' info (version, author, - description etc). - - See also: - - ``self._extractCorePluginInfo`` - """ - infos, config_parser = self._extractCorePluginInfo(directory, filename) - # collect additional (but usually quite usefull) information - if infos and config_parser and config_parser.has_section("Documentation"): - if config_parser.has_option("Documentation","Author"): - infos["author"] = config_parser.get("Documentation", "Author") - if config_parser.has_option("Documentation","Version"): - infos["version"] = config_parser.get("Documentation", "Version") - if config_parser.has_option("Documentation","Website"): - infos["website"] = config_parser.get("Documentation", "Website") - if config_parser.has_option("Documentation","Copyright"): - infos["copyright"] = config_parser.get("Documentation", "Copyright") - if config_parser.has_option("Documentation","Description"): - infos["description"] = config_parser.get("Documentation", "Description") - return infos, config_parser - - def getInfosDictFromPlugin(self, dirpath, filename): - """ - Returns the extracted plugin informations as a dictionary. - This function ensures that "name" and "path" are provided. - - If *callback* function has not been provided for this strategy, - we use the filename alone to extract minimal informations. - """ - infos, config_parser = self._extractBasicPluginInfo(dirpath, filename) - if not infos or infos.get("name", None) is None: - raise ValueError("Missing *name* of the plugin in extracted infos.") - if not infos or infos.get("path", None) is None: - raise ValueError("Missing *path* of the plugin in extracted infos.") - return infos, config_parser - - + + """ + Consider plugins described by a textual description file. + + A plugin is expected to be described by a text file ('ini' format) with a specific extension (.yapsy-plugin by default). + + This file must contain at least the following information:: + + [Core] + Name = name of the module + Module = relative_path/to/python_file_or_directory + + Optionnally the description file may also contain the following section (in addition to the above one):: + + [Documentation] + Author = Author Name + Version = Major.minor + Website = url_for_plugin + Description = A simple one-sentence description + + """ + + def __init__(self, name, extensions="yapsy-plugin"): + """ + Creates a new analyzer named *name* and dedicated to check and analyze plugins described by a textual "info file". + + *name* name of the plugin. + + *extensions* the expected extensions for the plugin info file. May be a string or a tuple of strings if several extensions are expected. + """ + IPluginFileAnalyzer.__init__(self, name) + self.setPluginInfoExtension(extensions) + + def setPluginInfoExtension(self, extensions): + """ + Set the extension that will identify a plugin info file. + + *extensions* May be a string or a tuple of strings if several extensions are expected. + """ + # Make sure extension is a tuple + if not isinstance(extensions, tuple): + extensions = (extensions, ) + self.expectedExtensions = extensions + + def isValidPlugin(self, filename): + """ + Check if it is a valid plugin based on the given plugin info file extension(s). + If several extensions are provided, the first matching will cause the function + to exit successfully. + """ + res = False + for ext in self.expectedExtensions: + if filename.endswith(".%s" % ext): + res = True + break + return res + + def getPluginNameAndModuleFromStream( + self, + infoFileObject, + candidate_infofile=None): + """ + Extract the name and module of a plugin from the + content of the info file that describes it and which + is stored in ``infoFileObject``. + + .. note:: Prefer using ``_extractCorePluginInfo`` + instead, whenever possible... + + .. warning:: ``infoFileObject`` must be a file-like object: + either an opened file for instance or a string + buffer wrapped in a StringIO instance as another + example. + + .. note:: ``candidate_infofile`` must be provided + whenever possible to get better error messages. + + Return a 3-uple with the name of the plugin, its + module and the config_parser used to gather the core + data *in a tuple*, if the required info could be + localised, else return ``(None,None,None)``. + + .. note:: This is supposed to be used internally by subclasses + and decorators. + """ + # parse the information buffer to get info about the plugin + config_parser = ConfigParser() + try: + if is_py2: + config_parser.readfp(infoFileObject) + else: + config_parser.read_file(infoFileObject) + except Exception as e: + log.debug( + "Could not parse the plugin file '%s' (exception raised was '%s')" % + (candidate_infofile, e)) + return (None, None, None) + # check if the basic info is available + if not config_parser.has_section("Core"): + log.debug( + "Plugin info file has no 'Core' section (in '%s')" % + candidate_infofile) + return (None, None, None) + if not config_parser.has_option( + "Core", + "Name") or not config_parser.has_option( + "Core", + "Module"): + log.debug( + "Plugin info file has no 'Name' or 'Module' section (in '%s')" % + candidate_infofile) + return (None, None, None) + # check that the given name is valid + name = config_parser.get("Core", "Name") + name = name.strip() + if PLUGIN_NAME_FORBIDEN_STRING in name: + log.debug( + "Plugin name contains forbiden character: %s (in '%s')" % + (PLUGIN_NAME_FORBIDEN_STRING, candidate_infofile)) + return (None, None, None) + return (name, config_parser.get("Core", "Module"), config_parser) + + def _extractCorePluginInfo(self, directory, filename): + """ + Gather the core information (name, and module to be loaded) + about a plugin described by it's info file (found at + 'directory/filename'). + + Return a dictionary with name and path of the plugin as well + as the ConfigParser instance used to collect these info. + + .. note:: This is supposed to be used internally by subclasses + and decorators. + """ + # now we can consider the file as a serious candidate + if not isinstance(filename, str): + # filename is a file object: use it + name, moduleName, config_parser = self.getPluginNameAndModuleFromStream( + filename) + else: + candidate_infofile_path = os.path.join(directory, filename) + # parse the information file to get info about the plugin + with open(candidate_infofile_path) as candidate_infofile: + name, moduleName, config_parser = self.getPluginNameAndModuleFromStream( + candidate_infofile, candidate_infofile_path) + if (name, moduleName, config_parser) == (None, None, None): + return (None, None) + infos = {"name": name, "path": os.path.join(directory, moduleName)} + return infos, config_parser + + def _extractBasicPluginInfo(self, directory, filename): + """ + Gather some basic documentation about the plugin described by + it's info file (found at 'directory/filename'). + + Return a dictionary containing the core information (name and + path) as well as as the 'documentation' info (version, author, + description etc). + + See also: + + ``self._extractCorePluginInfo`` + """ + infos, config_parser = self._extractCorePluginInfo(directory, filename) + # collect additional (but usually quite usefull) information + if infos and config_parser and config_parser.has_section( + "Documentation"): + if config_parser.has_option("Documentation", "Author"): + infos["author"] = config_parser.get("Documentation", "Author") + if config_parser.has_option("Documentation", "Version"): + infos["version"] = config_parser.get( + "Documentation", + "Version") + if config_parser.has_option("Documentation", "Website"): + infos["website"] = config_parser.get( + "Documentation", + "Website") + if config_parser.has_option("Documentation", "Copyright"): + infos["copyright"] = config_parser.get( + "Documentation", + "Copyright") + if config_parser.has_option("Documentation", "Description"): + infos["description"] = config_parser.get( + "Documentation", + "Description") + return infos, config_parser + + def getInfosDictFromPlugin(self, dirpath, filename): + """ + Returns the extracted plugin informations as a dictionary. + This function ensures that "name" and "path" are provided. + + If *callback* function has not been provided for this strategy, + we use the filename alone to extract minimal informations. + """ + infos, config_parser = self._extractBasicPluginInfo(dirpath, filename) + if not infos or infos.get("name", None) is None: + raise ValueError( + "Missing *name* of the plugin in extracted infos.") + if not infos or infos.get("path", None) is None: + raise ValueError( + "Missing *path* of the plugin in extracted infos.") + return infos, config_parser + + class PluginFileAnalyzerMathingRegex(IPluginFileAnalyzer): - """ - An analyzer that targets plugins decribed by files whose name match a given regex. - """ - def __init__(self, name, regexp): - IPluginFileAnalyzer.__init__(self,name) - self.regexp = regexp - - def isValidPlugin(self, filename): - """ - Checks if the given filename is a valid plugin for this Strategy - """ - reg = re.compile(self.regexp) - if reg.match(filename) is not None: - return True - return False - - def getInfosDictFromPlugin(self, dirpath, filename): - """ - Returns the extracted plugin informations as a dictionary. - This function ensures that "name" and "path" are provided. - """ - # use the filename alone to extract minimal informations. - infos = {} - module_name = os.path.splitext(filename)[0] - plugin_filename = os.path.join(dirpath,filename) - if module_name == "__init__": - module_name = os.path.basename(dirpath) - plugin_filename = dirpath - infos["name"] = "%s" % module_name - infos["path"] = plugin_filename - cf_parser = configparser.ConfigParser() - cf_parser.add_section("Core") - cf_parser.set("Core","Name",infos["name"]) - cf_parser.set("Core","Module",infos["path"]) - return infos,cf_parser + """ + An analyzer that targets plugins decribed by files whose name match a given regex. + """ + + def __init__(self, name, regexp): + IPluginFileAnalyzer.__init__(self, name) + self.regexp = regexp + + def isValidPlugin(self, filename): + """ + Checks if the given filename is a valid plugin for this Strategy + """ + reg = re.compile(self.regexp) + if reg.match(filename) is not None: + return True + return False + + def getInfosDictFromPlugin(self, dirpath, filename): + """ + Returns the extracted plugin informations as a dictionary. + This function ensures that "name" and "path" are provided. + """ + # use the filename alone to extract minimal informations. + infos = {} + module_name = os.path.splitext(filename)[0] + plugin_filename = os.path.join(dirpath, filename) + if module_name == "__init__": + module_name = os.path.basename(dirpath) + plugin_filename = dirpath + infos["name"] = "%s" % module_name + infos["path"] = plugin_filename + cf_parser = ConfigParser() + cf_parser.add_section("Core") + cf_parser.set("Core", "Name", infos["name"]) + cf_parser.set("Core", "Module", infos["path"]) + return infos, cf_parser class PluginFileLocator(IPluginLocator): - """ - Locates plugins on the file system using a set of analyzers to - determine what files actually corresponds to plugins. - - If more than one analyzer is being used, the first that will discover a - new plugin will avoid other strategies to find it too. - - By default each directory set as a "plugin place" is scanned - recursively. You can change that by a call to - ``disableRecursiveScan``. - """ - def __init__(self, analyzers=None, plugin_info_cls=PluginInfo): - """ - Defines the strategies, and the places for plugins to look into. - """ - IPluginLocator.__init__(self) - self._discovered_plugins = {} - self.setPluginPlaces(None) - self._analyzers = analyzers # analyzers used to locate plugins - if self._analyzers is None: - self._analyzers = [PluginFileAnalyzerWithInfoFile("info_ext")] - self._default_plugin_info_cls = PluginInfo - self._plugin_info_cls_map = {} - self._max_size = 1e3*1024 # in octets (by default 1 Mo) - self.recursive = True - - def disableRecursiveScan(self): - """ - Disable recursive scan of the directories given as plugin places. - """ - self.recursive = False - - def setAnalyzers(self, analyzers): - """ - Sets a new set of analyzers. - - .. warning:: the new analyzers won't be aware of the plugin - info class that may have been set via a previous - call to ``setPluginInfoClass``. - """ - self._analyzers = analyzers - - def removeAnalyzers(self, name): - """ - Removes analyzers of a given name. - """ - analyzersListCopy = self._analyzers[:] - foundAndRemoved = False - for obj in analyzersListCopy: - if obj.name == name: - self._analyzers.remove(obj) - foundAndRemoved = True - if not foundAndRemoved: - log.debug("'%s' is not a known strategy name: can't remove it." % name) - - def removeAllAnalyzer(self): - """ - Remove all analyzers. - """ - self._analyzers = [] - - def appendAnalyzer(self, analyzer): - """ - Append an analyzer to the existing list. - """ - self._analyzers.append(analyzer) - - - def _getInfoForPluginFromAnalyzer(self,analyzer,dirpath, filename): - """ - Return an instance of plugin_info_cls filled with data extracted by the analyzer. - - May return None if the analyzer fails to extract any info. - """ - plugin_info_dict,config_parser = analyzer.getInfosDictFromPlugin(dirpath, filename) - if plugin_info_dict is None: - return None - plugin_info_cls = self._plugin_info_cls_map.get(analyzer.name,self._default_plugin_info_cls) - plugin_info = plugin_info_cls(plugin_info_dict["name"],plugin_info_dict["path"]) - plugin_info.details = config_parser - return plugin_info - - def locatePlugins(self): - """ - Walk through the plugins' places and look for plugins. - - Return the candidates and number of plugins found. - """ + + """ + Locates plugins on the file system using a set of analyzers to + determine what files actually corresponds to plugins. + + If more than one analyzer is being used, the first that will discover a + new plugin will avoid other strategies to find it too. + + By default each directory set as a "plugin place" is scanned + recursively. You can change that by a call to + ``disableRecursiveScan``. + """ + + def __init__(self, analyzers=None, plugin_info_cls=PluginInfo): + """ + Defines the strategies, and the places for plugins to look into. + """ + IPluginLocator.__init__(self) + self._discovered_plugins = {} + self.setPluginPlaces(None) + self._analyzers = analyzers # analyzers used to locate plugins + if self._analyzers is None: + self._analyzers = [PluginFileAnalyzerWithInfoFile("info_ext")] + self._default_plugin_info_cls = PluginInfo + self._plugin_info_cls_map = {} + self._max_size = 1e3 * 1024 # in octets (by default 1 Mo) + self.recursive = True + + def disableRecursiveScan(self): + """ + Disable recursive scan of the directories given as plugin places. + """ + self.recursive = False + + def setAnalyzers(self, analyzers): + """ + Sets a new set of analyzers. + + .. warning:: the new analyzers won't be aware of the plugin + info class that may have been set via a previous + call to ``setPluginInfoClass``. + """ + self._analyzers = analyzers + + def removeAnalyzers(self, name): + """ + Removes analyzers of a given name. + """ + analyzersListCopy = self._analyzers[:] + foundAndRemoved = False + for obj in analyzersListCopy: + if obj.name == name: + self._analyzers.remove(obj) + foundAndRemoved = True + if not foundAndRemoved: + log.debug( + "'%s' is not a known strategy name: can't remove it." % + name) + + def removeAllAnalyzer(self): + """ + Remove all analyzers. + """ + self._analyzers = [] + + def appendAnalyzer(self, analyzer): + """ + Append an analyzer to the existing list. + """ + self._analyzers.append(analyzer) + + def _getInfoForPluginFromAnalyzer(self, analyzer, dirpath, filename): + """ + Return an instance of plugin_info_cls filled with data extracted by the analyzer. + + May return None if the analyzer fails to extract any info. + """ + plugin_info_dict, config_parser = analyzer.getInfosDictFromPlugin( + dirpath, filename) + if plugin_info_dict is None: + return None + plugin_info_cls = self._plugin_info_cls_map.get( + analyzer.name, + self._default_plugin_info_cls) + plugin_info = plugin_info_cls( + plugin_info_dict["name"], + plugin_info_dict["path"]) + plugin_info.details = config_parser + return plugin_info + + def locatePlugins(self): + """ + Walk through the plugins' places and look for plugins. + + Return the candidates and number of plugins found. + """ # print "%s.locatePlugins" % self.__class__ - _candidates = [] - _discovered = {} - for directory in map(os.path.abspath, self.plugins_places): - # first of all, is it a directory :) - if not os.path.isdir(directory): - log.debug("%s skips %s (not a directory)" % (self.__class__.__name__, directory)) - continue - if self.recursive: - debug_txt_mode = "recursively" - walk_iter = os.walk(directory, followlinks=True) - else: - debug_txt_mode = "non-recursively" - walk_iter = [(directory,[],os.listdir(directory))] - # iteratively walks through the directory - log.debug("%s walks (%s) into directory: %s" % (self.__class__.__name__, debug_txt_mode, directory)) - for item in walk_iter: - dirpath = item[0] - for filename in item[2]: - # print("testing candidate file %s" % filename) - for analyzer in self._analyzers: - # print("... with analyzer %s" % analyzer.name) - # eliminate the obvious non plugin files - if not analyzer.isValidPlugin(filename): - log.debug("%s is not a valid plugin for strategy %s" % (filename, analyzer.name)) - continue - candidate_infofile = os.path.join(dirpath, filename) - if candidate_infofile in _discovered: - log.debug("%s (with strategy %s) rejected because already discovered" % (candidate_infofile, analyzer.name)) - continue - log.debug("%s found a candidate:\n %s" % (self.__class__.__name__, candidate_infofile)) + _candidates = [] + _discovered = {} + for directory in map(os.path.abspath, self.plugins_places): + # first of all, is it a directory :) + if not os.path.isdir(directory): + log.debug( + "%s skips %s (not a directory)" % + (self.__class__.__name__, directory)) + continue + if self.recursive: + debug_txt_mode = "recursively" + walk_iter = os.walk(directory, followlinks=True) + else: + debug_txt_mode = "non-recursively" + walk_iter = [(directory, [], os.listdir(directory))] + # iteratively walks through the directory + log.debug( + "%s walks (%s) into directory: %s" % + (self.__class__.__name__, debug_txt_mode, directory)) + for item in walk_iter: + dirpath = item[0] + for filename in item[2]: + # print("testing candidate file %s" % filename) + for analyzer in self._analyzers: + # print("... with analyzer %s" % analyzer.name) + # eliminate the obvious non plugin files + if not analyzer.isValidPlugin(filename): + log.debug( + "%s is not a valid plugin for strategy %s" % + (filename, analyzer.name)) + continue + candidate_infofile = os.path.join(dirpath, filename) + if candidate_infofile in _discovered: + log.debug( + "%s (with strategy %s) rejected because already discovered" % + (candidate_infofile, analyzer.name)) + continue + log.debug( + "%s found a candidate:\n %s" % + (self.__class__.__name__, candidate_infofile)) # print candidate_infofile - plugin_info = self._getInfoForPluginFromAnalyzer(analyzer, dirpath, filename) - if plugin_info is None: - log.warning("Plugin candidate '%s' rejected by strategy '%s'" % (candidate_infofile, analyzer.name)) - break # we consider this was the good strategy to use for: it failed -> not a plugin -> don't try another strategy - # now determine the path of the file to execute, - # depending on wether the path indicated is a - # directory or a file + plugin_info = self._getInfoForPluginFromAnalyzer( + analyzer, + dirpath, + filename) + if plugin_info is None: + log.warning( + "Plugin candidate '%s' rejected by strategy '%s'" % + (candidate_infofile, analyzer.name)) + # we consider this was the good strategy to use + # for: it failed -> not a plugin -> don't try + # another strategy + break + # now determine the path of the file to execute, + # depending on wether the path indicated is a + # directory or a file # print plugin_info.path - # Remember all the files belonging to a discovered - # plugin, so that strategies (if several in use) won't - # collide - if os.path.isdir(plugin_info.path): - candidate_filepath = os.path.join(plugin_info.path, "__init__") - # it is a package, adds all the files concerned - for _file in os.listdir(plugin_info.path): - if _file.endswith(".py"): - self._discovered_plugins[os.path.join(plugin_info.path, _file)] = candidate_filepath - _discovered[os.path.join(plugin_info.path, _file)] = candidate_filepath - elif (plugin_info.path.endswith(".py") and os.path.isfile(plugin_info.path)) or os.path.isfile(plugin_info.path+".py"): - candidate_filepath = plugin_info.path - if candidate_filepath.endswith(".py"): - candidate_filepath = candidate_filepath[:-3] - # it is a file, adds it - self._discovered_plugins[".".join((plugin_info.path, "py"))] = candidate_filepath - _discovered[".".join((plugin_info.path, "py"))] = candidate_filepath - else: - log.error("Plugin candidate rejected: cannot find the file or directory module for '%s'" % (candidate_infofile)) - break + # Remember all the files belonging to a discovered + # plugin, so that strategies (if several in use) won't + # collide + if os.path.isdir(plugin_info.path): + candidate_filepath = os.path.join( + plugin_info.path, + "__init__") + # it is a package, adds all the files concerned + for _file in os.listdir(plugin_info.path): + if _file.endswith(".py"): + self._discovered_plugins[ + os.path.join( + plugin_info.path, + _file)] = candidate_filepath + _discovered[ + os.path.join( + plugin_info.path, + _file)] = candidate_filepath + elif (plugin_info.path.endswith(".py") and os.path.isfile(plugin_info.path)) or os.path.isfile(plugin_info.path + ".py"): + candidate_filepath = plugin_info.path + if candidate_filepath.endswith(".py"): + candidate_filepath = candidate_filepath[:-3] + # it is a file, adds it + self._discovered_plugins[ + ".".join( + (plugin_info.path, "py"))] = candidate_filepath + _discovered[ + ".".join( + (plugin_info.path, "py"))] = candidate_filepath + else: + log.error( + "Plugin candidate rejected: cannot find the file or directory module for '%s'" % + (candidate_infofile)) + break # print candidate_filepath - _candidates.append((candidate_infofile, candidate_filepath, plugin_info)) - # finally the candidate_infofile must not be discovered again - _discovered[candidate_infofile] = candidate_filepath - self._discovered_plugins[candidate_infofile] = candidate_filepath + _candidates.append( + (candidate_infofile, + candidate_filepath, + plugin_info)) + # finally the candidate_infofile must not be discovered + # again + _discovered[candidate_infofile] = candidate_filepath + self._discovered_plugins[ + candidate_infofile] = candidate_filepath # print "%s found by strategy %s" % (candidate_filepath, analyzer.name) - return _candidates, len(_candidates) - - def gatherCorePluginInfo(self, directory, filename): - """ - Return a ``PluginInfo`` as well as the ``ConfigParser`` used to build it. - - If filename is a valid plugin discovered by any of the known - strategy in use. Returns None,None otherwise. - """ - for analyzer in self._analyzers: - # eliminate the obvious non plugin files - if not analyzer.isValidPlugin(filename): - continue - plugin_info = self._getInfoForPluginFromAnalyzer(analyzer,directory, filename) - return plugin_info,plugin_info.details - return None,None - - # ----------------------------------------------- - # Backward compatible methods - # Note: their implementation must be conform to their - # counterpart in yapsy<1.10 - # ----------------------------------------------- - - def getPluginNameAndModuleFromStream(self, infoFileObject, candidate_infofile=None): - for analyzer in self._analyzers: - if analyzer.name == "info_ext": - return analyzer.getPluginNameAndModuleFromStream(infoFileObject) - else: - raise RuntimeError("No current file analyzer is able to provide plugin information from stream") - - def setPluginInfoClass(self, picls, name=None): - """ - Set the class that holds PluginInfo. The class should inherit - from ``PluginInfo``. - - If name is given, then the class will be used only by the corresponding analyzer. - - If name is None, the class will be set for all analyzers. - """ - if name is None: - self._default_plugin_info_cls = picls - self._plugin_info_cls_map = {} - else: - self._plugin_info_cls_map[name] = picls - - def setPluginPlaces(self, directories_list): - """ - Set the list of directories where to look for plugin places. - """ - if directories_list is None: - directories_list = [os.path.dirname(__file__)] - self.plugins_places = directories_list - - def updatePluginPlaces(self, directories_list): - """ - Updates the list of directories where to look for plugin places. - """ - self.plugins_places = list(set.union(set(directories_list), set(self.plugins_places))) - - def setPluginInfoExtension(self, ext): - """ - DEPRECATED(>1.9): for backward compatibility. Directly configure the - IPluginLocator instance instead ! - - This will only work if the strategy "info_ext" is active - for locating plugins. - """ - for analyzer in self._analyzers: - if analyzer.name == "info_ext": - analyzer.setPluginInfoExtension(ext) + return _candidates, len(_candidates) + + def gatherCorePluginInfo(self, directory, filename): + """ + Return a ``PluginInfo`` as well as the ``ConfigParser`` used to build it. + + If filename is a valid plugin discovered by any of the known + strategy in use. Returns None,None otherwise. + """ + for analyzer in self._analyzers: + # eliminate the obvious non plugin files + if not analyzer.isValidPlugin(filename): + continue + plugin_info = self._getInfoForPluginFromAnalyzer( + analyzer, + directory, + filename) + return plugin_info, plugin_info.details + return None, None + + # ----------------------------------------------- + # Backward compatible methods + # Note: their implementation must be conform to their + # counterpart in yapsy<1.10 + # ----------------------------------------------- + + def getPluginNameAndModuleFromStream( + self, + infoFileObject, + candidate_infofile=None): + for analyzer in self._analyzers: + if analyzer.name == "info_ext": + return analyzer.getPluginNameAndModuleFromStream( + infoFileObject) + else: + raise RuntimeError( + "No current file analyzer is able to provide plugin information from stream") + + def setPluginInfoClass(self, picls, name=None): + """ + Set the class that holds PluginInfo. The class should inherit + from ``PluginInfo``. + + If name is given, then the class will be used only by the corresponding analyzer. + + If name is None, the class will be set for all analyzers. + """ + if name is None: + self._default_plugin_info_cls = picls + self._plugin_info_cls_map = {} + else: + self._plugin_info_cls_map[name] = picls + + def setPluginPlaces(self, directories_list): + """ + Set the list of directories where to look for plugin places. + """ + if directories_list is None: + directories_list = [os.path.dirname(__file__)] + self.plugins_places = directories_list + + def updatePluginPlaces(self, directories_list): + """ + Updates the list of directories where to look for plugin places. + """ + self.plugins_places = list( + set.union( + set(directories_list), set( + self.plugins_places))) + + def setPluginInfoExtension(self, ext): + """ + DEPRECATED(>1.9): for backward compatibility. Directly configure the + IPluginLocator instance instead ! + + This will only work if the strategy "info_ext" is active + for locating plugins. + """ + for analyzer in self._analyzers: + if analyzer.name == "info_ext": + analyzer.setPluginInfoExtension(ext) diff --git a/package/yapsy/PluginInfo.py b/package/yapsy/PluginInfo.py index c3cc8d5..6f69b63 100644 --- a/package/yapsy/PluginInfo.py +++ b/package/yapsy/PluginInfo.py @@ -12,203 +12,197 @@ === """ -from configparser import ConfigParser +from yapsy.compat import ConfigParser, str from distutils.version import StrictVersion class PluginInfo(object): - """Representation of the most basic set of information related to a - given plugin such as its name, author, description... - - Any additional information can be stored ad retrieved in a - PluginInfo, when this one is created with a - ``ConfigParser.ConfigParser`` instance. - - This typically means that when metadata is read from a text file - (the original way for yapsy to describe plugins), all info that is - not part of the basic variables (name, path, version etc), can - still be accessed though the ``details`` member variables that - behaves like Python's ``ConfigParser.ConfigParser``. - - Warning: the instance associated with the ``details`` member - variable is never copied and used to store all plugin infos. If - you set it to a custom instance, it will be modified as soon as - another member variale of the plugin info is - changed. Alternatively, if you change the instance "outside" the - plugin info, it will also change the plugin info. - """ - - def __init__(self, plugin_name, plugin_path): - """ - Set the basic information (at least name and path) about the - plugin as well as the default values for other usefull - variables. - - *plugin_name* is a simple string describing the name of - the plugin. - - *plugin_path* describe the location where the plugin can be - found. - - .. warning:: The ``path`` attribute is the full path to the - plugin if it is organised as a directory or the - full path to a file without the ``.py`` extension - if the plugin is defined by a simple file. In the - later case, the actual plugin is reached via - ``plugin_info.path+'.py'``. - """ - self.__details = ConfigParser() - self.name = plugin_name - self.path = plugin_path - self._ensureDetailsDefaultsAreBackwardCompatible() - # Storage for stuff created during the plugin lifetime - self.plugin_object = None - self.categories = [] - self.error = None - - - def __setDetails(self,cfDetails): - """ - Fill in all details by storing a ``ConfigParser`` instance. - - .. warning: The values for ``plugin_name`` and - ``plugin_path`` given a init time will superseed - any value found in ``cfDetails`` in section - 'Core' for the options 'Name' and 'Module' (this - is mostly for backward compatibility). - """ - bkp_name = self.name - bkp_path = self.path - self.__details = cfDetails - self.name = bkp_name - self.path = bkp_path - self._ensureDetailsDefaultsAreBackwardCompatible() - - def __getDetails(self): - return self.__details - - def __getName(self): - return self.details.get("Core","Name") - - def __setName(self, name): - if not self.details.has_section("Core"): - self.details.add_section("Core") - self.details.set("Core","Name",name) - - - def __getPath(self): - return self.details.get("Core","Module") - - def __setPath(self,path): - if not self.details.has_section("Core"): - self.details.add_section("Core") - self.details.set("Core","Module",path) - - - def __getVersion(self): - return StrictVersion(self.details.get("Documentation","Version")) - - def setVersion(self, vstring): - """ - Set the version of the plugin. - - Used by subclasses to provide different handling of the - version number. - """ - if isinstance(vstring,StrictVersion): - vstring = str(vstring) - if not self.details.has_section("Documentation"): - self.details.add_section("Documentation") - self.details.set("Documentation","Version",vstring) - - def __getAuthor(self): - return self.details.get("Documentation","Author") - - def __setAuthor(self,author): - if not self.details.has_section("Documentation"): - self.details.add_section("Documentation") - self.details.set("Documentation","Author",author) - - - def __getCopyright(self): - return self.details.get("Documentation","Copyright") - - def __setCopyright(self,copyrightTxt): - if not self.details.has_section("Documentation"): - self.details.add_section("Documentation") - self.details.set("Documentation","Copyright",copyrightTxt) - - - def __getWebsite(self): - return self.details.get("Documentation","Website") - - def __setWebsite(self,website): - if not self.details.has_section("Documentation"): - self.details.add_section("Documentation") - self.details.set("Documentation","Website",website) - - - def __getDescription(self): - return self.details.get("Documentation","Description") - - def __setDescription(self,description): - if not self.details.has_section("Documentation"): - self.details.add_section("Documentation") - return self.details.set("Documentation","Description",description) - - - def __getCategory(self): - """ - DEPRECATED (>1.9): Mimic former behaviour when what is - noz the first category was considered as the only one the - plugin belonged to. - """ - if self.categories: - return self.categories[0] - else: - return "UnknownCategory" - - def __setCategory(self,c): - """ - DEPRECATED (>1.9): Mimic former behaviour by making so - that if a category is set as it it was the only category to - which the plugin belongs, then a __getCategory will return - this newly set category. - """ - self.categories = [c] + self.categories - - name = property(fget=__getName,fset=__setName) - path = property(fget=__getPath,fset=__setPath) - version = property(fget=__getVersion,fset=setVersion) - author = property(fget=__getAuthor,fset=__setAuthor) - copyright = property(fget=__getCopyright,fset=__setCopyright) - website = property(fget=__getWebsite,fset=__setWebsite) - description = property(fget=__getDescription,fset=__setDescription) - details = property(fget=__getDetails,fset=__setDetails) - # deprecated (>1.9): plugins are not longer associated to a - # single category ! - category = property(fget=__getCategory,fset=__setCategory) - - def _getIsActivated(self): - """ - Return the activated state of the plugin object. - Makes it possible to define a property. - """ - return self.plugin_object.is_activated - - is_activated = property(fget=_getIsActivated) - - def _ensureDetailsDefaultsAreBackwardCompatible(self): - """ - Internal helper function. - """ - if not self.details.has_option("Documentation","Author"): - self.author = "Unknown" - if not self.details.has_option("Documentation","Version"): - self.version = "0.0" - if not self.details.has_option("Documentation","Website"): - self.website = "None" - if not self.details.has_option("Documentation","Copyright"): - self.copyright = "Unknown" - if not self.details.has_option("Documentation","Description"): - self.description = "" + + """Representation of the most basic set of information related to a + given plugin such as its name, author, description... + + Any additional information can be stored ad retrieved in a + PluginInfo, when this one is created with a + ``ConfigParser.ConfigParser`` instance. + + This typically means that when metadata is read from a text file + (the original way for yapsy to describe plugins), all info that is + not part of the basic variables (name, path, version etc), can + still be accessed though the ``details`` member variables that + behaves like Python's ``ConfigParser.ConfigParser``. + + Warning: the instance associated with the ``details`` member + variable is never copied and used to store all plugin infos. If + you set it to a custom instance, it will be modified as soon as + another member variale of the plugin info is + changed. Alternatively, if you change the instance "outside" the + plugin info, it will also change the plugin info. + """ + + def __init__(self, plugin_name, plugin_path): + """ + Set the basic information (at least name and path) about the + plugin as well as the default values for other usefull + variables. + + *plugin_name* is a simple string describing the name of + the plugin. + + *plugin_path* describe the location where the plugin can be + found. + + .. warning:: The ``path`` attribute is the full path to the + plugin if it is organised as a directory or the + full path to a file without the ``.py`` extension + if the plugin is defined by a simple file. In the + later case, the actual plugin is reached via + ``plugin_info.path+'.py'``. + """ + self.__details = ConfigParser() + self.name = plugin_name + self.path = plugin_path + self._ensureDetailsDefaultsAreBackwardCompatible() + # Storage for stuff created during the plugin lifetime + self.plugin_object = None + self.categories = [] + self.error = None + + def __setDetails(self, cfDetails): + """ + Fill in all details by storing a ``ConfigParser`` instance. + + .. warning: The values for ``plugin_name`` and + ``plugin_path`` given a init time will superseed + any value found in ``cfDetails`` in section + 'Core' for the options 'Name' and 'Module' (this + is mostly for backward compatibility). + """ + bkp_name = self.name + bkp_path = self.path + self.__details = cfDetails + self.name = bkp_name + self.path = bkp_path + self._ensureDetailsDefaultsAreBackwardCompatible() + + def __getDetails(self): + return self.__details + + def __getName(self): + return self.details.get("Core", "Name") + + def __setName(self, name): + if not self.details.has_section("Core"): + self.details.add_section("Core") + self.details.set("Core", "Name", name) + + def __getPath(self): + return self.details.get("Core", "Module") + + def __setPath(self, path): + if not self.details.has_section("Core"): + self.details.add_section("Core") + self.details.set("Core", "Module", path) + + def __getVersion(self): + return StrictVersion(self.details.get("Documentation", "Version")) + + def setVersion(self, vstring): + """ + Set the version of the plugin. + + Used by subclasses to provide different handling of the + version number. + """ + if isinstance(vstring, StrictVersion): + vstring = str(vstring) + if not self.details.has_section("Documentation"): + self.details.add_section("Documentation") + self.details.set("Documentation", "Version", vstring) + + def __getAuthor(self): + return self.details.get("Documentation", "Author") + + def __setAuthor(self, author): + if not self.details.has_section("Documentation"): + self.details.add_section("Documentation") + self.details.set("Documentation", "Author", author) + + def __getCopyright(self): + return self.details.get("Documentation", "Copyright") + + def __setCopyright(self, copyrightTxt): + if not self.details.has_section("Documentation"): + self.details.add_section("Documentation") + self.details.set("Documentation", "Copyright", copyrightTxt) + + def __getWebsite(self): + return self.details.get("Documentation", "Website") + + def __setWebsite(self, website): + if not self.details.has_section("Documentation"): + self.details.add_section("Documentation") + self.details.set("Documentation", "Website", website) + + def __getDescription(self): + return self.details.get("Documentation", "Description") + + def __setDescription(self, description): + if not self.details.has_section("Documentation"): + self.details.add_section("Documentation") + return self.details.set("Documentation", "Description", description) + + def __getCategory(self): + """ + DEPRECATED (>1.9): Mimic former behaviour when what is + noz the first category was considered as the only one the + plugin belonged to. + """ + if self.categories: + return self.categories[0] + else: + return "UnknownCategory" + + def __setCategory(self, c): + """ + DEPRECATED (>1.9): Mimic former behaviour by making so + that if a category is set as it it was the only category to + which the plugin belongs, then a __getCategory will return + this newly set category. + """ + self.categories = [c] + self.categories + + name = property(fget=__getName, fset=__setName) + path = property(fget=__getPath, fset=__setPath) + version = property(fget=__getVersion, fset=setVersion) + author = property(fget=__getAuthor, fset=__setAuthor) + copyright = property(fget=__getCopyright, fset=__setCopyright) + website = property(fget=__getWebsite, fset=__setWebsite) + description = property(fget=__getDescription, fset=__setDescription) + details = property(fget=__getDetails, fset=__setDetails) + # deprecated (>1.9): plugins are not longer associated to a + # single category ! + category = property(fget=__getCategory, fset=__setCategory) + + def _getIsActivated(self): + """ + Return the activated state of the plugin object. + Makes it possible to define a property. + """ + return self.plugin_object.is_activated + + is_activated = property(fget=_getIsActivated) + + def _ensureDetailsDefaultsAreBackwardCompatible(self): + """ + Internal helper function. + """ + if not self.details.has_option("Documentation", "Author"): + self.author = "Unknown" + if not self.details.has_option("Documentation", "Version"): + self.version = "0.0" + if not self.details.has_option("Documentation", "Website"): + self.website = "None" + if not self.details.has_option("Documentation", "Copyright"): + self.copyright = "Unknown" + if not self.details.has_option("Documentation", "Description"): + self.description = "" diff --git a/package/yapsy/PluginManager.py b/package/yapsy/PluginManager.py index 9ac966b..525599d 100644 --- a/package/yapsy/PluginManager.py +++ b/package/yapsy/PluginManager.py @@ -27,12 +27,12 @@ For a *Standard* plugin: - ``myplugin.yapsy-plugin`` - + ``myplugin.yapsy-plugin`` + A *plugin info file* identical to the one previously described. - + ``myplugin`` - + A directory ontaining an actual Python plugin (ie with a ``__init__.py`` file that makes it importable). The upper namespace of the plugin should present a class inheriting the @@ -42,18 +42,18 @@ For a *Single file* plugin: - ``myplugin.yapsy-plugin`` - + ``myplugin.yapsy-plugin`` + A *plugin info file* which is identified thanks to its extension, see the `Plugin Info File Format`_ to see what should be in this file. - + The extension is customisable at the ``PluginManager``'s instanciation, since one may usually prefer the extension to bear the application name. - + ``myplugin.py`` - + The source of the plugin. This file should at least define a class inheriting the ``IPlugin`` interface. This class will be instanciated at plugin loading and it will be notified the @@ -77,15 +77,15 @@ [Core] Name = My plugin Name Module = the_name_of_the_pluginto_load_with_no_py_ending - + [Documentation] Description = What my plugin broadly does Author = My very own name Version = the_version_number_of_the_plugin Website = My very own website - - - + + + .. note:: From such plugin descriptions, the ``PluginManager`` will built its own representations of the plugins as instances of the :doc:`PluginInfo` class. @@ -124,7 +124,7 @@ API === - + """ import sys @@ -148,516 +148,557 @@ class PluginManager(object): - """ - Manage several plugins by ordering them in categories. - - The mechanism for searching and loading the plugins is already - implemented in this class so that it can be used directly (hence - it can be considered as a bit more than a mere interface) - - The file describing a plugin must be written in the syntax - compatible with Python's ConfigParser module as in the - `Plugin Info File Format`_ - """ - - def __init__(self, - categories_filter=None, - directories_list=None, - plugin_info_ext=None, - plugin_locator=None): - """ - Initialize the mapping of the categories and set the list of - directories where plugins may be. This can also be set by - direct call the methods: - - - ``setCategoriesFilter`` for ``categories_filter`` - - ``setPluginPlaces`` for ``directories_list`` - - ``setPluginInfoExtension`` for ``plugin_info_ext`` - - You may look at these function's documentation for the meaning - of each corresponding arguments. - """ - # as a good practice we don't use mutable objects as default - # values (these objects would become like static variables) - # for function/method arguments, but rather use None. - if categories_filter is None: - categories_filter = {"Default":IPlugin} - self.setCategoriesFilter(categories_filter) - plugin_locator = self._locatorDecide(plugin_info_ext, plugin_locator) - # plugin_locator could be either a dict defining strategies, or directly - # an IPluginLocator object - self.setPluginLocator(plugin_locator, directories_list) - - def _locatorDecide(self, plugin_info_ext, plugin_locator): - """ - For backward compatibility, we kept the *plugin_info_ext* argument. - Thus we may use it if provided. Returns the (possibly modified) - *plugin_locator*. - """ - specific_info_ext = plugin_info_ext is not None - specific_locator = plugin_locator is not None - if not specific_info_ext and not specific_locator: - # use the default behavior - res = PluginFileLocator() - elif not specific_info_ext and specific_locator: - # plugin_info_ext not used - res = plugin_locator - elif not specific_locator and specific_info_ext: - # plugin_locator not used, and plugin_info_ext provided - # -> compatibility mode - res = PluginFileLocator() - res.setAnalyzers([PluginFileAnalyzerWithInfoFile("info_ext",plugin_info_ext)]) - elif specific_info_ext and specific_locator: - # both provided... issue a warning that tells "plugin_info_ext" - # will be ignored - msg = ("Two incompatible arguments (%s) provided:", - "'plugin_info_ext' and 'plugin_locator'). Ignoring", - "'plugin_info_ext'.") - raise ValueError(" ".join(msg) % self.__class__.__name__) - return res - - def setCategoriesFilter(self, categories_filter): - """ - Set the categories of plugins to be looked for as well as the - way to recognise them. - - The ``categories_filter`` first defines the various categories - in which the plugins will be stored via its keys and it also - defines the interface tha has to be inherited by the actual - plugin class belonging to each category. - """ - self.categories_interfaces = categories_filter.copy() - # prepare the mapping from categories to plugin lists - self.category_mapping = {} - # also maps the plugin info files (useful to avoid loading - # twice the same plugin...) - self._category_file_mapping = {} - for categ in categories_filter: - self.category_mapping[categ] = [] - self._category_file_mapping[categ] = [] - - - def setPluginPlaces(self, directories_list): - """ - DEPRECATED(>1.9): directly configure the IPluginLocator instance instead ! - - Convenience method (actually call the IPluginLocator method) - """ - self.getPluginLocator().setPluginPlaces(directories_list) - - def updatePluginPlaces(self, directories_list): - """ - DEPRECATED(>1.9): directly configure the IPluginLocator instance instead ! - - Convenience method (actually call the IPluginLocator method) - """ - self.getPluginLocator().updatePluginPlaces(directories_list) - - def setPluginInfoExtension(self, ext): - """ - DEPRECATED(>1.9): for backward compatibility. Directly configure the - IPluginLocator instance instead ! - - .. warning:: This will only work if the strategy "info_ext" is - active for locating plugins. - """ - try: - self.getPluginLocator().setPluginInfoExtension(ext) - except KeyError: - log.error("Current plugin locator doesn't support setting the plugin info extension.") - - def setPluginInfoClass(self, picls, strategies=None): - """ - DEPRECATED(>1.9): directly configure the IPluginLocator instance instead ! - - Convenience method (actually call self.getPluginLocator().setPluginInfoClass) - - When using a ``PluginFileLocator`` you may restrict the - strategies to which the change of PluginInfo class will occur - by just giving the list of strategy names in the argument - "strategies" - """ - if strategies: - for name in strategies: - self.getPluginLocator().setPluginInfoClass(picls, name) - else: - self.getPluginLocator().setPluginInfoClass(picls) - - def getPluginInfoClass(self): - """ - DEPRECATED(>1.9): directly control that with the IPluginLocator - instance instead ! - - Get the class that holds PluginInfo. - """ - return self.getPluginLocator().getPluginInfoClass() - - def setPluginLocator(self, plugin_locator, dir_list=None, picls=None): - """ - Sets the strategy used to locate the basic information. - - See ``IPluginLocator`` for the policy that plugin_locator must enforce. - """ - if isinstance(plugin_locator, IPluginLocator): - self._plugin_locator = plugin_locator - if dir_list is not None: - self._plugin_locator.updatePluginPlaces(dir_list) - if picls is not None: - self.setPluginInfoClass(picls) - else: - raise TypeError("Unexpected format for plugin_locator ('%s' is not an instance of IPluginLocator)" % plugin_locator) - - def getPluginLocator(self): - """ - Grant direct access to the plugin locator. - """ - return self._plugin_locator - - def _gatherCorePluginInfo(self, directory, plugin_info_filename): - """ - DEPRECATED(>1.9): please use a specific plugin - locator if you need such information. - - Gather the core information (name, and module to be loaded) - about a plugin described by it's info file (found at - 'directory/filename'). - - Return an instance of ``PluginInfo`` and the - config_parser used to gather the core data *in a tuple*, if the - required info could be localised, else return ``(None,None)``. - - .. note:: This is supposed to be used internally by subclasses - and decorators. - - """ - return self.getPluginLocator().gatherCorePluginInfo(directory,plugin_info_filename) - - def _getPluginNameAndModuleFromStream(self,infoFileObject,candidate_infofile=""): - """ - DEPRECATED(>1.9): please use a specific plugin - locator if you need such information. - - Extract the name and module of a plugin from the - content of the info file that describes it and which - is stored in infoFileObject. - - .. note:: Prefer using ``_gatherCorePluginInfo`` - instead, whenever possible... - - .. warning:: ``infoFileObject`` must be a file-like - object: either an opened file for instance or a string - buffer wrapped in a StringIO instance as another - example. - - .. note:: ``candidate_infofile`` must be provided - whenever possible to get better error messages. - - Return a 3-uple with the name of the plugin, its - module and the config_parser used to gather the core - data *in a tuple*, if the required info could be - localised, else return ``(None,None,None)``. - - .. note:: This is supposed to be used internally by subclasses - and decorators. - """ - return self.getPluginLocator().getPluginNameAndModuleFromStream(infoFileObject, candidate_infofile) - - - def getCategories(self): - """ - Return the list of all categories. - """ - return list(self.category_mapping.keys()) - - def removePluginFromCategory(self, plugin,category_name): - """ - Remove a plugin from the category where it's assumed to belong. - """ - self.category_mapping[category_name].remove(plugin) - - - def appendPluginToCategory(self, plugin, category_name): - """ - Append a new plugin to the given category. - """ - self.category_mapping[category_name].append(plugin) - - def getPluginsOfCategory(self, category_name): - """ - Return the list of all plugins belonging to a category. - """ - return self.category_mapping[category_name][:] - - def getAllPlugins(self): - """ - Return the list of all plugins (belonging to all categories). - """ - allPlugins = set() - for pluginsOfOneCategory in self.category_mapping.values(): - allPlugins.update(pluginsOfOneCategory) - return list(allPlugins) - - def getPluginCandidates(self): - """ - Return the list of possible plugins. - - Each possible plugin (ie a candidate) is described by a 3-uple: - (info file path, python file path, plugin info instance) - - .. warning: locatePlugins must be called before ! - """ - if not hasattr(self, '_candidates'): - raise RuntimeError("locatePlugins must be called before getPluginCandidates") - return self._candidates[:] - - def removePluginCandidate(self,candidateTuple): - """ - Remove a given candidate from the list of plugins that should be loaded. - - The candidate must be represented by the same tuple described - in ``getPluginCandidates``. - - .. warning: locatePlugins must be called before ! - """ - if not hasattr(self, '_candidates'): - raise ValueError("locatePlugins must be called before removePluginCandidate") - self._candidates.remove(candidateTuple) - - def appendPluginCandidate(self, candidateTuple): - """ - Append a new candidate to the list of plugins that should be loaded. - - The candidate must be represented by the same tuple described - in ``getPluginCandidates``. - - .. warning: locatePlugins must be called before ! - """ - if not hasattr(self, '_candidates'): - raise ValueError("locatePlugins must be called before removePluginCandidate") - self._candidates.append(candidateTuple) - - def locatePlugins(self): - """ - Convenience method (actually call the IPluginLocator method) - """ - self._candidates, npc = self.getPluginLocator().locatePlugins() - - def loadPlugins(self, callback=None): - """ - Load the candidate plugins that have been identified through a - previous call to locatePlugins. For each plugin candidate - look for its category, load it and store it in the appropriate - slot of the ``category_mapping``. - - If a callback function is specified, call it before every load - attempt. The ``plugin_info`` instance is passed as an argument to - the callback. - """ + + """ + Manage several plugins by ordering them in categories. + + The mechanism for searching and loading the plugins is already + implemented in this class so that it can be used directly (hence + it can be considered as a bit more than a mere interface) + + The file describing a plugin must be written in the syntax + compatible with Python's ConfigParser module as in the + `Plugin Info File Format`_ + """ + + def __init__(self, + categories_filter=None, + directories_list=None, + plugin_info_ext=None, + plugin_locator=None): + """ + Initialize the mapping of the categories and set the list of + directories where plugins may be. This can also be set by + direct call the methods: + + - ``setCategoriesFilter`` for ``categories_filter`` + - ``setPluginPlaces`` for ``directories_list`` + - ``setPluginInfoExtension`` for ``plugin_info_ext`` + + You may look at these function's documentation for the meaning + of each corresponding arguments. + """ + # as a good practice we don't use mutable objects as default + # values (these objects would become like static variables) + # for function/method arguments, but rather use None. + if categories_filter is None: + categories_filter = {"Default": IPlugin} + self.setCategoriesFilter(categories_filter) + plugin_locator = self._locatorDecide(plugin_info_ext, plugin_locator) + # plugin_locator could be either a dict defining strategies, or directly + # an IPluginLocator object + self.setPluginLocator(plugin_locator, directories_list) + + def _locatorDecide(self, plugin_info_ext, plugin_locator): + """ + For backward compatibility, we kept the *plugin_info_ext* argument. + Thus we may use it if provided. Returns the (possibly modified) + *plugin_locator*. + """ + specific_info_ext = plugin_info_ext is not None + specific_locator = plugin_locator is not None + if not specific_info_ext and not specific_locator: + # use the default behavior + res = PluginFileLocator() + elif not specific_info_ext and specific_locator: + # plugin_info_ext not used + res = plugin_locator + elif not specific_locator and specific_info_ext: + # plugin_locator not used, and plugin_info_ext provided + # -> compatibility mode + res = PluginFileLocator() + res.setAnalyzers( + [PluginFileAnalyzerWithInfoFile("info_ext", plugin_info_ext)]) + elif specific_info_ext and specific_locator: + # both provided... issue a warning that tells "plugin_info_ext" + # will be ignored + msg = ("Two incompatible arguments (%s) provided:", + "'plugin_info_ext' and 'plugin_locator'). Ignoring", + "'plugin_info_ext'.") + raise ValueError(" ".join(msg) % self.__class__.__name__) + return res + + def setCategoriesFilter(self, categories_filter): + """ + Set the categories of plugins to be looked for as well as the + way to recognise them. + + The ``categories_filter`` first defines the various categories + in which the plugins will be stored via its keys and it also + defines the interface tha has to be inherited by the actual + plugin class belonging to each category. + """ + self.categories_interfaces = categories_filter.copy() + # prepare the mapping from categories to plugin lists + self.category_mapping = {} + # also maps the plugin info files (useful to avoid loading + # twice the same plugin...) + self._category_file_mapping = {} + for categ in categories_filter: + self.category_mapping[categ] = [] + self._category_file_mapping[categ] = [] + + def setPluginPlaces(self, directories_list): + """ + DEPRECATED(>1.9): directly configure the IPluginLocator instance instead ! + + Convenience method (actually call the IPluginLocator method) + """ + self.getPluginLocator().setPluginPlaces(directories_list) + + def updatePluginPlaces(self, directories_list): + """ + DEPRECATED(>1.9): directly configure the IPluginLocator instance instead ! + + Convenience method (actually call the IPluginLocator method) + """ + self.getPluginLocator().updatePluginPlaces(directories_list) + + def setPluginInfoExtension(self, ext): + """ + DEPRECATED(>1.9): for backward compatibility. Directly configure the + IPluginLocator instance instead ! + + .. warning:: This will only work if the strategy "info_ext" is + active for locating plugins. + """ + try: + self.getPluginLocator().setPluginInfoExtension(ext) + except KeyError: + log.error( + "Current plugin locator doesn't support setting the plugin info extension.") + + def setPluginInfoClass(self, picls, strategies=None): + """ + DEPRECATED(>1.9): directly configure the IPluginLocator instance instead ! + + Convenience method (actually call self.getPluginLocator().setPluginInfoClass) + + When using a ``PluginFileLocator`` you may restrict the + strategies to which the change of PluginInfo class will occur + by just giving the list of strategy names in the argument + "strategies" + """ + if strategies: + for name in strategies: + self.getPluginLocator().setPluginInfoClass(picls, name) + else: + self.getPluginLocator().setPluginInfoClass(picls) + + def getPluginInfoClass(self): + """ + DEPRECATED(>1.9): directly control that with the IPluginLocator + instance instead ! + + Get the class that holds PluginInfo. + """ + return self.getPluginLocator().getPluginInfoClass() + + def setPluginLocator(self, plugin_locator, dir_list=None, picls=None): + """ + Sets the strategy used to locate the basic information. + + See ``IPluginLocator`` for the policy that plugin_locator must enforce. + """ + if isinstance(plugin_locator, IPluginLocator): + self._plugin_locator = plugin_locator + if dir_list is not None: + self._plugin_locator.updatePluginPlaces(dir_list) + if picls is not None: + self.setPluginInfoClass(picls) + else: + raise TypeError( + "Unexpected format for plugin_locator ('%s' is not an instance of IPluginLocator)" % + plugin_locator) + + def getPluginLocator(self): + """ + Grant direct access to the plugin locator. + """ + return self._plugin_locator + + def _gatherCorePluginInfo(self, directory, plugin_info_filename): + """ + DEPRECATED(>1.9): please use a specific plugin + locator if you need such information. + + Gather the core information (name, and module to be loaded) + about a plugin described by it's info file (found at + 'directory/filename'). + + Return an instance of ``PluginInfo`` and the + config_parser used to gather the core data *in a tuple*, if the + required info could be localised, else return ``(None,None)``. + + .. note:: This is supposed to be used internally by subclasses + and decorators. + + """ + return self.getPluginLocator().gatherCorePluginInfo( + directory, + plugin_info_filename) + + def _getPluginNameAndModuleFromStream( + self, + infoFileObject, + candidate_infofile=""): + """ + DEPRECATED(>1.9): please use a specific plugin + locator if you need such information. + + Extract the name and module of a plugin from the + content of the info file that describes it and which + is stored in infoFileObject. + + .. note:: Prefer using ``_gatherCorePluginInfo`` + instead, whenever possible... + + .. warning:: ``infoFileObject`` must be a file-like + object: either an opened file for instance or a string + buffer wrapped in a StringIO instance as another + example. + + .. note:: ``candidate_infofile`` must be provided + whenever possible to get better error messages. + + Return a 3-uple with the name of the plugin, its + module and the config_parser used to gather the core + data *in a tuple*, if the required info could be + localised, else return ``(None,None,None)``. + + .. note:: This is supposed to be used internally by subclasses + and decorators. + """ + return self.getPluginLocator().getPluginNameAndModuleFromStream( + infoFileObject, + candidate_infofile) + + def getCategories(self): + """ + Return the list of all categories. + """ + return list(self.category_mapping.keys()) + + def removePluginFromCategory(self, plugin, category_name): + """ + Remove a plugin from the category where it's assumed to belong. + """ + self.category_mapping[category_name].remove(plugin) + + def appendPluginToCategory(self, plugin, category_name): + """ + Append a new plugin to the given category. + """ + self.category_mapping[category_name].append(plugin) + + def getPluginsOfCategory(self, category_name): + """ + Return the list of all plugins belonging to a category. + """ + return self.category_mapping[category_name][:] + + def getAllPlugins(self): + """ + Return the list of all plugins (belonging to all categories). + """ + allPlugins = set() + for pluginsOfOneCategory in self.category_mapping.values(): + allPlugins.update(pluginsOfOneCategory) + return list(allPlugins) + + def getPluginCandidates(self): + """ + Return the list of possible plugins. + + Each possible plugin (ie a candidate) is described by a 3-uple: + (info file path, python file path, plugin info instance) + + .. warning: locatePlugins must be called before ! + """ + if not hasattr(self, '_candidates'): + raise RuntimeError( + "locatePlugins must be called before getPluginCandidates") + return self._candidates[:] + + def removePluginCandidate(self, candidateTuple): + """ + Remove a given candidate from the list of plugins that should be loaded. + + The candidate must be represented by the same tuple described + in ``getPluginCandidates``. + + .. warning: locatePlugins must be called before ! + """ + if not hasattr(self, '_candidates'): + raise ValueError( + "locatePlugins must be called before removePluginCandidate") + self._candidates.remove(candidateTuple) + + def appendPluginCandidate(self, candidateTuple): + """ + Append a new candidate to the list of plugins that should be loaded. + + The candidate must be represented by the same tuple described + in ``getPluginCandidates``. + + .. warning: locatePlugins must be called before ! + """ + if not hasattr(self, '_candidates'): + raise ValueError( + "locatePlugins must be called before removePluginCandidate") + self._candidates.append(candidateTuple) + + def locatePlugins(self): + """ + Convenience method (actually call the IPluginLocator method) + """ + self._candidates, npc = self.getPluginLocator().locatePlugins() + + def loadPlugins(self, callback=None): + """ + Load the candidate plugins that have been identified through a + previous call to locatePlugins. For each plugin candidate + look for its category, load it and store it in the appropriate + slot of the ``category_mapping``. + + If a callback function is specified, call it before every load + attempt. The ``plugin_info`` instance is passed as an argument to + the callback. + """ # print "%s.loadPlugins" % self.__class__ - if not hasattr(self, '_candidates'): - raise ValueError("locatePlugins must be called before loadPlugins") - - processed_plugins = [] - for candidate_infofile, candidate_filepath, plugin_info in self._candidates: - # make sure to attribute a unique module name to the one - # that is about to be loaded - plugin_module_name_template = NormalizePluginNameForModuleName("yapsy_loaded_plugin_" + plugin_info.name) + "_%d" - for plugin_name_suffix in range(len(sys.modules)): - plugin_module_name = plugin_module_name_template % plugin_name_suffix - if plugin_module_name not in sys.modules: - break - - # tolerance on the presence (or not) of the py extensions - if candidate_filepath.endswith(".py"): - candidate_filepath = candidate_filepath[:-3] - # if a callback exists, call it before attempting to load - # the plugin so that a message can be displayed to the - # user - if callback is not None: - callback(plugin_info) - # cover the case when the __init__ of a package has been - # explicitely indicated - if "__init__" in os.path.basename(candidate_filepath): - candidate_filepath = os.path.dirname(candidate_filepath) - try: - # use imp to correctly load the plugin as a module - if os.path.isdir(candidate_filepath): - candidate_module = imp.load_module(plugin_module_name,None,candidate_filepath,("py","r",imp.PKG_DIRECTORY)) - else: - with open(candidate_filepath+".py","r") as plugin_file: - candidate_module = imp.load_module(plugin_module_name,plugin_file,candidate_filepath+".py",("py","r",imp.PY_SOURCE)) - except Exception: - exc_info = sys.exc_info() - log.error("Unable to import plugin: %s" % candidate_filepath, exc_info=exc_info) - plugin_info.error = exc_info - processed_plugins.append(plugin_info) - continue - processed_plugins.append(plugin_info) - if "__init__" in os.path.basename(candidate_filepath): - sys.path.remove(plugin_info.path) - # now try to find and initialise the first subclass of the correct plugin interface - for element in (getattr(candidate_module,name) for name in dir(candidate_module)): - plugin_info_reference = None - for category_name in self.categories_interfaces: - try: - is_correct_subclass = issubclass(element, self.categories_interfaces[category_name]) - except TypeError: - continue - if is_correct_subclass and element is not self.categories_interfaces[category_name]: - current_category = category_name - if candidate_infofile not in self._category_file_mapping[current_category]: - # we found a new plugin: initialise it and search for the next one - if not plugin_info_reference: - plugin_info.plugin_object = element() - plugin_info_reference = plugin_info - plugin_info.categories.append(current_category) - self.category_mapping[current_category].append(plugin_info_reference) - self._category_file_mapping[current_category].append(candidate_infofile) - # Remove candidates list since we don't need them any more and - # don't need to take up the space - delattr(self, '_candidates') - return processed_plugins - - def collectPlugins(self): - """ - Walk through the plugins' places and look for plugins. Then - for each plugin candidate look for its category, load it and - stores it in the appropriate slot of the category_mapping. - """ -# print "%s.collectPlugins" % self.__class__ - self.locatePlugins() - self.loadPlugins() - - - def getPluginByName(self,name,category="Default"): - """ - Get the plugin correspoding to a given category and name - """ - if category in self.category_mapping: - for item in self.category_mapping[category]: - if item.name == name: - return item - return None - - def activatePluginByName(self,name,category="Default"): - """ - Activate a plugin corresponding to a given category + name. - """ - pta_item = self.getPluginByName(name,category) - if pta_item is not None: - plugin_to_activate = pta_item.plugin_object - if plugin_to_activate is not None: - log.debug("Activating plugin: %s.%s"% (category,name)) - plugin_to_activate.activate() - return plugin_to_activate - return None - - - def deactivatePluginByName(self,name,category="Default"): - """ - Desactivate a plugin corresponding to a given category + name. - """ - if category in self.category_mapping: - plugin_to_deactivate = None - for item in self.category_mapping[category]: - if item.name == name: - plugin_to_deactivate = item.plugin_object - break - if plugin_to_deactivate is not None: - log.debug("Deactivating plugin: %s.%s"% (category,name)) - plugin_to_deactivate.deactivate() - return plugin_to_deactivate - return None + if not hasattr(self, '_candidates'): + raise ValueError("locatePlugins must be called before loadPlugins") + + processed_plugins = [] + for candidate_infofile, candidate_filepath, plugin_info in self._candidates: + # make sure to attribute a unique module name to the one + # that is about to be loaded + plugin_module_name_template = NormalizePluginNameForModuleName( + "yapsy_loaded_plugin_" + plugin_info.name) + "_%d" + for plugin_name_suffix in range(len(sys.modules)): + plugin_module_name = plugin_module_name_template % plugin_name_suffix + if plugin_module_name not in sys.modules: + break + + # tolerance on the presence (or not) of the py extensions + if candidate_filepath.endswith(".py"): + candidate_filepath = candidate_filepath[:-3] + # if a callback exists, call it before attempting to load + # the plugin so that a message can be displayed to the + # user + if callback is not None: + callback(plugin_info) + # cover the case when the __init__ of a package has been + # explicitely indicated + if "__init__" in os.path.basename(candidate_filepath): + candidate_filepath = os.path.dirname(candidate_filepath) + try: + # use imp to correctly load the plugin as a module + if os.path.isdir(candidate_filepath): + candidate_module = imp.load_module( + plugin_module_name, + None, + candidate_filepath, + ("py", + "r", + imp.PKG_DIRECTORY)) + else: + with open(candidate_filepath + ".py", "r") as plugin_file: + candidate_module = imp.load_module( + plugin_module_name, + plugin_file, + candidate_filepath + + ".py", + ("py", + "r", + imp.PY_SOURCE)) + except Exception: + exc_info = sys.exc_info() + log.error( + "Unable to import plugin: %s" % + candidate_filepath, + exc_info=exc_info) + plugin_info.error = exc_info + processed_plugins.append(plugin_info) + continue + processed_plugins.append(plugin_info) + if "__init__" in os.path.basename(candidate_filepath): + sys.path.remove(plugin_info.path) + # now try to find and initialise the first subclass of the correct + # plugin interface + for element in ( + getattr( + candidate_module, + name) for name in dir(candidate_module)): + plugin_info_reference = None + for category_name in self.categories_interfaces: + try: + is_correct_subclass = issubclass( + element, + self.categories_interfaces[category_name]) + # XXX here we also get RunTimeError if running in a Flask + # environment + except Exception: + continue + if is_correct_subclass and element is not self.categories_interfaces[ + category_name]: + current_category = category_name + if candidate_infofile not in self._category_file_mapping[ + current_category]: + # we found a new plugin: initialise it and search + # for the next one + if not plugin_info_reference: + plugin_info.plugin_object = element() + plugin_info_reference = plugin_info + plugin_info.categories.append(current_category) + self.category_mapping[current_category].append( + plugin_info_reference) + self._category_file_mapping[ + current_category].append(candidate_infofile) + # Remove candidates list since we don't need them any more and + # don't need to take up the space + delattr(self, '_candidates') + return processed_plugins + + def collectPlugins(self): + """ + Walk through the plugins' places and look for plugins. Then + for each plugin candidate look for its category, load it and + stores it in the appropriate slot of the category_mapping. + """ +# print "%s.collectPlugins" % self.__class__ + self.locatePlugins() + self.loadPlugins() + + def getPluginByName(self, name, category="Default"): + """ + Get the plugin correspoding to a given category and name + """ + if category in self.category_mapping: + for item in self.category_mapping[category]: + if item.name == name: + return item + return None + + def activatePluginByName(self, name, category="Default"): + """ + Activate a plugin corresponding to a given category + name. + """ + pta_item = self.getPluginByName(name, category) + if pta_item is not None: + plugin_to_activate = pta_item.plugin_object + if plugin_to_activate is not None: + log.debug("Activating plugin: %s.%s" % (category, name)) + plugin_to_activate.activate() + return plugin_to_activate + return None + + def deactivatePluginByName(self, name, category="Default"): + """ + Desactivate a plugin corresponding to a given category + name. + """ + if category in self.category_mapping: + plugin_to_deactivate = None + for item in self.category_mapping[category]: + if item.name == name: + plugin_to_deactivate = item.plugin_object + break + if plugin_to_deactivate is not None: + log.debug("Deactivating plugin: %s.%s" % (category, name)) + plugin_to_deactivate.deactivate() + return plugin_to_deactivate + return None class PluginManagerSingleton(object): - """ - Singleton version of the most basic plugin manager. - - Being a singleton, this class should not be initialised explicitly - and the ``get`` classmethod must be called instead. - - To call one of this class's methods you have to use the ``get`` - method in the following way: - ``PluginManagerSingleton.get().themethodname(theargs)`` - - To set up the various coonfigurables variables of the - PluginManager's behaviour please call explicitly the following - methods: - - - ``setCategoriesFilter`` for ``categories_filter`` - - ``setPluginPlaces`` for ``directories_list`` - - ``setPluginInfoExtension`` for ``plugin_info_ext`` - """ - - __instance = None - - __decoration_chain = None - - def __init__(self): - """ - Initialisation: this class should not be initialised - explicitly and the ``get`` classmethod must be called instead. - - To set up the various configurables variables of the - PluginManager's behaviour please call explicitly the following - methods: - - - ``setCategoriesFilter`` for ``categories_filter`` - - ``setPluginPlaces`` for ``directories_list`` - - ``setPluginInfoExtension`` for ``plugin_info_ext`` - """ - if self.__instance is not None: - raise Exception("Singleton can't be created twice !") - - def setBehaviour(self,list_of_pmd): - """ - Set the functionalities handled by the plugin manager by - giving a list of ``PluginManager`` decorators. - - This function shouldn't be called several time in a same - process, but if it is only the first call will have an effect. - - It also has an effect only if called before the initialisation - of the singleton. - - In cases where the function is indeed going to change anything - the ``True`` value is return, in all other cases, the ``False`` - value is returned. - """ - if self.__decoration_chain is None and self.__instance is None: - log.debug("Setting up a specific behaviour for the PluginManagerSingleton") - self.__decoration_chain = list_of_pmd - return True - else: - log.debug("Useless call to setBehaviour: the singleton is already instanciated of already has a behaviour.") - return False - setBehaviour = classmethod(setBehaviour) - - - def get(self): - """ - Actually create an instance - """ - if self.__instance is None: - if self.__decoration_chain is not None: - # Get the object to be decorated -# print self.__decoration_chain - pm = self.__decoration_chain[0]() - for cls_item in self.__decoration_chain[1:]: -# print cls_item - pm = cls_item(decorated_manager=pm) - # Decorate the whole object - self.__instance = pm - else: - # initialise the 'inner' PluginManagerDecorator - self.__instance = PluginManager() - log.debug("PluginManagerSingleton initialised") - return self.__instance - get = classmethod(get) + + """ + Singleton version of the most basic plugin manager. + + Being a singleton, this class should not be initialised explicitly + and the ``get`` classmethod must be called instead. + + To call one of this class's methods you have to use the ``get`` + method in the following way: + ``PluginManagerSingleton.get().themethodname(theargs)`` + + To set up the various coonfigurables variables of the + PluginManager's behaviour please call explicitly the following + methods: + + - ``setCategoriesFilter`` for ``categories_filter`` + - ``setPluginPlaces`` for ``directories_list`` + - ``setPluginInfoExtension`` for ``plugin_info_ext`` + """ + + __instance = None + + __decoration_chain = None + + def __init__(self): + """ + Initialisation: this class should not be initialised + explicitly and the ``get`` classmethod must be called instead. + + To set up the various configurables variables of the + PluginManager's behaviour please call explicitly the following + methods: + + - ``setCategoriesFilter`` for ``categories_filter`` + - ``setPluginPlaces`` for ``directories_list`` + - ``setPluginInfoExtension`` for ``plugin_info_ext`` + """ + if self.__instance is not None: + raise Exception("Singleton can't be created twice !") + + def setBehaviour(self, list_of_pmd): + """ + Set the functionalities handled by the plugin manager by + giving a list of ``PluginManager`` decorators. + + This function shouldn't be called several time in a same + process, but if it is only the first call will have an effect. + + It also has an effect only if called before the initialisation + of the singleton. + + In cases where the function is indeed going to change anything + the ``True`` value is return, in all other cases, the ``False`` + value is returned. + """ + if self.__decoration_chain is None and self.__instance is None: + log.debug( + "Setting up a specific behaviour for the PluginManagerSingleton") + self.__decoration_chain = list_of_pmd + return True + else: + log.debug( + "Useless call to setBehaviour: the singleton is already instanciated of already has a behaviour.") + return False + setBehaviour = classmethod(setBehaviour) + + def get(self): + """ + Actually create an instance + """ + if self.__instance is None: + if self.__decoration_chain is not None: + # Get the object to be decorated + # print self.__decoration_chain + pm = self.__decoration_chain[0]() + for cls_item in self.__decoration_chain[1:]: + # print cls_item + pm = cls_item(decorated_manager=pm) + # Decorate the whole object + self.__instance = pm + else: + # initialise the 'inner' PluginManagerDecorator + self.__instance = PluginManager() + log.debug("PluginManagerSingleton initialised") + return self.__instance + get = classmethod(get) # For backward compatility import the most basic decorator (it changed # place as of v1.8) from yapsy.PluginManagerDecorator import PluginManagerDecorator - diff --git a/package/yapsy/PluginManagerDecorator.py b/package/yapsy/PluginManagerDecorator.py index 893ba5a..b32f79a 100644 --- a/package/yapsy/PluginManagerDecorator.py +++ b/package/yapsy/PluginManagerDecorator.py @@ -33,70 +33,71 @@ class PluginManagerDecorator(object): - """ - Add several responsibilities to a plugin manager object in a - more flexible way than by mere subclassing. This is indeed an - implementation of the Decorator Design Patterns. - - - There is also an additional mechanism that allows for the - automatic creation of the object to be decorated when this object - is an instance of PluginManager (and not an instance of its - subclasses). This way we can keep the plugin managers creation - simple when the user don't want to mix a lot of 'enhancements' on - the base class. - """ - - def __init__(self, decorated_object=None, - # The following args will only be used if we need to - # create a default PluginManager - categories_filter={"Default":IPlugin}, - directories_list=[os.path.dirname(__file__)], - plugin_info_ext="yapsy-plugin"): - """ - Mimics the PluginManager's __init__ method and wraps an - instance of this class into this decorator class. - - - *If the decorated_object is not specified*, then we use the - PluginManager class to create the 'base' manager, and to do - so we will use the arguments: ``categories_filter``, - ``directories_list``, and ``plugin_info_ext`` or their - default value if they are not given. - - - *If the decorated object is given*, these last arguments are - simply **ignored** ! - - All classes (and especially subclasses of this one) that want - to be a decorator must accept the decorated manager as an - object passed to the init function under the exact keyword - ``decorated_object``. - """ - - if decorated_object is None: - log.debug("Creating a default PluginManager instance to be decorated.") - from yapsy.PluginManager import PluginManager - decorated_object = PluginManager(categories_filter, - directories_list, - plugin_info_ext) - self._component = decorated_object - - def __getattr__(self,name): - """ - Decorator trick copied from: - http://www.pasteur.fr/formation/infobio/python/ch18s06.html - """ + + """ + Add several responsibilities to a plugin manager object in a + more flexible way than by mere subclassing. This is indeed an + implementation of the Decorator Design Patterns. + + + There is also an additional mechanism that allows for the + automatic creation of the object to be decorated when this object + is an instance of PluginManager (and not an instance of its + subclasses). This way we can keep the plugin managers creation + simple when the user don't want to mix a lot of 'enhancements' on + the base class. + """ + + def __init__(self, decorated_object=None, + # The following args will only be used if we need to + # create a default PluginManager + categories_filter={"Default": IPlugin}, + directories_list=[os.path.dirname(__file__)], + plugin_info_ext="yapsy-plugin"): + """ + Mimics the PluginManager's __init__ method and wraps an + instance of this class into this decorator class. + + - *If the decorated_object is not specified*, then we use the + PluginManager class to create the 'base' manager, and to do + so we will use the arguments: ``categories_filter``, + ``directories_list``, and ``plugin_info_ext`` or their + default value if they are not given. + + - *If the decorated object is given*, these last arguments are + simply **ignored** ! + + All classes (and especially subclasses of this one) that want + to be a decorator must accept the decorated manager as an + object passed to the init function under the exact keyword + ``decorated_object``. + """ + + if decorated_object is None: + log.debug( + "Creating a default PluginManager instance to be decorated.") + from yapsy.PluginManager import PluginManager + decorated_object = PluginManager(categories_filter, + directories_list, + plugin_info_ext) + self._component = decorated_object + + def __getattr__(self, name): + """ + Decorator trick copied from: + http://www.pasteur.fr/formation/infobio/python/ch18s06.html + """ # print "looking for %s in %s" % (name, self.__class__) - return getattr(self._component,name) - - - def collectPlugins(self): - """ - This function will usually be a shortcut to successively call - ``self.locatePlugins`` and then ``self.loadPlugins`` which are - very likely to be redefined in each new decorator. - - So in order for this to keep on being a "shortcut" and not a - real pain, I'm redefining it here. - """ - self.locatePlugins() - self.loadPlugins() + return getattr(self._component, name) + + def collectPlugins(self): + """ + This function will usually be a shortcut to successively call + ``self.locatePlugins`` and then ``self.loadPlugins`` which are + very likely to be redefined in each new decorator. + + So in order for this to keep on being a "shortcut" and not a + real pain, I'm redefining it here. + """ + self.locatePlugins() + self.loadPlugins() diff --git a/package/yapsy/VersionedPluginManager.py b/package/yapsy/VersionedPluginManager.py index abee6d7..342fcf6 100644 --- a/package/yapsy/VersionedPluginManager.py +++ b/package/yapsy/VersionedPluginManager.py @@ -21,117 +21,117 @@ class VersionedPluginInfo(PluginInfo): - """ - Gather some info about a plugin such as its name, author, - description... - """ - - def __init__(self, plugin_name, plugin_path): - """ - Set the name and path of the plugin as well as the default - values for other usefull variables. - """ - PluginInfo.__init__(self, plugin_name, plugin_path) - # version number is now required to be a StrictVersion object - self.version = StrictVersion("0.0") - - def setVersion(self, vstring): - self.version = StrictVersion(vstring) + + """ + Gather some info about a plugin such as its name, author, + description... + """ + + def __init__(self, plugin_name, plugin_path): + """ + Set the name and path of the plugin as well as the default + values for other usefull variables. + """ + PluginInfo.__init__(self, plugin_name, plugin_path) + # version number is now required to be a StrictVersion object + self.version = StrictVersion("0.0") + + def setVersion(self, vstring): + self.version = StrictVersion(vstring) class VersionedPluginManager(PluginManagerDecorator): - """ - Handle plugin versioning by making sure that when several - versions are present for a same plugin, only the latest version is - manipulated via the standard methods (eg for activation and - deactivation) - - More precisely, for operations that must be applied on a single - named plugin at a time (``getPluginByName``, - ``activatePluginByName``, ``deactivatePluginByName`` etc) the - targetted plugin will always be the one with the latest version. - - .. note:: The older versions of a given plugin are still reachable - via the ``getPluginsOfCategoryFromAttic`` method. - """ - - def __init__(self, - decorated_manager=None, - categories_filter={"Default":IPlugin}, - directories_list=None, - plugin_info_ext="yapsy-plugin"): - """ - Create the plugin manager and record the ConfigParser instance - that will be used afterwards. - - The ``config_change_trigger`` argument can be used to set a - specific method to call when the configuration is - altered. This will let the client application manage the way - they want the configuration to be updated (e.g. write on file - at each change or at precise time intervalls or whatever....) - """ - # Create the base decorator class - PluginManagerDecorator.__init__(self,decorated_manager, - categories_filter, - directories_list, - plugin_info_ext) - self.setPluginInfoClass(VersionedPluginInfo) - # prepare the storage for the early version of the plugins, - # for which only the latest version is the one that will be - # kept in the "core" plugin storage. - self._prepareAttic() - - def _prepareAttic(self): - """ - Create and correctly initialize the storage where the wrong - version of the plugins will be stored. - """ - self._attic = {} - for categ in self.getCategories(): - self._attic[categ] = [] - - - def getLatestPluginsOfCategory(self,category_name): - """ - DEPRECATED(>1.8): Please consider using getPluginsOfCategory - instead. - - Return the list of all plugins belonging to a category. - """ - return self.getPluginsOfCategory(category_name) - - def loadPlugins(self, callback=None): - """ - Load the candidate plugins that have been identified through a - previous call to locatePlugins. - - In addition to the baseclass functionality, this subclass also - needs to find the latest version of each plugin. - """ - self._component.loadPlugins(callback) - for categ in self.getCategories(): - latest_plugins = {} - allPlugins = self.getPluginsOfCategory(categ) - # identify the latest version of each plugin - for plugin in allPlugins: - name = plugin.name - version = plugin.version - if name in latest_plugins: - if version > latest_plugins[name].version: - older_plugin = latest_plugins[name] - latest_plugins[name] = plugin - self.removePluginFromCategory(older_plugin,categ) - self._attic[categ].append(older_plugin) - else: - self.removePluginFromCategory(plugin,categ) - self._attic[categ].append(plugin) - else: - latest_plugins[name] = plugin - - def getPluginsOfCategoryFromAttic(self,categ): - """ - Access the older version of plugins for which only the latest - version is available through standard methods. - """ - return self._attic[categ] - + + """ + Handle plugin versioning by making sure that when several + versions are present for a same plugin, only the latest version is + manipulated via the standard methods (eg for activation and + deactivation) + + More precisely, for operations that must be applied on a single + named plugin at a time (``getPluginByName``, + ``activatePluginByName``, ``deactivatePluginByName`` etc) the + targetted plugin will always be the one with the latest version. + + .. note:: The older versions of a given plugin are still reachable + via the ``getPluginsOfCategoryFromAttic`` method. + """ + + def __init__(self, + decorated_manager=None, + categories_filter={"Default": IPlugin}, + directories_list=None, + plugin_info_ext="yapsy-plugin"): + """ + Create the plugin manager and record the ConfigParser instance + that will be used afterwards. + + The ``config_change_trigger`` argument can be used to set a + specific method to call when the configuration is + altered. This will let the client application manage the way + they want the configuration to be updated (e.g. write on file + at each change or at precise time intervalls or whatever....) + """ + # Create the base decorator class + PluginManagerDecorator.__init__(self, decorated_manager, + categories_filter, + directories_list, + plugin_info_ext) + self.setPluginInfoClass(VersionedPluginInfo) + # prepare the storage for the early version of the plugins, + # for which only the latest version is the one that will be + # kept in the "core" plugin storage. + self._prepareAttic() + + def _prepareAttic(self): + """ + Create and correctly initialize the storage where the wrong + version of the plugins will be stored. + """ + self._attic = {} + for categ in self.getCategories(): + self._attic[categ] = [] + + def getLatestPluginsOfCategory(self, category_name): + """ + DEPRECATED(>1.8): Please consider using getPluginsOfCategory + instead. + + Return the list of all plugins belonging to a category. + """ + return self.getPluginsOfCategory(category_name) + + def loadPlugins(self, callback=None): + """ + Load the candidate plugins that have been identified through a + previous call to locatePlugins. + + In addition to the baseclass functionality, this subclass also + needs to find the latest version of each plugin. + """ + self._component.loadPlugins(callback) + for categ in self.getCategories(): + latest_plugins = {} + allPlugins = self.getPluginsOfCategory(categ) + # identify the latest version of each plugin + for plugin in allPlugins: + name = plugin.name + version = plugin.version + if name in latest_plugins: + if version > latest_plugins[name].version: + older_plugin = latest_plugins[name] + latest_plugins[name] = plugin + self.removePluginFromCategory(older_plugin, categ) + self._attic[categ].append(older_plugin) + else: + self.removePluginFromCategory(plugin, categ) + self._attic[categ].append(plugin) + else: + latest_plugins[name] = plugin + + def getPluginsOfCategoryFromAttic(self, categ): + """ + Access the older version of plugins for which only the latest + version is available through standard methods. + """ + return self._attic[categ] diff --git a/package/yapsy/__init__.py b/package/yapsy/__init__.py index 693fad8..1be5ab4 100644 --- a/package/yapsy/__init__.py +++ b/package/yapsy/__init__.py @@ -27,7 +27,7 @@ should get you a fully working plugin management system:: from yapsy.PluginManager import PluginManager - + # Build the manager simplePluginManager = PluginManager() # Tell it the default place(s) where to find plugins @@ -53,7 +53,7 @@ """ -__version__="1.10.423" +__version__ = "1.10.423" # tell epydoc that the documentation is in the reStructuredText format __docformat__ = "restructuredtext en" @@ -63,7 +63,7 @@ log = logging.getLogger('yapsy') # Some constants concerning the plugins -PLUGIN_NAME_FORBIDEN_STRING=";;" +PLUGIN_NAME_FORBIDEN_STRING = ";;" """ .. warning:: This string (';;' by default) is forbidden in plugin names, and will be usable to describe lists of plugins @@ -71,18 +71,28 @@ """ import re +from yapsy.compat import is_py2, str + +if is_py2: + RE_NON_ALPHANUM = re.compile("\W", re.U) +else: + RE_NON_ALPHANUM = re.compile("\W") -RE_NON_ALPHANUM = re.compile("\W") def NormalizePluginNameForModuleName(pluginName): - """ - Normalize a plugin name into a safer name for a module name. - - .. note:: may do a little more modifications than strictly - necessary and is not optimized for speed. - """ - if len(pluginName)==0: - return "_" - if pluginName[0].isdigit(): - pluginName = "_" + pluginName - return RE_NON_ALPHANUM.sub("_",pluginName) + """ + Normalize a plugin name into a safer name for a module name. + + .. note:: may do a little more modifications than strictly + necessary and is not optimized for speed. + """ + if is_py2: + pluginName = str(pluginName, 'utf-8') + if len(pluginName) == 0: + return "_" + if pluginName[0].isdigit(): + pluginName = "_" + pluginName + ret = RE_NON_ALPHANUM.sub("_", pluginName) + if is_py2: + return ret.encode('utf-8') + return ret diff --git a/package/yapsy/compat.py b/package/yapsy/compat.py new file mode 100644 index 0000000..082da94 --- /dev/null +++ b/package/yapsy/compat.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +""" +pythoncompat +""" + +import sys + +# ------- +# Pythons +# ------- + +# Syntax sugar. +_ver = sys.version_info + +#: Python 2.x? +is_py2 = (_ver[0] == 2) + +#: Python 3.x? +is_py3 = (_ver[0] == 3) + +#: Python 3.0.x +is_py30 = (is_py3 and _ver[1] == 0) + +#: Python 3.1.x +is_py31 = (is_py3 and _ver[1] == 1) + +#: Python 3.2.x +is_py32 = (is_py3 and _ver[1] == 2) + +#: Python 3.3.x +is_py33 = (is_py3 and _ver[1] == 3) + +#: Python 3.4.x +is_py34 = (is_py3 and _ver[1] == 4) + +#: Python 2.7.x +is_py27 = (is_py2 and _ver[1] == 7) + +#: Python 2.6.x +is_py26 = (is_py2 and _ver[1] == 6) + +#: Python 2.5.x +is_py25 = (is_py2 and _ver[1] == 5) + +#: Python 2.4.x +is_py24 = (is_py2 and _ver[1] == 4) # I'm assuming this is not by choice. + + +# --------- +# Platforms +# --------- + + +# Syntax sugar. +_ver = sys.version.lower() + +is_pypy = ('pypy' in _ver) +is_jython = ('jython' in _ver) +is_ironpython = ('iron' in _ver) + +# Assume CPython, if nothing else. +is_cpython = not any((is_pypy, is_jython, is_ironpython)) + +# Windows-based system. +is_windows = 'win32' in str(sys.platform).lower() + +# Standard Linux 2+ system. +is_linux = ('linux' in str(sys.platform).lower()) +is_osx = ('darwin' in str(sys.platform).lower()) +is_hpux = ('hpux' in str(sys.platform).lower()) # Complete guess. +is_solaris = ('solar==' in str(sys.platform).lower()) # Complete guess. + +if is_py2: + from ConfigParser import ConfigParser + from StringIO import StringIO + + builtin_str = str + bytes = str + str = unicode + basestring = basestring + numeric_types = (int, long, float) + + +elif is_py3: + from configparser import ConfigParser + from io import StringIO + + builtin_str = str + str = str + bytes = bytes + basestring = (str, bytes) + numeric_types = (int, float) From c8ce5ba4a0b78f654f7d852cae14cfea42576199 Mon Sep 17 00:00:00 2001 From: Josip Delic Date: Mon, 29 Sep 2014 10:29:03 +0200 Subject: [PATCH 34/34] added travis python2 tests changed url to travis added changelog entry --- .travis.yml | 3 +++ README.md | 4 ++-- package/CHANGELOG.txt | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9431b53..c1e5da3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,10 @@ language: python python: + - "2.6" + - "2.7" - "3.2" - "3.3" + - "3.4" # command to install dependencies install: diff --git a/README.md b/README.md index cb991c7..566338c 100644 --- a/README.md +++ b/README.md @@ -15,5 +15,5 @@ Yapsy's development is [hosted at Sourceforge](http://sourceforge.net/projects/y Yapsy is also continuously tested on [travis-ci](https://travis-ci.org): -[![Build Status](https://travis-ci.org/tibonihoo/yapsy.png?branch=python3)](https://travis-ci.org/tibonihoo/yapsy) -[![Coverage Status](https://coveralls.io/repos/tibonihoo/yapsy/badge.png?branch=python3)](https://coveralls.io/r/tibonihoo/yapsy?branch=python3) +[![Build Status](https://travis-ci.org/tibonihoo/yapsy.png?branch=master)](https://travis-ci.org/tibonihoo/yapsy) +[![Coverage Status](https://coveralls.io/repos/tibonihoo/yapsy/badge.png?branch=master)](https://coveralls.io/r/tibonihoo/yapsy?branch=master) diff --git a/package/CHANGELOG.txt b/package/CHANGELOG.txt index 972a845..f6aaf73 100644 --- a/package/CHANGELOG.txt +++ b/package/CHANGELOG.txt @@ -1,3 +1,9 @@ +version-X.XX.XXX [unreleased] + + - code: one branch for python3 and python2 + - code: pep8 with 'autopep8 -aaa -i -r .' + - code: PluginManger can also throw other exceptions in issubclass + version-1.10.423 [2014-06-07] - code: Speed optimisation for the regexp compiled in __init__.py (see https://sourceforge.net/p/yapsy/patches/4/)