aboutsummaryrefslogtreecommitdiffstats
path: root/libbe/storage/util/settings_object.py
diff options
context:
space:
mode:
authorW. Trevor King <wking@drexel.edu>2010-01-22 11:30:26 -0500
committerW. Trevor King <wking@drexel.edu>2010-01-22 11:30:26 -0500
commitbda68bb5d93f4b608fb1dd17c5a0cf1bb406daf9 (patch)
treec250b47d8f20e7df63fd20495f32e6cb1e985b0c /libbe/storage/util/settings_object.py
parenta818e71b0c3ae00e7c67ce745e49c7cef7ceac55 (diff)
downloadbugseverywhere-bda68bb5d93f4b608fb1dd17c5a0cf1bb406daf9.tar.gz
Reworked settings_object module, but command.init tests still fail:
$ python test.py libbe.command.init Doctest: libbe.command.init.Init ... FAIL ... ----------------------- File ".../libbe/command/init.py", line 47, in libbe.command.init.Init Failed example: ui.run(cmd) Exception raised: Traceback (most recent call last): ... File "/tmp/be.wtk/libbe/command/init.py", line 97, in _run bd = libbe.bugdir.BugDir(storage, from_storage=False) File "/tmp/be.wtk/libbe/bugdir.py", line 185, in __init__ self.save() File "/tmp/be.wtk/libbe/bugdir.py", line 228, in save self.save_settings() File "/tmp/be.wtk/libbe/bugdir.py", line 204, in save_settings mf = mapfile.generate(self._get_saved_settings()) File "/tmp/be.wtk/libbe/storage/util/settings_object.py", line 230, in _get_saved_settings self, self._setting_name_to_attr_name(k)) File "/tmp/be.wtk/libbe/storage/util/properties.py", line 194, in _fget value = fget(self) File "/tmp/be.wtk/libbe/storage/util/properties.py", line 329, in _fget primer(self) File "/tmp/be.wtk/libbe/storage/util/settings_object.py", line 69, in prop_load_settings self.load_settings() File "/tmp/be.wtk/libbe/bugdir.py", line 194, in load_settings self.settings = mapfile.parse(settings_mapfile) File "/tmp/be.wtk/libbe/storage/util/mapfile.py", line 123, in parse c = yaml.load(contents) ... File "/usr/lib/python2.6/site-packages/yaml/reader.py", line 213, in update_raw data = self.stream.read(size) AttributeError: 'NoneType' object has no attribute 'read' ...
Diffstat (limited to 'libbe/storage/util/settings_object.py')
-rw-r--r--libbe/storage/util/settings_object.py345
1 files changed, 226 insertions, 119 deletions
diff --git a/libbe/storage/util/settings_object.py b/libbe/storage/util/settings_object.py
index ca94f23..655e0ed 100644
--- a/libbe/storage/util/settings_object.py
+++ b/libbe/storage/util/settings_object.py
@@ -40,7 +40,7 @@ class _Token (object):
pass
class UNPRIMED (_Token):
- "Property has not been primed."
+ "Property has not been primed (loaded)."
pass
class EMPTY (_Token):
@@ -60,13 +60,13 @@ def prop_save_settings(self, old, new):
def prop_load_settings(self):
"""
- The default action undertaken when an UNPRIMED property is accessed.
+ The default action undertaken when an UNPRIMED property is
+ accessed. Attempt to run .load_settings(), which calls
+ ._setup_saved_settings() internally. If .storage is inaccessible,
+ don't do anything.
"""
- if self.storage != None and self.storage.is_readable() \
- and self._settings_loaded==False:
+ if self.storage != None and self.storage.is_readable():
self.load_settings()
- else:
- self._setup_saved_settings(flag_as_loaded=False)
# Some name-mangling routines for pretty printing setting names
def setting_name_to_attr_name(self, name):
@@ -129,6 +129,12 @@ def versioned_property(name, doc,
* you set change_hook and might have mutable property values
See the docstrings in libbe.properties for details on how each of
these cases are handled.
+
+ The value stored in .settings[name] will be
+ * no value (or UNPRIMED) if the property has been neither set,
+ nor loaded as blank.
+ * EMPTY if the value has been loaded as blank.
+ * some value if the property has been either loaded or set.
"""
settings_properties.append(name)
if require_save == True:
@@ -152,7 +158,8 @@ def versioned_property(name, doc,
% (', '.join(allowed))
hooked = change_hook_property(hook=change_hook, mutable=mutable,
default=EMPTY)
- primed = primed_property(primer=primer, initVal=UNPRIMED)
+ primed = primed_property(primer=primer, initVal=UNPRIMED,
+ unprimeableVal=EMPTY)
settings = settings_property(name=name, null=UNPRIMED)
docp = doc_property(doc=fulldoc)
deco = hooked(primed(settings(docp(funcs))))
@@ -181,7 +188,6 @@ class SavedSettingsObject(object):
_attr_name_to_setting_name = attr_name_to_setting_name
def __init__(self):
- self._settings_loaded = False
self.storage = None
self.settings = {}
@@ -191,17 +197,15 @@ class SavedSettingsObject(object):
self.settings = {}
self._setup_saved_settings()
- def _setup_saved_settings(self, flag_as_loaded=True):
+ def _setup_saved_settings(self):
"""
- To be run after setting self.settings up from disk. Marks all
- settings as primed.
+ To be run after setting self.settings up from disk. Fills in
+ all missing settings entries with EMPTY.
"""
for property in self.settings_properties:
if property not in self.settings \
or self.settings[property] == UNPRIMED:
self.settings[property] = EMPTY
- if flag_as_loaded == True:
- self._settings_loaded = True
def save_settings(self):
"""Save the settings to disk."""
@@ -220,15 +224,16 @@ class SavedSettingsObject(object):
load already.
"""
settings = {}
+ for k in self.settings_properties: # force full load
+ if not k in self.settings or self.settings[k] == UNPRIMED:
+ value = getattr(
+ self, self._setting_name_to_attr_name(k))
for k in self.settings_properties:
- if k in self.settings and \
- not self.settings[k] in [None, EMPTY]:
+ if k in self.settings and self.settings[k] != EMPTY:
settings[k] = self.settings[k]
- else:
- value = getattr(
+ elif k in self.required_saved_properties:
+ settings[k] = getattr(
self, self._setting_name_to_attr_name(k))
- if value not in [None, EMPTY, []]:
- settings[k] = value
return settings
def clear_cached_setting(self, setting=None):
@@ -242,134 +247,245 @@ class SavedSettingsObject(object):
if libbe.TESTING == True:
+ import copy
+
+ class TestStorage (list):
+ def __init__(self):
+ list.__init__(self)
+ self.readable = True
+ self.writeable = True
+ def is_readable(self):
+ return self.readable
+ def is_writeable(self):
+ return self.writeable
+
+ class TestObject (SavedSettingsObject):
+ def load_settings(self):
+ self.load_count += 1
+ if len(self.storage) == 0:
+ self.settings = {}
+ else:
+ self.settings = copy.deepcopy(self.storage[-1])
+ self._setup_saved_settings()
+ def save_settings(self):
+ settings = self._get_saved_settings()
+ self.storage.append(copy.deepcopy(settings))
+ def __init__(self):
+ SavedSettingsObject.__init__(self)
+ self.load_count = 0
+ self.storage = TestStorage()
+
class SavedSettingsObjectTests(unittest.TestCase):
- def testSimpleProperty(self):
- """Testing a minimal versioned property"""
- class Test(SavedSettingsObject):
+ def testSimplePropertyDoc(self):
+ """Testing a minimal versioned property docstring"""
+ class Test (TestObject):
+ settings_properties = []
+ required_saved_properties = []
+ @versioned_property(
+ name="Content-type",
+ doc="A test property",
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties)
+ def content_type(): return {}
+ expected = "A test property\n\nThis property defaults to None."
+ self.failUnless(Test.content_type.__doc__ == expected,
+ Test.content_type.__doc__)
+ def testSimplePropertyFromMemory(self):
+ """Testing a minimal versioned property from memory"""
+ class Test (TestObject):
settings_properties = []
required_saved_properties = []
- @versioned_property(name="Content-type",
- doc="A test property",
- settings_properties=settings_properties,
- required_saved_properties= \
- required_saved_properties)
+ @versioned_property(
+ name="Content-type",
+ doc="A test property",
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties)
def content_type(): return {}
- def __init__(self):
- SavedSettingsObject.__init__(self)
t = Test()
- # access missing setting
- self.failUnless(t._settings_loaded == False, t._settings_loaded)
self.failUnless(len(t.settings) == 0, len(t.settings))
+ # accessing t.content_type triggers the priming, but
+ # t.storage.is_readable() == False, so nothing happens.
+ t.storage.readable = False
+ self.failUnless(t.content_type == None, t.content_type)
+ self.failUnless(t.settings == {}, t.settings)
+ self.failUnless(len(t.settings) == 0, len(t.settings))
+ self.failUnless(t.content_type == None, t.content_type)
+ # accessing t.content_type triggers the priming again, and
+ # now that t.storage.is_readable() == True, this fills out
+ # t.settings with EMPTY data. At this point there should
+ # be one load and no saves.
+ t.storage.readable = True
self.failUnless(t.content_type == None, t.content_type)
- # accessing t.content_type triggers the priming, which runs
- # t._setup_saved_settings, which fills out t.settings with
- # EMPTY data. t._settings_loaded is still false though, since
- # the default priming does not do any of the `official' loading
- # that occurs in t.load_settings.
self.failUnless(len(t.settings) == 1, len(t.settings))
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
- self.failUnless(t._settings_loaded == False, t._settings_loaded)
- # load settings creates an EMPTY value in the settings array
- t.load_settings()
- self.failUnless(t._settings_loaded == True, t._settings_loaded)
- self.failUnless(t.settings["Content-type"] == EMPTY,
- t.settings["Content-type"])
self.failUnless(t.content_type == None, t.content_type)
+ self.failUnless(t.load_count == 1, t.load_count)
+ self.failUnless(len(t.storage) == 0, len(t.storage))
+ # an explicit call to load settings forces a reload,
+ # but nothing else changes.
+ t.load_settings()
self.failUnless(len(t.settings) == 1, len(t.settings))
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
+ self.failUnless(t.content_type == None, t.content_type)
+ self.failUnless(t.load_count == 2, t.load_count)
+ self.failUnless(len(t.storage) == 0, len(t.storage))
# now we set a value
t.content_type = 5
self.failUnless(t.settings["Content-type"] == 5,
t.settings["Content-type"])
+ self.failUnless(t.load_count == 2, t.load_count)
+ self.failUnless(len(t.storage) == 1, len(t.storage))
+ self.failUnless(t.storage == [{'Content-type':5}], t.storage)
+ # getting its value changes nothing
self.failUnless(t.content_type == 5, t.content_type)
self.failUnless(t.settings["Content-type"] == 5,
t.settings["Content-type"])
+ self.failUnless(t.load_count == 2, t.load_count)
+ self.failUnless(len(t.storage) == 1, len(t.storage))
+ self.failUnless(t.storage == [{'Content-type':5}], t.storage)
# now we set another value
t.content_type = "text/plain"
self.failUnless(t.content_type == "text/plain", t.content_type)
self.failUnless(t.settings["Content-type"] == "text/plain",
t.settings["Content-type"])
+ self.failUnless(t.load_count == 2, t.load_count)
+ self.failUnless(len(t.storage) == 2, len(t.storage))
+ self.failUnless(t.storage == [{'Content-type':5},
+ {'Content-type':'text/plain'}],
+ t.storage)
+ # t._get_saved_settings() returns a dict of required or
+ # non-default values.
self.failUnless(t._get_saved_settings() == \
{"Content-type":"text/plain"},
t._get_saved_settings())
# now we clear to the post-primed value
t.content_type = EMPTY
- self.failUnless(t._settings_loaded == True, t._settings_loaded)
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
self.failUnless(t.content_type == None, t.content_type)
self.failUnless(len(t.settings) == 1, len(t.settings))
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
+ self.failUnless(t._get_saved_settings() == {},
+ t._get_saved_settings())
+ self.failUnless(t.storage == [{'Content-type':5},
+ {'Content-type':'text/plain'},
+ {}],
+ t.storage)
+ def testSimplePropertyFromStorage(self):
+ """Testing a minimal versioned property from storage"""
+ class Test (TestObject):
+ settings_properties = []
+ required_saved_properties = []
+ @versioned_property(
+ name="prop-a",
+ doc="A test property",
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties)
+ def prop_a(): return {}
+ @versioned_property(
+ name="prop-b",
+ doc="Another test property",
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties)
+ def prop_b(): return {}
+ t = Test()
+ t.storage.append({'prop-a':'saved'})
+ # setting prop-b forces a load (to check for changes),
+ # which also pulls in prop-a.
+ t.prop_b = 'new-b'
+ settings = {'prop-b':'new-b', 'prop-a':'saved'}
+ self.failUnless(t.settings == settings, t.settings)
+ self.failUnless(t._get_saved_settings() == settings,
+ t._get_saved_settings())
+ # test that _get_saved_settings() works even when settings
+ # were _not_ loaded beforehand
+ t = Test()
+ t.storage.append({'prop-a':'saved'})
+ settings ={'prop-a':'saved'}
+ self.failUnless(t.settings == {}, t.settings)
+ self.failUnless(t._get_saved_settings() == settings,
+ t._get_saved_settings())
+
def testDefaultingProperty(self):
"""Testing a defaulting versioned property"""
- class Test(SavedSettingsObject):
+ class Test (TestObject):
settings_properties = []
required_saved_properties = []
- @versioned_property(name="Content-type",
- doc="A test property",
- default="text/plain",
- settings_properties=settings_properties,
- required_saved_properties= \
- required_saved_properties)
+ @versioned_property(
+ name="Content-type",
+ doc="A test property",
+ default="text/plain",
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties)
def content_type(): return {}
- def __init__(self):
- SavedSettingsObject.__init__(self)
t = Test()
- self.failUnless(t._settings_loaded == False, t._settings_loaded)
+ self.failUnless(t.settings == {}, t.settings)
self.failUnless(t.content_type == "text/plain", t.content_type)
- self.failUnless(t._settings_loaded == False, t._settings_loaded)
- t.load_settings()
- self.failUnless(t._settings_loaded == True, t._settings_loaded)
- self.failUnless(t.content_type == "text/plain", t.content_type)
- self.failUnless(t.settings["Content-type"] == EMPTY,
- t.settings["Content-type"])
- self.failUnless(t._get_saved_settings() ==
- {"Content-type":"text/plain"},
+ self.failUnless(t.settings == {"Content-type":EMPTY},
+ t.settings)
+ self.failUnless(t.load_count == 1, t.load_count)
+ self.failUnless(len(t.storage) == 0, len(t.storage))
+ self.failUnless(t._get_saved_settings() == {},
t._get_saved_settings())
t.content_type = "text/html"
self.failUnless(t.content_type == "text/html",
t.content_type)
- self.failUnless(t.settings["Content-type"] == "text/html",
- t.settings["Content-type"])
+ self.failUnless(t.settings == {"Content-type":"text/html"},
+ t.settings)
+ self.failUnless(t.load_count == 1, t.load_count)
+ self.failUnless(len(t.storage) == 1, len(t.storage))
+ self.failUnless(t.storage == [{'Content-type':'text/html'}],
+ t.storage)
self.failUnless(t._get_saved_settings() == \
{"Content-type":"text/html"},
t._get_saved_settings())
def testRequiredDefaultingProperty(self):
"""Testing a required defaulting versioned property"""
- class Test(SavedSettingsObject):
+ class Test (TestObject):
settings_properties = []
required_saved_properties = []
- @versioned_property(name="Content-type",
- doc="A test property",
- default="text/plain",
- settings_properties=settings_properties,
- required_saved_properties= \
- required_saved_properties,
- require_save=True)
+ @versioned_property(
+ name="Content-type",
+ doc="A test property",
+ default="text/plain",
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties,
+ require_save=True)
def content_type(): return {}
- def __init__(self):
- SavedSettingsObject.__init__(self)
t = Test()
+ self.failUnless(t.settings == {}, t.settings)
+ self.failUnless(t.content_type == "text/plain", t.content_type)
+ self.failUnless(t.settings == {"Content-type":EMPTY},
+ t.settings)
+ self.failUnless(t.load_count == 1, t.load_count)
+ self.failUnless(len(t.storage) == 0, len(t.storage))
self.failUnless(t._get_saved_settings() == \
{"Content-type":"text/plain"},
t._get_saved_settings())
t.content_type = "text/html"
+ self.failUnless(t.content_type == "text/html",
+ t.content_type)
+ self.failUnless(t.settings == {"Content-type":"text/html"},
+ t.settings)
+ self.failUnless(t.load_count == 1, t.load_count)
+ self.failUnless(len(t.storage) == 1, len(t.storage))
+ self.failUnless(t.storage == [{'Content-type':'text/html'}],
+ t.storage)
self.failUnless(t._get_saved_settings() == \
{"Content-type":"text/html"},
t._get_saved_settings())
def testClassVersionedPropertyDefinition(self):
"""Testing a class-specific _versioned property decorator"""
- class Test(SavedSettingsObject):
+ class Test (TestObject):
settings_properties = []
required_saved_properties = []
- def _versioned_property(settings_properties= \
- settings_properties,
- required_saved_properties= \
- required_saved_properties,
- **kwargs):
+ def _versioned_property(
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties,
+ **kwargs):
if "settings_properties" not in kwargs:
kwargs["settings_properties"] = settings_properties
if "required_saved_properties" not in kwargs:
@@ -377,69 +493,60 @@ if libbe.TESTING == True:
required_saved_properties
return versioned_property(**kwargs)
@_versioned_property(name="Content-type",
- doc="A test property",
- default="text/plain",
- require_save=True)
+ doc="A test property",
+ default="text/plain",
+ require_save=True)
def content_type(): return {}
- def __init__(self):
- SavedSettingsObject.__init__(self)
t = Test()
self.failUnless(t._get_saved_settings() == \
{"Content-type":"text/plain"},
t._get_saved_settings())
+ self.failUnless(t.load_count == 1, t.load_count)
+ self.failUnless(len(t.storage) == 0, len(t.storage))
t.content_type = "text/html"
self.failUnless(t._get_saved_settings() == \
{"Content-type":"text/html"},
t._get_saved_settings())
+ self.failUnless(t.load_count == 1, t.load_count)
+ self.failUnless(len(t.storage) == 1, len(t.storage))
+ self.failUnless(t.storage == [{'Content-type':'text/html'}],
+ t.storage)
def testMutableChangeHookedProperty(self):
"""Testing a mutable change-hooked property"""
- SAVES = []
- def prop_log_save_settings(self, old, new, saves=SAVES):
- saves.append("'%s' -> '%s'" % (str(old), str(new)))
- prop_save_settings(self, old, new)
- class Test(SavedSettingsObject):
+ class Test (TestObject):
settings_properties = []
required_saved_properties = []
- @versioned_property(name="List-type",
- doc="A test property",
- mutable=True,
- change_hook=prop_log_save_settings,
- settings_properties=settings_properties,
- required_saved_properties= \
- required_saved_properties)
+ @versioned_property(
+ name="List-type",
+ doc="A test property",
+ mutable=True,
+ change_hook=prop_save_settings,
+ settings_properties=settings_properties,
+ required_saved_properties=required_saved_properties)
def list_type(): return {}
- def __init__(self):
- SavedSettingsObject.__init__(self)
t = Test()
- self.failUnless(t._settings_loaded == False, t._settings_loaded)
- t.load_settings()
- self.failUnless(SAVES == [], SAVES)
- self.failUnless(t._settings_loaded == True, t._settings_loaded)
+ self.failUnless(len(t.storage) == 0, len(t.storage))
self.failUnless(t.list_type == None, t.list_type)
- self.failUnless(SAVES == [], SAVES)
+ self.failUnless(len(t.storage) == 0, len(t.storage))
self.failUnless(t.settings["List-type"]==EMPTY,
t.settings["List-type"])
t.list_type = []
self.failUnless(t.settings["List-type"] == [],
t.settings["List-type"])
- self.failUnless(SAVES == [
- "'<class 'libbe.storage.util.settings_object.EMPTY'>' -> '[]'"
- ], SAVES)
- t.list_type.append(5)
- self.failUnless(SAVES == [
- "'<class 'libbe.storage.util.settings_object.EMPTY'>' -> '[]'",
- ], SAVES)
+ self.failUnless(len(t.storage) == 1, len(t.storage))
+ self.failUnless(t.storage == [{'List-type':[]}],
+ t.storage)
+ t.list_type.append(5) # external modification not detected yet
+ self.failUnless(len(t.storage) == 1, len(t.storage))
+ self.failUnless(t.storage == [{'List-type':[]}],
+ t.storage)
self.failUnless(t.settings["List-type"] == [5],
t.settings["List-type"])
- self.failUnless(SAVES == [ # the append(5) has not yet been saved
- "'<class 'libbe.storage.util.settings_object.EMPTY'>' -> '[]'",
- ], SAVES)
- self.failUnless(t.list_type == [5], t.list_type)#get triggers saved
-
- self.failUnless(SAVES == [ # now the append(5) has been saved.
- "'<class 'libbe.storage.util.settings_object.EMPTY'>' -> '[]'",
- "'[]' -> '[5]'"
- ], SAVES)
+ self.failUnless(t.list_type == [5], t.list_type)# get triggers save
+ self.failUnless(len(t.storage) == 2, len(t.storage))
+ self.failUnless(t.storage == [{'List-type':[]},
+ {'List-type':[5]}],
+ t.storage)
unitsuite = unittest.TestLoader().loadTestsFromTestCase( \
SavedSettingsObjectTests)