]> granicus.if.org Git - python/commitdiff
Several changes:
authorBarry Warsaw <barry@python.org>
Wed, 1 Jul 1998 20:41:12 +0000 (20:41 +0000)
committerBarry Warsaw <barry@python.org>
Wed, 1 Jul 1998 20:41:12 +0000 (20:41 +0000)
1. Convert to using re module

2. Added two new exception classes

    a. MissingSectionHeaderError which signals an early parsing
       exception when options appear in the file before any section
       header.  Previously a bogus TypeError was thrown deeper down.

    b. ParsingError which collates any non-fatal parsing errors.
       ConfigParser.read() will raise this after the entire file was
       parsed if any errors occurred during parsing (client could just
       catch the exception and continue, because the ConfigParser
       instance would still be initialized with the valid data).

   (small note: Error.__msg => Error._msg)

3. ConfigParser.__read() now uses re which has the following minor
   semantic change: underscore is now allowed in section header and
   option name.  Also, because of the old regexps, theoretically.
   Fixed continuation line bug reported by F. Lundh.

4. It seemed that the old ConfigParser automatically added the option
   `name' to every section, which contained the name of the section.
   This seemed bogus to me so I took it out.

Lib/ConfigParser.py

index 957222c27f77657dcd38ffb10cdc182e0f08c34b..f22306a720163d661ef8b8a1ce0db61730abfc8f 100644 (file)
@@ -2,10 +2,11 @@
 
 A setup file consists of sections, lead by a "[section]" header,
 and followed by "name: value" entries, with continuations and such in
-the style of rfc822.
+the style of RFC 822.
+
+The option values can contain format strings which refer to other values in
+the same section, or values in a special [DEFAULT] section.
 
-The option values can contain format strings which refer to other
-values in the same section, or values in a special [DEFAULT] section.
 For example:
 
     something: %(dir)s/whatever
@@ -55,14 +56,7 @@ ConfigParser -- responsible for for parsing a list of
 
 import sys
 import string
-import regex
-from types import ListType
-
-
-SECTHEAD_RE = "^\[\([-A-Za-z0-9]*\)\][" + string.whitespace + "]*$"
-secthead_cre = regex.compile(SECTHEAD_RE)
-OPTION_RE = "^\([-A-Za-z0-9.]+\)\(:\|[" + string.whitespace + "]*=\)\(.*\)$"
-option_cre = regex.compile(OPTION_RE)
+import re
 
 DEFAULTSECT = "DEFAULT"
 
@@ -71,9 +65,9 @@ DEFAULTSECT = "DEFAULT"
 # exception classes
 class Error:
     def __init__(self, msg=''):
-        self.__msg = msg
+        self._msg = msg
     def __repr__(self):
-        return self.__msg
+        return self._msg
 
 class NoSectionError(Error):
     def __init__(self, section):
@@ -101,6 +95,26 @@ class InterpolationError(Error):
         self.option = option
         self.section = section
 
+class MissingSectionHeaderError(Error):
+    def __init__(self, filename, lineno, line):
+        Error.__init__(
+            self,
+            'File contains no section headers.\nfile: %s, line: %d\n%s' %
+            (filename, lineno, line))
+        self.filename = filename
+        self.lineno = lineno
+        self.line = line
+
+class ParsingError(Error):
+    def __init__(self, filename):
+        Error.__init__(self, 'File contains parsing errors: %s' % filename)
+        self.filename = filename
+        self.errors = []
+
+    def append(self, lineno, line):
+        self.errors.append((lineno, line))
+        self._msg = self._msg + '\n\t[line %2d]: %s' % (lineno, line)
+
 
 \f
 class ConfigParser:
@@ -159,7 +173,8 @@ class ConfigParser:
         """Get an option value for a given section.
 
         All % interpolations are expanded in the return values, based
-        on the defaults passed into the constructor.
+        on the defaults passed into the constructor, unless the optional
+        argument `raw' is true.
 
         The section DEFAULT is special.
         """
@@ -201,6 +216,24 @@ class ConfigParser:
             raise ValueError, 'Not a boolean: %s' % v
         return val
 
+    #
+    # Regular expressions for parsing section headers and options.  Note a
+    # slight semantic change from the previous version, because of the use
+    # of \w, _ is allowed in section header names.
+    __SECTCRE = re.compile(
+        r'\['                                 # [
+        r'(?P<header>[-\w]+)'                 # `-', `_' or any alphanum
+        r'\]'                                 # ]
+        )
+    __OPTCRE = re.compile(
+        r'(?P<option>[-.\w]+)'                # - . _ alphanum
+        r'[ \t]*[:=][ \t]*'                   # any number of space/tab,
+                                              # followed by separator
+                                              # (either : or =), followed
+                                              # by any # space/tab
+        r'(?P<value>.*)$'                     # everything up to eol
+        )
+
     def __read(self, fp):
         """Parse a sectioned setup file.
 
@@ -211,9 +244,10 @@ class ConfigParser:
         leading whitespace.  Blank lines, lines beginning with a '#',
         and just about everything else is ignored.
         """
-        cursect = None                  # None, or a dictionary
+        cursect = None                            # None, or a dictionary
         optname = None
         lineno = 0
+        e = None                                  # None, or an exception
         while 1:
             line = fp.readline()
             if not line:
@@ -226,31 +260,47 @@ class ConfigParser:
                and line[0] == "r":      # no leading whitespace
                 continue
             # continuation line?
-            if line[0] in ' \t' and cursect <> None and optname:
+            if line[0] in ' \t' and cursect is not None and optname:
                 value = string.strip(line)
                 if value:
-                    cursect = cursect[optname] + '\n ' + value
-            # a section header?
-            elif secthead_cre.match(line) >= 0:
-                sectname = secthead_cre.group(1)
-                if self.__sections.has_key(sectname):
-                    cursect = self.__sections[sectname]
-                elif sectname == DEFAULTSECT:
-                    cursect = self.__defaults
-                else:
-                    cursect = {'name': sectname}
-                    self.__sections[sectname] = cursect
-                # So sections can't start with a continuation line.
-                optname = None
-            # an option line?
-            elif option_cre.match(line) >= 0:
-                optname, optval = option_cre.group(1, 3)
-                optname = string.lower(optname)
-                optval = string.strip(optval)
-                # allow empty values
-                if optval == '""':
-                    optval = ''
-                cursect[optname] = optval
-            # an error
+                    cursect[optname] = cursect[optname] + '\n ' + value
+            # a section header or option header?
             else:
-                print 'Error in %s at %d: %s', (fp.name, lineno, `line`)
+                # is it a section header?
+                mo = self.__SECTCRE.match(line)
+                if mo:
+                    sectname = mo.group('header')
+                    if self.__sections.has_key(sectname):
+                        cursect = self.__sections[sectname]
+                    elif sectname == DEFAULTSECT:
+                        cursect = self.__defaults
+                    else:
+                        cursect = {}
+                        self.__sections[sectname] = cursect
+                    # So sections can't start with a continuation line
+                    optname = None
+                # no section header in the file?
+                elif cursect is None:
+                    raise MissingSectionHeaderError(fp.name, lineno, `line`)
+                # an option line?
+                else:
+                    mo = self.__OPTCRE.match(line)
+                    if mo:
+                        optname, optval = mo.group('option', 'value')
+                        optname = string.lower(optname)
+                        optval = string.strip(optval)
+                        # allow empty values
+                        if optval == '""':
+                            optval = ''
+                        cursect[optname] = optval
+                    else:
+                        # a non-fatal parsing error occurred.  set up the
+                        # exception but keep going. the exception will be
+                        # raised at the end of the file and will contain a
+                        # list of all bogus lines
+                        if not e:
+                            e = ParsingError(fp.name)
+                        e.append(lineno, `line`)
+        # if any parsing errors occurred, raise an exception
+        if e:
+            raise e