[yum-cvs] yum/yum __init__.py, 1.257, 1.258 config.py, 1.103, 1.104 parser.py, 1.7, 1.8

Seth Vidal skvidal at linux.duke.edu
Mon Oct 2 03:46:59 UTC 2006


Update of /home/groups/yum/cvs/yum/yum
In directory login1.linux.duke.edu:/tmp/cvs-serv11169/yum

Modified Files:
	__init__.py config.py parser.py 
Log Message:

make the include= and yum -c http://path/to/file behave like I thought they
should.

This, ultimately, removes the need for parser.IncludingConfigParser and
parser.IncludedConfigParser but I haven't removed them yet b/c I wanted to
verify that they are not used elsewhere, first.


Index: __init__.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/__init__.py,v
retrieving revision 1.257
retrieving revision 1.258
diff -u -r1.257 -r1.258
--- __init__.py	29 Sep 2006 14:45:21 -0000	1.257
+++ __init__.py	2 Oct 2006 03:46:57 -0000	1.258
@@ -26,7 +26,7 @@
 import glob
 import logging
 import logging.config
-from ConfigParser import ParsingError
+from ConfigParser import ParsingError, ConfigParser
 import Errors
 import rpmsack
 import rpmUtils.updates
@@ -36,6 +36,7 @@
 import config
 import repos
 import misc
+from parser import ConfigPreProcessor
 import transactioninfo
 import urlgrabber
 from urlgrabber.grabber import URLGrabError
@@ -166,19 +167,19 @@
 
         # Read .repo files from directories specified by the reposdir option
         # (typically /etc/yum.repos.d and /etc/yum/repos.d)
-        parser = config.IncludedDirConfigParser(vars=self.yumvar)
+        parser = ConfigParser()
         for reposdir in self.conf.reposdir:
             if os.path.exists(self.conf.installroot+'/'+reposdir):
                 reposdir = self.conf.installroot + '/' + reposdir
 
             if os.path.isdir(reposdir):
-                #XXX: why can't we just pass the list of files?
-                files = ' '.join(glob.glob('%s/*.repo' % reposdir))
-                try:
-                    parser.read(files)
-                except ParsingError, e:
-                    msg = str(e)
-                    raise Errors.ConfigError, msg
+                for repofn in glob.glob('%s/*.repo' % reposdir):
+                    confpp_obj = ConfigPreProcessor(repofn, vars=self.yumvar)
+                    try:
+                        parser.readfp(confpp_obj)
+                    except ParsingError, e:
+                        msg = str(e)
+                        raise Errors.ConfigError, msg
 
         # Check sections in the .repo files that were just slurped up
         for section in parser.sections():

Index: config.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/config.py,v
retrieving revision 1.103
retrieving revision 1.104
diff -u -r1.103 -r1.104
--- config.py	29 Sep 2006 15:44:58 -0000	1.103
+++ config.py	2 Oct 2006 03:46:57 -0000	1.104
@@ -20,8 +20,8 @@
 import rpm
 import copy
 import urlparse
-from parser import IncludingConfigParser, IncludedDirConfigParser
-from ConfigParser import NoSectionError, NoOptionError
+from parser import ConfigPreProcessor
+from ConfigParser import NoSectionError, NoOptionError, ConfigParser, ParsingError
 import rpmUtils.transaction
 import rpmUtils.arch
 import Errors
@@ -560,14 +560,16 @@
 
     May raise Errors.ConfigError if a problem is detected with while parsing.
     '''
-    if not os.path.exists(configfile):
-        raise Errors.ConfigError, 'No such config file %s' % configfile
 
     StartupConf.installroot.default = root
     startupconf = StartupConf()
 
-    parser = IncludingConfigParser()
-    parser.read(configfile)
+    parser = ConfigParser()
+    confpp_obj = ConfigPreProcessor(configfile)
+    try:
+        parser.readfp(confpp_obj)
+    except ParsingError, e:
+        raise Errors.ConfigError("Parsing file failed: %s" % e)
     startupconf.populate(parser, 'main')
 
     # Check that plugin paths are all absolute

Index: parser.py
===================================================================
RCS file: /home/groups/yum/cvs/yum/yum/parser.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- parser.py	5 Jul 2006 14:32:49 -0000	1.7
+++ parser.py	2 Oct 2006 03:46:57 -0000	1.8
@@ -7,6 +7,8 @@
 import os.path
 from ConfigParser import ConfigParser, NoSectionError, NoOptionError
 
+import Errors
+
 #TODO: better handling of recursion
 #TODO: ability to handle bare includes (ie. before first [section])
 #TODO: avoid include line reordering on write
@@ -276,6 +278,188 @@
 
     return ''.join(done)
 
+class ConfigPreProcessor:
+    """
+    ConfigParser Include Pre-Processor
+    
+        File-like Object capable of pre-processing include= lines for
+        a ConfigParser. 
+        
+        The readline function expands lines matching include=(url)
+        into lines from the url specified. Includes may occur in
+        included files as well. 
+        
+        Suggested Usage:
+            cfg = ConfigParser.ConfigParser()
+            fileobj = confpp( fileorurl )
+            cfg.readfp(fileobj)
+    """
+    
+    
+    def __init__(self, configfile, vars=None):
+        # put the vars away in a helpful place
+        self._vars = vars
+        
+        # set some file-like object attributes for ConfigParser
+        # these just make confpp look more like a real file object.
+        self.mode = 'r' 
+        
+        # establish whether to use urlgrabber or urllib
+        # we want to use urlgrabber if it supports urlopen
+        if hasattr(urlgrabber.grabber, 'urlopen'):
+            self._urlresolver = urlgrabber.grabber
+        else: 
+            self._urlresolver = urllib
+        
+        
+        # first make configfile a url even if it points to 
+        # a local file
+        scheme = urlparse.urlparse(configfile)[0]
+        if scheme == '':
+            # check it to make sure it's not a relative file url
+            if configfile[0] != '/':
+                configfile = os.getcwd() + '/' + configfile
+            url = 'file://' + configfile
+        else:
+            url = configfile
+        
+        # these are used to maintain the include stack and check
+        # for recursive/duplicate includes
+        self._incstack = []
+        self._alreadyincluded = []
+        
+        # _pushfile will return None if he couldn't open the file
+        fo = self._pushfile( url )
+        if fo is None: 
+            raise Errors.ConfigError, 'Error accessing file: %s' % url
+        
+    def readline( self, size=0 ):
+        """
+        Implementation of File-Like Object readline function. This should be
+        the only function called by ConfigParser according to the python docs.
+        We maintain a stack of real FLOs and delegate readline calls to the 
+        FLO on top of the stack. When EOF occurs on the topmost FLO, it is 
+        popped off the stack and the next FLO takes over. include= lines 
+        found anywhere cause a new FLO to be opened and pushed onto the top 
+        of the stack. Finally, we return EOF when the bottom-most (configfile
+        arg to __init__) FLO returns EOF.
+        
+        Very Technical Pseudo Code:
+        
+        def confpp.readline() [this is called by ConfigParser]
+            open configfile, push on stack
+            while stack has some stuff on it
+                line = readline from file on top of stack
+                pop and continue if line is EOF
+                if line starts with 'include=' then
+                    error if file is recursive or duplicate
+                    otherwise open file, push on stack
+                    continue
+                else
+                    return line
+            
+            return EOF
+        """
+        
+        # set line to EOF initially. 
+        line=''
+        while len(self._incstack) > 0:
+            # peek at the file like object on top of the stack
+            fo = self._incstack[-1]
+            line = fo.readline()
+            if len(line) > 0:
+                m = re.match( r'\s*include\s*=\s*(?P<url>.*)', line )
+                if m:
+                    url = m.group('url')
+                    if len(url) == 0:
+                        raise Errors.ConfigError, \
+                             'Error parsing config %s: include must specify file to include.' % (self.name)
+                    else:
+                        # whooohoo a valid include line.. push it on the stack
+                        fo = self._pushfile( url )
+                else:
+                    # line didn't match include=, just return it as is
+                    # for the ConfigParser
+                    break
+            else:
+                # the current file returned EOF, pop it off the stack.
+                self._popfile()
+        
+        # at this point we have a line from the topmost file on the stack
+        # or EOF if the stack is empty
+        if vars:
+            return varReplace(line, self._vars)
+        return line
+    
+    
+    def _absurl( self, url ):
+        """
+        Returns an absolute url for the (possibly) relative
+        url specified. The base url used to resolve the
+        missing bits of url is the url of the file currently
+        being included (i.e. the top of the stack).
+        """
+        
+        if len(self._incstack) == 0:
+            # it's the initial config file. No base url to resolve against.
+            return url
+        else:
+            return urlparse.urljoin( self.geturl(), url )
+    
+    
+    def _pushfile( self, url ):
+        """
+        Opens the url specified, pushes it on the stack, and 
+        returns a file like object. Returns None if the url 
+        has previously been included.
+        If the file can not be opened this function exits.
+        """
+        
+        # absolutize this url using the including files url
+        # as a base url.
+        absurl = self._absurl(url)
+        # check if this has previously been included.
+        if self._urlalreadyincluded(absurl):
+            return None
+        try:
+            fo = self._urlresolver.urlopen(absurl)
+        except urlgrabber.grabber.URLGrabError, e:
+            fo = None
+        if fo is not None:
+            self.name = absurl
+            self._incstack.append( fo )
+            self._alreadyincluded.append(absurl)
+        else:
+            raise Errors.ConfigError, \
+                  'Error accessing file for config %s' % (absurl)
+
+        return fo
+    
+    
+    def _popfile( self ):
+        """
+        Pop a file off the stack signaling completion of including that file.
+        """
+        fo = self._incstack.pop()
+        fo.close()
+        if len(self._incstack) > 0:
+            self.name = self._incstack[-1].geturl()
+        else:
+            self.name = None
+    
+    
+    def _urlalreadyincluded( self, url ):
+        """
+        Checks if the url has already been included at all.. this 
+        does not necessarily have to be recursive
+        """
+        for eurl in self._alreadyincluded:
+            if eurl == url: return 1
+        return 0
+    
+    
+    def geturl(self): return self.name
+
 
 def _test():
     import sys




More information about the Yum-cvs-commits mailing list