Support ini section header like [section "name"].
authorHuang Hao <hao.h.huang@intel.com>
Fri, 7 Sep 2012 05:11:07 +0000 (13:11 +0800)
committerZhang Qiang <qiang.z.zhang@intel.com>
Sat, 8 Sep 2012 06:55:15 +0000 (14:55 +0800)
Add a class SectionPattern to support section header style like
[section "name"]. It's also compatible with style like [section].

Change-Id: I6c35eb00a2f3be912cee34f421173fb0523aa191

gitbuildsys/conf.py
tests/test_config.py
tests/testdata/ini/home1.ini
tests/testdata/ini/project1.ini

index 3199608..49b7066 100644 (file)
 
 from __future__ import with_statement
 import os
+import re
+import ast
 import base64
 from ConfigParser import SafeConfigParser, NoSectionError, NoOptionError, \
                          MissingSectionHeaderError
 
 from gitbuildsys import msger, errors
 
+
+class SectionPattern(object):
+    '''Pattern of section that support [section "name"] and [section].
+    1. If there is white-space in section header, it must obey the format like:
+        section_type white_spaces section_name,
+    section_name could be any string.
+    2. otherwise section name is the whole string in brackets
+    '''
+
+    SECTCRE = re.compile(
+        r'\['                            # [
+        r'(?P<header>[^] \t]+)'          # section name without any white-space
+            r'([ \t]+'                   # or
+            r'(?P<name>[^]]+)'           # section type and section name
+            r')?'                        # this section name is optional
+        r'\]'                            # ]
+        )
+
+    class MatchObject(object):
+        '''Match object for SectionPattern'''
+
+        def __init__(self, match):
+            self.match = match
+
+        def group(self, _group1):
+            '''return a tuple(type, name) if section has a name,
+            otherwise return a string as section name
+            '''
+            type_ = self.match.group('header')
+            name = self.match.group('name')
+            if not name:
+                return type_
+
+            name = self.evalute_string(name)
+            return type_, name
+
+        @staticmethod
+        def evalute_string(string):
+            '''safely evaluate string'''
+            if string.startswith('"') or string.startswith("'"):
+                return ast.literal_eval(string)
+            return string
+
+    def match(self, string):
+        '''return MatchObject if string match the pattern'''
+        match = self.SECTCRE.match(string)
+        return self.MatchObject(match) if match else match
+
+
 class BrainConfigParser(SafeConfigParser):
     """Standard ConfigParser derived class which can reserve most of the
     comments, indents, and other user customized stuff inside the ini file.
     """
 
+    SECTCRE = SectionPattern()
+
     def read(self, filenames):
         """Limit the read() only support one input file. It's enough for
         current case.
@@ -340,10 +393,10 @@ distconf = $build__distconf
             return self.DEFAULTS[section][opt]
 
         if not sect_found:
-            raise errors.ConfigError('no section %s' % section)
+            raise errors.ConfigError('no section %s' % str(section))
         else:
             raise errors.ConfigError('no opt: %s in section %s' \
-                                     % (opt, section))
+                                     % (opt, str(section)))
 
     def check_opt(self, opt, section='general'):
         if section in self.DEFAULTS and \
index 79c3e82..87f799a 100644 (file)
@@ -134,6 +134,33 @@ class ConfigGettingTest(unittest.TestCase):
         '''value can be overwrite if name is the same'''
         self.assertEqual('projv1', self.get('section', 'common_key'))
 
+    @Fixture(project='project1.ini')
+    def test_get_named_section(self):
+        '''get value from named section'''
+        self.assertEquals('projv4', self.get(('profile', 'rsa'), 'proj_only'))
+
+    @Fixture(home='home1.ini', project='project1.ini')
+    def test_inherit_named_section(self):
+        '''value can be inherit from named section correctly'''
+        self.assertEquals('homev4', self.get(('profile', 'rsa'), 'home_only'))
+
+    @Fixture(home='home1.ini', project='project1.ini')
+    def test_overwrite_named_section(self):
+        '''value can be overwrite from named section correctly'''
+        self.assertEquals('projv3', self.get(('profile', 'rsa'), 'common'))
+
+    @Fixture(project='project1.ini')
+    def test_no_such_named_section(self):
+        '''test no such section'''
+        self.assertRaises(errors.ConfigError,
+                          self.get, ('profile', 'NOT_EXISTS'), 'key')
+
+    @Fixture(project='project1.ini')
+    def test_no_such_option_in_named_section(self):
+        '''test no such section'''
+        self.assertRaises(errors.ConfigError,
+                          self.get, ('profile', 'rsa'), 'not_exists_option')
+
 
 if __name__ == '__main__':
     unittest.main()
\ No newline at end of file
index c711620..39daae6 100644 (file)
@@ -1,3 +1,7 @@
 [section]
 common_key = homev1
-home_only_key = homev2
\ No newline at end of file
+home_only_key = homev2
+
+[profile "rsa"]
+common = homev3
+home_only = homev4
index c60aceb..20eeb19 100644 (file)
@@ -1,3 +1,7 @@
 [section]
 common_key = projv1
-proj_only_key = projv2
\ No newline at end of file
+proj_only_key = projv2
+
+[profile "rsa"]
+common = projv3
+proj_only = projv4