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