[conduit/new-settings] Initial work to improve settings serialization
- From: John Stowers <jstowers src gnome org>
- To: svn-commits-list gnome org
- Subject: [conduit/new-settings] Initial work to improve settings serialization
- Date: Fri, 17 Apr 2009 05:30:54 -0400 (EDT)
commit 424cbf4c704d30eb23ce099220f6d7b4098f7b77
Author: John Stowers <john stowers gmail com>
Date: Fri Apr 17 21:27:34 2009 +1200
Initial work to improve settings serialization
Improvements:
* Modularises code
* Supports nested settings, i.e. lists of strings, etc
---
conduit/ModuleWrapper.py | 7 +-
conduit/SyncSet.py | 24 +++--
conduit/XMLSerialization.py | 176 +++++++++++++++++++++++++++++++++
conduit/dataproviders/DataProvider.py | 60 +++---------
4 files changed, 212 insertions(+), 55 deletions(-)
diff --git a/conduit/ModuleWrapper.py b/conduit/ModuleWrapper.py
index 2ba1c36..a93ac43 100644
--- a/conduit/ModuleWrapper.py
+++ b/conduit/ModuleWrapper.py
@@ -245,8 +245,8 @@ class ModuleWrapper:
return self.descriptiveIcon
- def set_configuration_xml(self, xmltext):
- self.module.set_configuration_xml(xmltext)
+ def set_configuration_xml(self, xmltext, xmlversion):
+ self.module.set_configuration_xml(xmltext, xmlversion)
def get_configuration_xml(self):
return self.module.get_configuration_xml()
@@ -282,8 +282,9 @@ class PendingDataproviderWrapper(ModuleWrapper):
def get_key(self):
return self.key
- def set_configuration_xml(self, xmltext):
+ def set_configuration_xml(self, xmltext, xmlversion):
self.xmltext = xmltext
+ self.xmlversion = xmlversion
def get_configuration_xml(self):
return self.xmltext
diff --git a/conduit/SyncSet.py b/conduit/SyncSet.py
index 857ae5d..84fef59 100644
--- a/conduit/SyncSet.py
+++ b/conduit/SyncSet.py
@@ -17,7 +17,7 @@ import conduit.Settings as Settings
#Increment this number when the xml settings file
#changes format
-SETTINGS_VERSION = "1"
+SETTINGS_VERSION = "2"
class SyncSet(gobject.GObject):
"""
@@ -55,7 +55,7 @@ class SyncSet(gobject.GObject):
except Exception:
log.warn("Could not uninitialize %s" % dp, exc_info=True)
- def _restore_dataprovider(self, cond, wrapperKey, dpName="", dpxml="", trySourceFirst=True):
+ def _restore_dataprovider(self, cond, wrapperKey, dpName="", dpxml="", xml_version=SETTINGS_VERSION, trySourceFirst=True):
"""
Adds the dataprovider back onto the canvas at the specifed
location and configures it with the given settings
@@ -68,7 +68,7 @@ class SyncSet(gobject.GObject):
if dpxml:
for i in dpxml.childNodes:
if i.nodeType == i.ELEMENT_NODE and i.localName == "configuration":
- wrapper.set_configuration_xml(xmltext=i.toxml())
+ wrapper.set_configuration_xml(xmltext=i.toxml(), xmlversion=xml_version)
cond.add_dataprovider(wrapper, trySourceFirst)
def on_dataprovider_available_unavailable(self, loader, dpw):
@@ -208,10 +208,20 @@ class SyncSet(gobject.GObject):
#check the xml file is in a version we can read.
if doc.documentElement.hasAttribute("settings-version"):
- if SETTINGS_VERSION != doc.documentElement.getAttribute("settings-version"):
- log.info("%s xml file is incorrect version" % xmlSettingFilePath)
+ xml_version = doc.documentElement.getAttribute("settings-version")
+ try:
+ xml_version = int(xml_version)
+ except ValueError, TypeError:
+ log.error("%s xml file version is not valid" % xmlSettingFilePath)
+ os.remove(xmlSettingFilePath)
+ return
+ if int(SETTINGS_VERSION) < xml_version:
+ log.warning("%s xml file is incorrect version" % xmlSettingFilePath)
os.remove(xmlSettingFilePath)
return
+ else:
+ log.info("%s xml file version not found, assuming version 1" % xmlSettingFilePath)
+ xml_version = 1
#Parse...
for conds in doc.getElementsByTagName("conduit"):
@@ -242,7 +252,7 @@ class SyncSet(gobject.GObject):
name = i.getAttribute("name")
#add to canvas
if len(key) > 0:
- self._restore_dataprovider(cond, key, name, i, True)
+ self._restore_dataprovider(cond, key, name, i, xml_version, True)
#many datasinks
elif i.nodeType == i.ELEMENT_NODE and i.localName == "datasinks":
#each datasink
@@ -252,7 +262,7 @@ class SyncSet(gobject.GObject):
name = sink.getAttribute("name")
#add to canvas
if len(key) > 0:
- self._restore_dataprovider(cond, key, name, sink, False)
+ self._restore_dataprovider(cond, key, name, sink, xml_version, False)
except:
log.warn("Error parsing %s. Exception:\n%s" % (xmlSettingFilePath, traceback.format_exc()))
diff --git a/conduit/XMLSerialization.py b/conduit/XMLSerialization.py
new file mode 100644
index 0000000..f4db41a
--- /dev/null
+++ b/conduit/XMLSerialization.py
@@ -0,0 +1,176 @@
+# Copyright 2009 - Andrew Stomont <andyjstormont googlemail com>
+#
+# This file is part of GPE-Mail.
+#
+# GPE-Mail is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPE-Mail is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GPE-Mail. If not, see <http://www.gnu.org/licenses/>.
+
+from os.path import exists
+from xml.dom.minidom import parseString
+
+class Settings( object ):
+ """
+ A class to store/retrieve data to/from an XML file
+ """
+
+ def __init__(self, xml_text="<configuration/>", xml_version = 2):
+ """
+ Initializes Settings class
+ """
+ self.xml_document = parseString(xml_text)
+ self.xml_version = xml_version
+
+ def __string_to_type__(self, string, desired_type):
+ """
+ Converts a string into the desired scalar type
+ """
+ if desired_type == "bool":
+ return self.__bool_from_string__(string)
+ elif desired_type == "float":
+ return float(string)
+ elif desired_type == "int":
+ return int(string)
+ elif desired_type == "str":
+ return str(string) # 'just in case'
+
+ def __type_as_string__(self, data_type):
+ """
+ Returns string name of given data type
+ """
+ if type(data_type) == list:
+ return "list"
+ elif type(data_type) == tuple:
+ return "tuple"
+ elif type(data_type) == dict:
+ return "dict"
+ elif type(data_type) == int:
+ return "int"
+ elif type(data_type) == float:
+ return "float"
+ elif type(data_type) == str:
+ return "str"
+ elif type(data_type) == bool:
+ return "bool"
+
+ def __bool_from_string__(self, string):
+ """
+ Returns a bool from a string representation
+ """
+ if string == "True":
+ return True
+ else:
+ return False
+
+ def __getitem__(self, name):
+ """
+ Called when variable get via subscript interface
+ """
+ node = self.__get_data_node__(name)
+ if node:
+ return self.__node_to_data__(node)
+ else:
+ raise KeyError(name)
+
+ def __setitem__(self, name, value):
+ """
+ Called when variable set via subscript interface
+ """
+ newNode = self.__data_to_node__(name, value)
+ oldNode = self.__get_data_node__(name)
+ if oldNode:
+ self.xml_document.documentElement.replaceChild(newNode, oldNode)
+ else:
+ self.xml_document.documentElement.appendChild(newNode)
+
+ def __delitem__(self, name):
+ """
+ Deletes item from saved file
+ """
+ node = self.__get_data_node__(name)
+ if node:
+ self.xml_document.documentElement.removeChild(node)
+ else:
+ raise KeyError(name)
+
+ def __contains__(self, name):
+ """
+ This gets called by the 'in' construct
+ """
+ node = self.__get_data_node__(name)
+ if node:
+ return True
+ return False
+
+ def __get_data_node__(self, name):
+ """
+ Returns data node with given name
+ """
+ for node in self.xml_document.documentElement.childNodes:
+ if node.nodeType == node.ELEMENT_NODE and node.nodeName == name:
+ return node
+
+ def __iter__(self):
+ return self.iteritems()
+
+ def iteritems(self):
+ for node in self.xml_document.documentElement.childNodes:
+ if node.nodeType == node.ELEMENT_NODE:
+ yield node.nodeName, self.__node_to_data__(node)
+
+ def __data_to_node__(self, name, data):
+ """
+ Converts a python data type into an xml node
+ """
+ node = self.xml_document.createElement(str(name))
+ node.setAttribute("type", self.__type_as_string__(data))
+ #node.setAttribute("name", str(name))
+ if type(data) == dict:
+ for (index, value) in data.iteritems():
+ node.appendChild( self.__data_to_node__(index, value ))
+ elif type(data) == list:
+ for (index, value) in enumerate(data):
+ node.appendChild(self.__data_to_node__("item", value))
+ else:
+ node.appendChild(self.xml_document.createTextNode(str(data)))
+ return node
+
+ def __node_to_data__(self, node):
+ """
+ Returns python data from data node
+ """
+ node_type = node.getAttribute("type")
+ if node_type == "dict":
+ retval = {}
+ for childNode in node.childNodes:
+ if childNode.nodeType == node.ELEMENT_NODE:
+ retval[childNode.nodeName] = self.__node_to_data__(childNode)
+ return retval
+ elif node_type in ("list", "tuple"):
+ if self.xml_version == 1:
+ retval = node.firstChild.data.split(',')
+ return retval
+ else:
+ retval = []
+ for childNode in node.childNodes:
+ if childNode.nodeType == node.ELEMENT_NODE:
+ #retval.insert(int(childNode.nodeName), self.__node_to_data__(childNode))
+ retval.append(self.__node_to_data__(childNode))
+ if node_type == "tuple":
+ retval = tuple(retval)
+ return retval
+ else:
+ if len(node.childNodes) > 0:
+ return self.__string_to_type__(node.firstChild.data, node_type)
+ else:
+ return ""
+
diff --git a/conduit/dataproviders/DataProvider.py b/conduit/dataproviders/DataProvider.py
index 11d839f..6b2614e 100644
--- a/conduit/dataproviders/DataProvider.py
+++ b/conduit/dataproviders/DataProvider.py
@@ -15,6 +15,7 @@ import conduit
import conduit.ModuleWrapper as ModuleWrapper
import conduit.utils as Utils
import conduit.Settings as Settings
+import conduit.XMLSerialization as XMLSerialization
STATUS_NONE = _("Ready")
STATUS_CHANGE_DETECTED = _("New data to sync")
@@ -269,25 +270,11 @@ class DataProviderBase(gobject.GObject):
Returns the dataprovider configuration as xml
@rtype: C{string}
"""
- doc = xml.dom.minidom.Element("configuration")
+ xml_configuration = XMLSerialization.Settings()
configDict = self.get_configuration()
- for config in configDict:
- configxml = xml.dom.minidom.Element(str(config))
- #store the value and value type
- try:
- vtype = Settings.TYPE_TO_TYPE_NAME[ type(configDict[config]) ]
- value = Settings.TYPE_TO_STRING[ type(configDict[config]) ](configDict[config])
- except KeyError:
- log.warn("Cannot convert %s to string. Value of %s not saved" % (type(configDict[config]), config))
- vtype = Settings.TYPE_TO_TYPE_NAME[str]
- value = Settings.TYPE_TO_STRING[str](configDict[config])
- configxml.setAttribute("type", vtype)
- valueNode = xml.dom.minidom.Text()
- valueNode.data = value
- configxml.appendChild(valueNode)
- doc.appendChild(configxml)
-
- return doc.toxml()
+ for name, value in configDict.iteritems():
+ xml_configuration[name] = value
+ return xml_configuration.xml_document.toxml()
def _get_configuration_parameters(self, configuration):
'''
@@ -378,39 +365,22 @@ class DataProviderBase(gobject.GObject):
callable(getattr(self, c, None)))
)
- def set_configuration_xml(self, xmltext):
+ def set_configuration_xml(self, xmltext, xmlversion):
"""
Restores applications settings from XML
@param xmltext: xml representation of settings
@type xmltext: C{string}
"""
- doc = xml.dom.minidom.parseString(xmltext)
- configxml = doc.documentElement
-
- if configxml.nodeType == configxml.ELEMENT_NODE and configxml.localName == "configuration":
- settings = {}
- for s in configxml.childNodes:
- if s.nodeType == s.ELEMENT_NODE:
- #now convert the setting to the correct type (if filled out)
- if s.hasChildNodes():
- raw = s.firstChild.data
- vtype = s.getAttribute("type")
- try:
- data = Settings.STRING_TO_TYPE[vtype](raw)
- except KeyError:
- #fallback to string type
- log.warn("Cannot convert string (%s) to native type %s\n" % (raw, vtype, traceback.format_exc()))
- data = str(raw)
- settings[s.localName] = data
-
- try:
- self.set_configuration(settings)
- except Exception, err:
- log.warn("Error restoring %s configuration\n%s" %
- (self._name_, traceback.format_exc()))
- else:
- log.debug("Could not find <configuration> xml fragment")
+ xml_configuration = XMLSerialization.Settings(xmltext, xmlversion)
+ settings = {}
+ for name, value in xml_configuration:
+ settings[name] = value
+ try:
+ self.set_configuration(settings)
+ except Exception, err:
+ log.warn("Error restoring %s configuration\n%s" %
+ (self._name_, traceback.format_exc()))
def get_UID(self):
"""
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]