Rev 5771: Merge config-abstract-store into config-concrete-stores in file:///home/vila/src/bzr/experimental/config/

Vincent Ladeuil v.ladeuil+lp at free.fr
Tue Apr 12 08:04:08 UTC 2011


At file:///home/vila/src/bzr/experimental/config/

------------------------------------------------------------
revno: 5771 [merge]
revision-id: v.ladeuil+lp at free.fr-20110412080407-isi0ke3tpobwqcie
parent: v.ladeuil+lp at free.fr-20110409201235-3it28ixix0213rt1
parent: v.ladeuil+lp at free.fr-20110412072636-2ovjedcp9u7pg0vo
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: config-concrete-stores
timestamp: Tue 2011-04-12 10:04:07 +0200
message:
  Merge config-abstract-store into config-concrete-stores
modified:
  bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
  bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
-------------- next part --------------
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py	2011-04-09 20:12:35 +0000
+++ b/bzrlib/config.py	2011-04-12 08:04:07 +0000
@@ -2138,8 +2138,9 @@
 class Store(object):
     """Abstract interface to persistent storage for configuration options."""
 
-    def __init__(self):
-        self.loaded = False
+    @property
+    def loaded(self):
+        raise NotImplementedError(self.loaded)
 
     def load(self):
         raise NotImplementedError(self.load)
@@ -2174,51 +2175,36 @@
         super(ConfigObjStore, self).__init__()
         self.transport = transport
         self.file_name = file_name
-        # No transient content is known initially
-        self._content = None
-
-    @classmethod
-    def from_string(cls, str_or_unicode, transport, file_name):
+        self._config_obj = None
+
+    @property
+    def loaded(self):
+        return self._config_obj != None
+
+    def load(self):
+        """Load the store from the associated file."""
+        if self.loaded:
+            return
+        content = self.transport.get_bytes(self.file_name)
+        self._load_from_string(content)
+
+    def _load_from_string(self, str_or_unicode):
         """Create a config store from a string.
 
         :param str_or_unicode: A string representing the file content. This will
             be utf-8 encoded internally.
 
-        :param transport: The transport object where the config file is located.
-
-        :param file_name: The configuration file basename.
-        """
-        conf = cls(transport=transport, file_name=file_name)
-        conf._create_from_string(str_or_unicode)
-        return conf
-
-    def _create_from_string(self, str_or_unicode):
-        # We just keep the content waiting for load() to be called when needed
-        self._content = StringIO(str_or_unicode.encode('utf-8'))
-
-    def load(self, allow_no_such_file=False):
-        """Load the store from the associated file.
-
-        :param allow_no_such_file: Swallow the NoSuchFile exception if True.
-            This allows delayed loading when creating the first option ever.
+        This is for tests and should not be used in production unless a
+        convincing use case can be demonstrated :)
         """
         if self.loaded:
-            return
-        if self._content is not None:
-            co_input = self._content
-        else:
-            try:
-                content = self.transport.get_bytes(self.file_name)
-            except errors.NoSuchFile:
-                if allow_no_such_file:
-                    content = ''
-                else:
-                    raise
-            co_input =  StringIO(content)
+            raise AssertionError('Already loaded: %r' % (self._config_obj,))
+        co_input = StringIO(str_or_unicode.encode('utf-8'))
         try:
             # The config files are always stored utf8-encoded
             self._config_obj = ConfigObj(co_input, encoding='utf-8')
         except configobj.ConfigObjError, e:
+            self._config_obj = None
             # FIXME: external_url should really accepts an optional relpath
             # parameter (bug #750169) :-/ -- vila 2011-04-04
             # The following will do in the interim but maybe we don't want to
@@ -2227,14 +2213,14 @@
             file_path = os.path.join(self.transport.external_url(),
                                      self.file_name)
             raise errors.ParseConfigError(e.errors, file_path)
-        self.loaded = True
 
     def save(self):
+        if not self.loaded:
+            # Nothing to save
+            return
         out = StringIO()
         self._config_obj.write(out)
         self.transport.put_bytes(self.file_name, out.getvalue())
-        # We don't need the transient content anymore
-        self._content = None
 
     def get_sections(self):
         """Get the configobj section in the file order.
@@ -2245,17 +2231,27 @@
         self.load()
         cobj = self._config_obj
         if cobj.scalars:
+
+# use self.readonly_section_kls
+
             yield ReadOnlySection(None, cobj)
         for section_name in cobj.sections:
             yield ReadOnlySection(section_name, cobj[section_name])
 
     def get_mutable_section(self, section_name=None):
         # We need a loaded store
-        self.load(allow_no_such_file=True)
+        try:
+            self.load()
+        except errors.NoSuchFile:
+            # The file doesn't exist, let's pretend it was empty
+            self._load_from_string('')
         if section_name is None:
             section = self._config_obj
         else:
             section = self._config_obj.setdefault(section_name, {})
+
+# use self.mutable_section_kls
+
         return MutableSection(section_name, section)
 
 

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2011-04-09 20:12:35 +0000
+++ b/bzrlib/tests/test_config.py	2011-04-12 08:04:07 +0000
@@ -1826,19 +1826,19 @@
 
     def test_get_unkown_option(self):
         a_dict = dict()
-        section = config.ReadOnlySection('myID', a_dict)
+        section = config.ReadOnlySection(None, a_dict)
         self.assertEquals('out of thin air',
                           section.get('foo', 'out of thin air'))
 
     def test_options_is_shared(self):
         a_dict = dict()
-        section = config.ReadOnlySection('myID', a_dict)
+        section = config.ReadOnlySection(None, a_dict)
         self.assertIs(a_dict, section.options)
 
 
 class TestConfigMutableSection(tests.TestCase):
 
-    # FIXME: Parametrize so that all sections (includind os.envrion and the
+    # FIXME: Parametrize so that all sections (includind os.environ and the
     # ones produced by Stores) run these tests -- vila 2011-04-01
 
     def test_set(self):
@@ -1898,12 +1898,13 @@
     If provided, the content is added to the store but without saving it on
     disk. It should be a string or a unicode string in the ConfigObj syntax.
     While this poses a constraint on other store implementations, it keeps a
-    simple syntax usable by test writers.
+    simple syntax usable by test writers. Note that the other store
+    implementations can rely on ConfigObj to parse the content and get the
+    option definitions and values from it.
     """
-    if content is None:
-        store = config.ConfigObjStore(transport, file_name)
-    else:
-        store = config.ConfigObjStore.from_string(content, transport, file_name)
+    store = config.ConfigObjStore(transport, file_name)
+    if content is not None:
+        store._load_from_string(content)
     return store
 
 
@@ -1933,13 +1934,6 @@
         store.load()
         self.assertEquals(True, store.loaded)
 
-    def test_from_string_delayed_load(self):
-        store = self.get_store('foo.conf', '')
-        self.assertEquals(False, store.loaded)
-        store.load()
-        # We loaded the store from the provided content
-        self.assertEquals(True, store.loaded)
-
     def test_get_no_sections_for_empty(self):
         store = self.get_store('foo.conf', '')
         store.load()
@@ -1966,12 +1960,20 @@
         return self._get_store(
             self.get_transport(), file_name, content=content)
 
-    def test_save_empty_succeeds(self):
-        store = self.get_store('foo.conf', '')
-        store.load()
+    def test_save_empty_creates_no_file(self):
+        store = self.get_store('foo.conf')
+        store.save()
         self.assertEquals(False, self.get_transport().has('foo.conf'))
+
+    def test_save_emptied_succeeds(self):
+        store = self.get_store('foo.conf', 'foo=bar\n')
+        section = store.get_mutable_section(None)
+        section.remove('foo')
         store.save()
         self.assertEquals(True, self.get_transport().has('foo.conf'))
+        modified_store = self.get_store('foo.conf')
+        sections = list(modified_store.get_sections())
+        self.assertLength(0, sections)
 
     def test_save_with_content_succeeds(self):
         store = self.get_store('foo.conf', 'foo=bar\n')
@@ -2022,10 +2024,11 @@
         self.assertRaises(errors.NoSuchFile, store.load)
 
     def test_invalid_content(self):
-        store = config.ConfigObjStore.from_string(
-            'this is invalid !', self.get_transport(), 'foo.conf', )
+        store = config.ConfigObjStore(self.get_transport(), 'foo.conf', )
         self.assertEquals(False, store.loaded)
-        exc = self.assertRaises(errors.ParseConfigError, store.load)
+        exc = self.assertRaises(
+            errors.ParseConfigError, store._load_from_string,
+            'this is invalid !')
         self.assertEndsWith(exc.filename, 'foo.conf')
         # And the load failed
         self.assertEquals(False, store.loaded)
@@ -2035,7 +2038,8 @@
         # option names share the same name space...)
         # FIXME: This should be fixed by forbidding dicts as values ?
         # -- vila 2011-04-05
-        store = config.ConfigObjStore.from_string('''
+        store = config.ConfigObjStore(self.get_transport(), 'foo.conf', )
+        store._load_from_string('''
 foo=bar
 l=1,2
 [DEFAULT]
@@ -2046,7 +2050,7 @@
 foo_in_baz=barbaz
 [[qux]]
 foo_in_qux=quux
-''', self.get_transport(), 'foo.conf')
+''')
         sections = list(store.get_sections())
         self.assertLength(4, sections)
         # The default section has no name.



More information about the bazaar-commits mailing list