[yum-cvs] yum/yum __init__.py, 1.103, 1.104 config.py, 1.56, 1.57 constants.py, 1.2, 1.3 plugins.py, 1.2, 1.3

Menno Smits mjs at login.linux.duke.edu
Sun Mar 27 03:22:00 UTC 2005


Update of /home/groups/yum/cvs/yum/yum
In directory login:/tmp/cvs-serv10595

Modified Files:
	__init__.py config.py constants.py plugins.py 
Log Message:
Lots of plugin related changes:
    - API versioning
    - cleaned up API somewhat
    - hook methods now need a "_hook" suffix. This will allow detection of
      plugins that are trying to register hooks that aren't supported.
    - plugin hook method return codes are now ignored. Plugins should now use
      PluginYumExit exception if yum should terminate.
    - started reposetup hook
    - custom option conflict detection
    - renamed PLUG_OPT_WHERE_GLOBAL to PLUG_OPT_WHERE_MAIN


Index: __init__.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/__init__.py,v
retrieving revision 1.103
retrieving revision 1.104
diff -u -r1.103 -r1.104
--- __init__.py	26 Mar 2005 07:54:15 -0000	1.103
+++ __init__.py	27 Mar 2005 03:21:57 -0000	1.104
@@ -77,8 +77,7 @@
         self.getReposFromConfig()
 
         # Initialise plugins
-        if self.plugins.run('init') != 0:
-            sys.exit()
+        self.plugins.run('init')
 
     def getReposFromConfig(self):
         """read in repositories from config main and .repo files"""
@@ -132,7 +131,7 @@
             except Errors.RepoError, e: 
                 self.errorlog(2, e)
                 continue
-        
+
     def doTsSetup(self):
         """setup all the transaction set storage items we'll need
            This can't happen in __init__ b/c we don't know our installroot
@@ -194,7 +193,6 @@
                 self.errorlog(0, str(e))
                 raise
 
-
     def doSackSetup(self, archlist=None):
         """populates the package sacks for information from our repositories,
            takes optional archlist for archs to include"""
@@ -215,8 +213,7 @@
         for repo in self.repos.listEnabled():
             self.excludePackages(repo)
             self.includePackages(repo)
-        if self.plugins.run('exclude') != 0:
-            sys.exit()
+        self.plugins.run('exclude')
         self.pkgSack.buildIndexes()
         
     def doUpdateSetup(self):
@@ -293,8 +290,7 @@
     def runTransaction(self, cb):
         """takes an rpm callback object, performs the transaction"""
 
-        if self.plugins.run('pretrans') != 0:
-            return
+        self.plugins.run('pretrans')
 
         errors = self.ts.run(cb.callback, '')
         if errors:
@@ -304,8 +300,7 @@
             
             raise yum.Errors.YumBaseError, errstring
 
-        if self.plugins.run('posttrans') != 0:
-            return
+        self.plugins.run('posttrans')
         
     def excludePackages(self, repo=None):
         """removes packages from packageSacks based on global exclude lists,

Index: config.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/config.py,v
retrieving revision 1.56
retrieving revision 1.57
diff -u -r1.56 -r1.57
--- config.py	26 Mar 2005 18:28:25 -0000	1.56
+++ config.py	27 Mar 2005 03:21:57 -0000	1.57
@@ -335,7 +335,7 @@
             setattr(self, option, self.configdata[option])
 
         # Process options from plugins
-        dopluginopts(self.plugins, self.cfg, 'main', PLUG_OPT_WHERE_GLOBAL, 
+        dopluginopts(self.plugins, self.cfg, 'main', PLUG_OPT_WHERE_MAIN, 
                 self.setConfigOption)
 
     def listConfigOptions(self):

Index: constants.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/constants.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- constants.py	21 Mar 2005 12:37:13 -0000	1.2
+++ constants.py	27 Mar 2005 03:21:57 -0000	1.3
@@ -45,7 +45,7 @@
 PLUG_OPT_FLOAT = 2
 PLUG_OPT_BOOL = 3
 
-PLUG_OPT_WHERE_GLOBAL = 0
+PLUG_OPT_WHERE_MAIN = 0
 PLUG_OPT_WHERE_REPO = 1
 PLUG_OPT_WHERE_ALL = 2
 

Index: plugins.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/plugins.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- plugins.py	25 Mar 2005 09:47:28 -0000	1.2
+++ plugins.py	27 Mar 2005 03:21:57 -0000	1.3
@@ -8,28 +8,29 @@
 import config 
 import Errors
 
-# TODO: rename PLUG_OPT_WHERE_GLOBAL - > PLUG_OPT_WHERE_MAIN 
-
-# TODO: API version checking
-#   - multiversion plugin support?
+# TODO: fix log() during yum init: early calls to log() (before Logger instance
+# is created) mean that all output goes to stdout regardless of the log settings.
+#   - peek at debuglevel option? 
 
-# TODO: prefix for slot methods ("yum_"?) so that yum knows if plugin is trying
-# to hook into something that it doesn't support (eg. new plugin with older yum
-# version)
+# TODO: finish reposetup slot: where to call from?
 
 # TODO: better documentation of how the whole thing works (esp. addition of
 # config options)
+#       - document from user perspective in yum man page
+#       - PLUGINS.txt for developers
 
-# TODO: use exception in plugins to signal that yum should abort instead of
-# return codes
+# TODO: require an explicit call to load plugins so that software using yum as
+# a library doesn't get nasty suprises
 
-# TODO: fix log() during yum init: early calls to log() (before Logger instance
-# is created) mean that all output goes to stdout regardless of the log settings.
+# TODO: check for *_hook methods that aren't supported
 
-# TODO: reposetup slot: plugin must be able to enable and disable repos
+# TODO "log" slot? To allow plugins to do customised logging/history (say to a
+# SQL db)
 
-# TODO: make it that plugins need to be explicitly enabled so that software using
-#   yum as a library doesn't get expected plugin interference?
+# TODO: method for plugins to retrieve running yum version (move __version__ to
+# yum/__init__.py?)
+
+# TODO: multiversion plugin support
 
 # TODO: cmd line options to disable plugins (all or specific)
 
@@ -38,23 +39,39 @@
 # TODO: config vars marked as PLUG_OPT_WHERE_ALL should inherit defaults from
 #   the [main] setting if the user doesn't specify them
 
-# TODO: handling of plugins that define options which collide with other
-# plugins or builtin yum options
-
 # TODO: plugins should be able to specify convertor functions for config vars
 
 # TODO: investigate potential issues with plugins doing user output during
 #   their close handlers, esp wrt GUI apps
 
-# TODO "log" slot? To allow plugins to do customised logging/history (say to a
-# SQL db)
+# TODO: detect conflicts between builtin yum options and registered plugin
+# options (will require refactoring of config.py)
 
-# TODO: require an explicit call to load plugins so that software using yum as
-# a # library doesn't get nasty suprises
+
+# The API_VERSION constant defines the current plugin API version. It is used
+# to decided whether or not plugins can be loaded. It is compared against the
+# 'requires_api_version' attribute of each plugin. The version number has the
+# format: "major_version.minor_version".
+# 
+# For a plugin to be loaded the major version required by the plugin must match
+# the major version in API_VERSION. Additionally, the minor version in
+# API_VERSION must be greater than or equal the minor version required by the
+# plugin.
+# 
+# If a change to yum is made that break backwards compatibility wrt the plugin
+# API, the major version number must be incremented and the minor version number
+# reset to 0. If a change is made that doesn't break backwards compatibility,
+# then the minor number must be incremented.
+API_VERSION = '0.2'
 
 PLUGINS_CONF = '/etc/yum/plugins.conf'
+SLOTS = ('config', 'init', 'reposetup', 'exclude', 'pretrans', 'posttrans',
+        'close')
+
+class PluginYumExit(Errors.YumBaseError):
+    '''Used by plugins to signal that yum should stop
+    '''
 
-SLOTS = ('config', 'init', 'exclude', 'pretrans', 'posttrans', 'close')
 class YumPlugins:
 
     def __init__(self, base):
@@ -64,7 +81,7 @@
 
         self._getglobalconf()
         self._importplugins()
-        self.opts = []
+        self.opts = {}
 
         # Call close handlers when yum exit's
         atexit.register(self.run, 'close')
@@ -110,6 +127,8 @@
             conduitcls = ConfigPluginConduit
         elif slotname == 'init':
             conduitcls = InitPluginConduit
+        elif slotname == 'reposetup':
+            conduitcls = RepoSetupPluginConduit
         elif slotname == 'close':
             conduitcls = PluginConduit
         elif slotname in ('pretrans', 'posttrans', 'exclude'):
@@ -165,8 +184,20 @@
         fp, pathname, description = imp.find_module(modname, [dir])
         module = imp.load_module(modname, fp, pathname, description)
 
-        #TODO: check API version here?
+        # Check API version required by the plugin
+        if not hasattr(module, 'requires_api_version'):
+             raise Errors.ConfigError(
+                'Plugin "%s" doesn\'t specify required API version' % modname
+                )
+        if not apiverok(API_VERSION, module.requires_api_version):
+            raise Errors.ConfigError(
+                'Plugin "%s" requires API %s. Supported API is %s.' % (
+                    modname,
+                    module.requires_api_version,
+                    API_VERSION,
+                    ))
 
+        # Store the plugin module and its configuration file
         if not self._plugins.has_key(modname):
             self._plugins[modname] = (module, conf)
         else:
@@ -174,8 +205,11 @@
                     'exist in the plugin search path' % modname)
         
         for slot in SLOTS:
-            if hasattr(module, slot):
-                self._pluginfuncs[slot].append((modname, getattr(module, slot)))
+            funcname = slot+'_hook'
+            if hasattr(module, funcname):
+                self._pluginfuncs[slot].append(
+                        (modname, getattr(module, funcname))
+                        )
 
     def _getpluginconf(self, modname):
         '''Parse the plugin specific configuration file and return a CFParser
@@ -204,22 +238,25 @@
         name: Name of the new option.
         valuetype: Option type (PLUG_OPT_BOOL, PLUG_OPT_STRING ...)
         where: Where the option should be available in the config file.
-            (PLUG_OPT_WHERE_GLOBAL, PLUG_OPT_WHERE_REPO, ...)
+            (PLUG_OPT_WHERE_MAIN, PLUG_OPT_WHERE_REPO, ...)
         default: Default value for the option if not set by the user.
         '''
-        #TODO: duplicate detection
-        self.opts.append((name, valuetype, where, default))
+        if self.opts.has_key(name):
+            raise Errors.ConfigError('Plugin option conflict: ' \
+                    'an option named "%s" has already been registered' % name
+                    )
+        self.opts[name] = (valuetype, where, default)
 
     def getopts(self, targetwhere):
         '''Retrieve plugin defined options for the given part of the
         configuration file. 
 
         targetwhere: the type of option wanted. Should be
-            PLUG_OPT_WHERE_GLOBAL, PLUG_OPT_WHERE_REPO or PLUG_OPT_WHERE_ALL
+            PLUG_OPT_WHERE_MAIN, PLUG_OPT_WHERE_REPO or PLUG_OPT_WHERE_ALL
         return: A list of (name, value_type, default) tuples.
         '''
         out = []
-        for name, valuetype, where, default in self.opts:
+        for name, (valuetype, where, default) in self.opts.iteritems():
             if where == targetwhere or where == PLUG_OPT_WHERE_ALL:
                 out.append((name, valuetype, default))
         return out
@@ -236,36 +273,36 @@
     def error(self, level, msg):
         self._base.errorlog(level, msg)
 
-    def promptyn(self, msg):
+    def promptYN(self, msg):
         self.info(2, msg)
         if self._base.conf.getConfigOption('assumeyes'):
             return 1
         else:
             return self._base.userconfirm()
 
-    def getconfstring(self, section, opt, default=None):
+    def confString(self, section, opt, default=None):
         '''Read a string value from the plugin's own configuration file
         '''
         return self._conf._getoption(section, opt, default)
 
-    def getconfint(self, section, opt, default=None):
+    def confInt(self, section, opt, default=None):
         '''Read an integer value from the plugin's own configuration file
         '''
         return self._conf._getint(section, opt, default)
 
-    def getconffloat(self, section, opt, default=None):
+    def confFloat(self, section, opt, default=None):
         '''Read a float value from the plugin's own configuration file
         '''
         return self._conf._getfloat(section, opt, default)
 
-    def getconfbool(self, section, opt, default=None):
+    def confBool(self, section, opt, default=None):
         '''Read a boolean value from the plugin's own configuration file
         '''
         return self._conf._getboolean(section, opt, default)
 
 class ConfigPluginConduit(PluginConduit):
 
-    def registeropt(self, *args, **kwargs):
+    def registerOpt(self, *args, **kwargs):
         self._parent.registeropt(*args, **kwargs)
 
 class InitPluginConduit(PluginConduit):
@@ -273,10 +310,12 @@
     def getConf(self):
         return self._base.conf
 
-class MainPluginConduit(InitPluginConduit):
+class RepoSetupPluginConduit(InitPluginConduit):
 
-    def getRepos(self):
-        return self._base.repos.listEnabled()
+    def getRepos(self, pattern='*'):
+        return self._base.repos.findRepos(pattern)
+
+class MainPluginConduit(RepoSetupPluginConduit):
 
     def getPackages(self, repo=None):
         if repo:
@@ -291,6 +330,23 @@
     def getTsInfo(self):
         return self._base.tsInfo
 
+def parsever(apiver):
+    maj, min = apiver.split('.')
+    return int(maj), int(min)
+
+def apiverok(a, b):
+    '''Return true if API version "a" supports API version "b"
+    '''
+    a = parsever(a)
+    b = parsever(b)
+
+    if a[0] != b[0]:
+        return 0
+
+    if a[1] >= b[1]:
+        return 1
+
+    return 0
 
 
 




More information about the Yum-cvs-commits mailing list