[yum-cvs] yum/yum plugins.py, NONE, 1.1 __init__.py, 1.98, 1.99 config.py, 1.49, 1.50 constants.py, 1.1, 1.2

Menno Smits mjs at login.linux.duke.edu
Mon Mar 21 12:37:15 UTC 2005


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

Modified Files:
	__init__.py config.py constants.py 
Added Files:
	plugins.py 
Log Message:
Initial yum plugin work.


--- NEW FILE plugins.py ---

import os
import glob
import imp
import atexit
from constants import *

# TODO: API version checking
#   - multiversion plugin support?

# 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: move all config hooks to within yum pacakge (not cli.py). This may
# require some minor refactoring of various yum bits.

# TODO: better documentation of how the whole thing works (esp. addition of
# config options)

# TODO: use exception in plugins to signal that yum should abort instead of
# return codes

# 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: reposetup slot: plugin must be able to enable and disable repos

# TODO: make it that plugins need to be expicity enabled so that software using
#   yum as a library doesn't get expected plugin interference?

# TODO: cmd line options to disable plugins (all or specific)

# TODO: cmdline/config option to specify additional plugin directories (plugin path)

# 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: require an explicit call to load plugins so that software using yum as
# a # library doesn't get nasty suprises

SLOTS = ('config', 'init', 'exclude', 'pretrans', 'posttrans', 'close')

class YumPlugins:

    def __init__(self, base, dir):
        self.dir = dir
        self.base = base
        self._importplugins()
        self.opts = []

        # Call close handlers when yum exit's
        atexit.register(self.run, 'close')

        # Let plugins register custom config file options
        self.run('config')

    def run(self, slotname):
        '''Run all plugin functions for the given slot.
        Returns true if yum needs to quit, false otherwise.
        '''
       
        # Determine handler class to use
        if slotname in ('config'):
            gwcls = ConfigPluginConduit
        elif slotname == 'init':
            gwcls = InitPluginConduit
        elif slotname == 'close':
            gwcls = PluginConduit
        elif slotname in ('pretrans', 'posttrans', 'exclude'):
            gwcls = MainPluginConduit
        else:
            raise ValueError('unknown slot name "%s"' % slotname)

        gw = gwcls(self, self.base)

        for modname, func in self._pluginfuncs[slotname]:
            self.base.log(4, 'Running %s handler for %s plugin' % (
                slotname, modname))
            result = func(gw)
            if result:
                # Plugin said we need to terminate
                self.base.log(2, "Exiting due to '%s' plugin." % modname)
                return result
        return 0

    def _importplugins(self):

        # Initialise plugin dict
        self._plugins = {}
        self._pluginfuncs = {}
        for slot in SLOTS:
            self._pluginfuncs[slot] = []

        # Import plugins and collect plugin calls for each slot
        for module in glob.glob('%s/*.py' % self.dir):
            modname = os.path.basename(module).split('.py')[0]

            self.base.log(2, 'Loading %s plugin' % modname)

            fp, pathname, description = imp.find_module(modname, [self.dir])
            module = imp.load_module(modname, fp, pathname, description)

            #TODO: check API version here?

            self._plugins[modname] = module 
            
            for slot in SLOTS:
                if hasattr(module, slot):
                    self._pluginfuncs[slot].append(
                            (modname, getattr(module, slot))
                            )

    def registeropt(self, name, valuetype, where, default):
        '''Called from plugins to register a new config file option.

        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, ...)
        default: Default value for the option if not set by the user.
        '''
        #TODO: duplicate detection
        self.opts.append((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
        return: A list of (name, value_type, default) tuples.
        '''
        out = []
        for name, valuetype, where, default in self.opts:
            if where == targetwhere or where == PLUG_OPT_WHERE_ALL:
                out.append((name, valuetype, default))
        return out

class PluginConduit:
    def __init__(self, parent, base):
        self._parent = parent
        self._base = base

    def info(self, level, msg):
        self._base.log(level, msg)

    def error(self, level, msg):
        self._base.errorlog(level, msg)

    def promptyn(self, msg):
        self.info(2, msg)
        if self._base.conf.getConfigOption('assumeyes'):
            return 1
        else:
            return self._base.userconfirm()

class ConfigPluginConduit(PluginConduit):

    def registeropt(self, *args, **kwargs):
        self._parent.registeropt(*args, **kwargs)

class InitPluginConduit(PluginConduit):

    def getConf(self):
        return self._base.conf

class MainPluginConduit(InitPluginConduit):

    def getRepos(self):
        return self._base.repos.listEnabled()

    def getPackages(self, repo=None):
        if repo:
            arg = repo.id
        else:
            arg = None
        return self._base.pkgSack.returnPackages(arg)

    def delPackage(self, po):
        self._base.pkgSack.delPackage(po)

    def getTsInfo(self):
        return self._base.tsInfo





Index: __init__.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/__init__.py,v
retrieving revision 1.98
retrieving revision 1.99
diff -u -r1.98 -r1.99
--- __init__.py	15 Mar 2005 07:33:19 -0000	1.98
+++ __init__.py	21 Mar 2005 12:37:13 -0000	1.99
@@ -37,6 +37,7 @@
 import transactioninfo
 from urlgrabber.grabber import URLGrabError
 import depsolve
+import plugins
 
 from packages import parsePackages, YumLocalPackage, YumInstalledPackage, bestPackage
 from repomd import mdErrors
@@ -64,13 +65,22 @@
 
     def filelog(self, value, msg):
         print msg
-    
-    def doConfigSetup(self, fn='/etc/yum.conf', root='/'):
+   
+    def doConfigSetup(self, fn='/etc/yum.conf', root='/', 
+            plugindir='/usr/lib/yum-plugins'):
         """basic stub function for doing configuration setup"""
-        
-        self.conf = config.yumconf(configfile=fn, root=root)
+       
+        # Load plugins first as they make affect available config options
+        self.plugins = plugins.YumPlugins(self, plugindir)
+
+        self.conf = config.yumconf(configfile=fn, root=root, 
+                plugins=self.plugins)
         self.getReposFromConfig()
 
+        # Initialise plugins
+        if self.plugins.run('init') != 0:
+            sys.exit()
+
     def getReposFromConfig(self):
         """read in repositories from config main and .repo files"""
         
@@ -207,6 +217,8 @@
         for repo in self.repos.listEnabled():
             self.excludePackages(repo)
             self.includePackages(repo)
+        if self.plugins.run('exclude') != 0:
+            sys.exit()
         self.pkgSack.buildIndexes()
         
     def doUpdateSetup(self):
@@ -308,7 +320,7 @@
         for po in exactmatch + matched:
             self.log(3, 'Excluding %s' % po)
             self.pkgSack.delPackage(po)
-        
+      
         self.log(2, 'Finished')
 
     def includePackages(self, repo):

Index: config.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/config.py,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -r1.49 -r1.50
--- config.py	15 Mar 2005 07:43:03 -0000	1.49
+++ config.py	21 Mar 2005 12:37:13 -0000	1.50
@@ -33,6 +33,8 @@
 import urlgrabber.grabber
 from repos import variableReplace, Repository
 
+from constants import *
+
 
 class CFParser(ConfigParser.ConfigParser):
     """wrapper around ConfigParser to provide two simple but useful functions:
@@ -171,7 +173,7 @@
 class yumconf(object):
     """primary config class for yum"""
     
-    def __init__(self, configfile = '/etc/yum.conf', root='/'):
+    def __init__(self, configfile = '/etc/yum.conf', root='/', plugins=None):
         self.cfg = CFParser()
         configh = confpp(configfile)
         try:
@@ -181,7 +183,7 @@
         except ConfigParser.ParsingError, e:
             raise Errors.ConfigError, str(e)
             
-
+        self.plugins = plugins
         self.configdata = {} # dict to hold all the data goodies
        
         
@@ -238,7 +240,6 @@
 
         optionfloats = [('timeout', 30.0)]
 
-
         # do these two early so we can do the rest using variableReplace()
         for (option, default) in [('distroverpkg' , 'fedora-release'),
                                   ('installroot', root)]:
@@ -327,6 +328,11 @@
         for option in ['exclude', 'installonlypkgs', 'kernelpkgnames', 'tsflags']:
             self.configdata[option] = parseList(self.configdata[option])
 
+
+        # Process options from plugins
+        dopluginopts(self.plugins, self.cfg, 'main', PLUG_OPT_WHERE_GLOBAL, 
+                self.setConfigOption)
+
     def listConfigOptions(self):
         """return list of options available for global config"""
         return self.configdata.keys()
@@ -410,7 +416,6 @@
        Returns a repos.Repository object
        """
     
-    
     thisrepo = Repository(section)
     thisrepo.set('yumvar', yumconfig.yumvar)
 
@@ -470,6 +475,10 @@
     thisrepo.set('pkgdir', pkgdir)
     thisrepo.set('hdrdir', hdrdir)
     
+    # Process options from plugins
+    dopluginopts(yumconfig.plugins, cfgparser, section, PLUG_OPT_WHERE_REPO, 
+            thisrepo.set)
+
     return thisrepo
 
 
@@ -489,7 +498,19 @@
     (value, count) = commarepl.subn(' ', value)
     listvalue = value.split()
     return listvalue
-        
+      
+def dopluginopts(plugins, cfgparser, section, where, setfunc):
+    '''Process options from plugins
+    '''
+    if plugins:
+        typetofunc =  {
+            PLUG_OPT_STRING: cfgparser._getoption,
+            PLUG_OPT_INT: cfgparser._getint,
+            PLUG_OPT_BOOL: cfgparser._getboolean,
+            PLUG_OPT_FLOAT: cfgparser._getfloat,
+            }
+        for name, vtype, default in plugins.getopts(where):
+            setfunc(name, typetofunc[vtype](section, name, default))
 
 
 class confpp:

Index: constants.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/constants.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- constants.py	12 Mar 2005 22:13:15 -0000	1.1
+++ constants.py	21 Mar 2005 12:37:13 -0000	1.2
@@ -39,3 +39,13 @@
 SYMBOLFLAGS = {'>':'GT', '<':'LT', '=': 'EQ', '==': 'EQ', '>=':'GE', '<=':'LE'}
 LETTERFLAGS = {'GT':'>', 'LT':'<', 'EQ':'=', 'GE': '>=', 'LE': '<='}
 
+# Constants for plugin config option registration
+PLUG_OPT_STRING = 0
+PLUG_OPT_INT = 1
+PLUG_OPT_FLOAT = 2
+PLUG_OPT_BOOL = 3
+
+PLUG_OPT_WHERE_GLOBAL = 0
+PLUG_OPT_WHERE_REPO = 1
+PLUG_OPT_WHERE_ALL = 2
+




More information about the Yum-cvs-commits mailing list