Package fife :: Package extensions :: Package serializers :: Module simplexml
[hide private]
[frames] | no frames]

Source Code for Module fife.extensions.serializers.simplexml

  1  # -*- coding: utf-8 -*- 
  2   
  3  # #################################################################### 
  4  #  Copyright (C) 2005-2012 by the FIFE team 
  5  #  http://www.fifengine.net 
  6  #  This file is part of FIFE. 
  7  # 
  8  #  FIFE is free software; you can redistribute it and/or 
  9  #  modify it under the terms of the GNU Lesser General Public 
 10  #  License as published by the Free Software Foundation; either 
 11  #  version 2.1 of the License, or (at your option) any later version. 
 12  # 
 13  #  This library is distributed in the hope that it will be useful, 
 14  #  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 16  #  Lesser General Public License for more details. 
 17  # 
 18  #  You should have received a copy of the GNU Lesser General Public 
 19  #  License along with this library; if not, write to the 
 20  #  Free Software Foundation, Inc., 
 21  #  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
 22  # #################################################################### 
 23   
 24  import os 
 25  from StringIO import StringIO 
 26           
 27  from fife.extensions.serializers import ET, SerializerError, InvalidFormat, NotFound 
 28   
 29   
 30  EMPTY_XML_FILE="""\ 
 31  <?xml version='1.0' encoding='UTF-8'?> 
 32  <Settings> 
 33   
 34  </Settings> 
 35  """ 
 36   
37 -class SimpleSerializer(object):
38 """ 39 Use this as a base class for custom setting loaders/savers to use with the Setting class. 40 """ 41
42 - def __init__(self, filename=None):
43 pass
44
45 - def get(self, module, name, defaultValue=None):
46 pass
47
48 - def set(self, module, name, value, extra_attrs={}):
49 pass
50
51 - def load(self, filename=None):
52 """ 53 @note: If the filename specified is empty this function MUST initialize an empty settings 54 file in whatever format you need. 55 """ 56 pass
57
58 - def save(self, filename=None):
59 pass
60
61 - def getModuleName(self):
62 """ 63 @note: Returns all the module names that are present in the settings.xml file 64 as a list of strings 65 """ 66 pass
67
68 - def getAllSettings(self,module):
69 """ 70 @note: Returns all the setting names under the Module name module as a dictionary structure 71 """ 72 pass
73
74 -class SimpleXMLSerializer(SimpleSerializer):
75 """ 76 This class is a simple interface to get and store data in XML files. 77 78 Usage:: 79 from fife.extensions.serializers.simplexml import SimpleXMLSerializer 80 serializer = SimpleXMLSerializer(filename="somefile.xml") 81 serializer.set("module_name", "variable_name", "value") 82 somevariable = serializer.get("module_name", "variable_name", "default_value") 83 """
84 - def __init__(self, filename=None):
85 self._file = filename 86 self._tree = None 87 self._root_element = None 88 89 self._initialized = False
90
91 - def load(self, filename=None):
92 """ 93 Loads the XML file into memory and validates it. 94 95 Raises a SerializerError exception if the file is not specified. 96 97 @param filename: The file to load 98 @type filename: C{str} 99 100 @note: If the file does not exist it will automatically create a blank file for you. 101 """ 102 if filename: 103 self._file = filename 104 105 if not self._file: 106 raise SerializerError("Cannot load file or create file. No filename specified!") 107 108 if not os.path.exists(self._file): 109 self._tree = ET.parse(StringIO(EMPTY_XML_FILE)) 110 self._tree.write(self._file, 'UTF-8') 111 else: 112 self._tree = ET.parse(self._file) 113 114 self._root_element = self._tree.getroot() 115 self._validateTree()
116
117 - def save(self, filename=None):
118 """ 119 Saves the XML file. 120 121 @param filename: The file to save 122 @type filename: C{str} 123 124 @note: This Overwrites the file if it exists. 125 """ 126 if not self._initialized: 127 self.load() 128 self._initialized = True 129 130 if filename: 131 savefile = filename 132 else: 133 savefile = self._file 134 135 if not savefile: 136 raise SerializerError("Cannot save file. No filename specified!") 137 138 """ Writes the settings to file """ 139 self._indent(self._root_element) 140 self._tree.write(savefile, 'UTF-8')
141 142
143 - def getValue(self, e_type, e_value):
144 if e_type == 'int': 145 return int(e_value) 146 elif e_type == 'float': 147 return float(e_value) 148 elif e_type == 'bool': 149 e_value = e_value.lower() 150 if e_value == "" or e_value == "false" or e_value == "no" or e_value == "0": 151 return False 152 else: 153 return True 154 elif e_type == 'str' or e_type == 'string': 155 return str(e_value) 156 elif e_type == 'unicode': 157 return unicode(e_value) 158 elif e_type == 'list': 159 return self._deserializeList(e_value) 160 elif e_type == 'dict': 161 return self._deserializeDict(e_value)
162 163
164 - def get(self, module, name, defaultValue=None):
165 """ Gets the value of a specified variable 166 167 @param module: Name of the module to get the variable from 168 @param name: Variable name 169 @param defaultValue: Specifies the default value to return if the variable is not found 170 @type defaultValue: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict} 171 """ 172 if not self._initialized: 173 self.load() 174 self._initialized = True 175 176 if not isinstance(name, str) and not isinstance(name, unicode): 177 raise AttributeError("SimpleXMLSerializer.get(): Invalid type for name argument.") 178 179 #get the module tree: for example find tree under module FIFE 180 moduleTree = self._getModuleTree(module) 181 element = None 182 for e in moduleTree.getchildren(): 183 if e.tag == "Setting" and e.get("name", "") == name: 184 element = e 185 break 186 else: 187 return defaultValue 188 189 e_value = element.text 190 e_strip = element.get("strip", "1").strip().lower() 191 e_type = str(element.get("type", "str")).strip() 192 193 if e_value is None: 194 return defaultValue 195 196 # Strip value 197 if e_strip == "" or e_strip == "false" or e_strip == "no" or e_strip == "0": 198 e_strip = False 199 else: e_strip = True 200 201 if e_type == "str" or e_type == "unicode": 202 if e_strip: e_value = e_value.strip() 203 else: 204 e_value = e_value.strip() 205 206 # Return value 207 e_value = self.getValue(e_type,e_value) 208 209 return e_value
210
211 - def set(self, module, name, value, extra_attrs={}):
212 """ 213 Sets a variable to specified value. 214 215 @param module: Module where the variable should be set 216 @param name: Name of the variable 217 @param value: Value to assign to the variable 218 @type value: C{str} or C{unicode} or C{int} or C{float} or C{bool} or C{list} or C{dict} 219 @param extra_attrs: Extra attributes to be stored in the XML-file 220 @type extra_attrs: C{dict} 221 """ 222 if not self._initialized: 223 self.load() 224 self._initialized = True 225 226 if not isinstance(name, str) and not isinstance(name, unicode): 227 raise AttributeError("SimpleXMLSerializer.set(): Invalid type for name argument.") 228 229 moduleTree = self._getModuleTree(module) 230 e_type = "str" 231 232 if isinstance(value, bool): # This must be before int 233 e_type = "bool" 234 value = str(value) 235 elif isinstance(value, int): 236 e_type = "int" 237 value = str(value) 238 elif isinstance(value, float): 239 e_type = "float" 240 value = str(value) 241 elif isinstance(value, unicode): 242 e_type = "unicode" 243 value = unicode(value) 244 elif isinstance(value, list): 245 e_type = "list" 246 value = self._serializeList(value) 247 elif isinstance(value, dict): 248 e_type = "dict" 249 value = self._serializeDict(value) 250 else: 251 e_type = "str" 252 value = str(value) 253 254 for e in moduleTree.getchildren(): 255 if e.tag != "Setting": continue 256 if e.get("name", "") == name: 257 e.text = value 258 break 259 else: 260 attrs = {"name":name, "type":e_type} 261 for k in extra_attrs: 262 if k not in attrs: 263 attrs[k] = extra_args[k] 264 elm = ET.SubElement(moduleTree, "Setting", attrs) 265 elm.text = value
266 267 """ 268 returns a list of string, where each string is a module name 269 """
270 - def getModuleName(self):
271 self._moduleNames = [] 272 for c in self._root_element.getchildren(): 273 if c.tag == "Module": 274 name = c.get("name","") 275 if not isinstance(name, str) and not isinstance(name, unicode): 276 raise AttributeError("SimpleXMLSerializer.get(): Invalid type for name argument.") 277 278 self._moduleNames.append(name) 279 return self._moduleNames
280 281
282 - def getAllSettings(self,module):
283 self._settingsFromFile = {} 284 285 # if file has not been loaded, load the file 286 if not self._initialized: 287 self.load() 288 self._initialized = True 289 290 # get the module tree, as we want to get values for module FIFE only 291 moduleTree = self._getModuleTree(module) 292 293 # now from the tree read every value, and put the necessary values 294 # to the list 295 for e in moduleTree.getchildren(): 296 if e.tag == "Setting": 297 name = e.get("name", "") 298 299 # check the name 300 if not isinstance(name, str) and not isinstance(name, unicode): 301 raise AttributeError("SimpleXMLSerializer.get(): Invalid type for name argument.") 302 element = e 303 304 e_value = element.text 305 e_strip = element.get("strip", "1").strip().lower() 306 e_type = str(element.get("type", "str")).strip() 307 308 # Strip value 309 if e_strip == "" or e_strip == "false" or e_strip == "no" or e_strip == "0": 310 e_strip = False 311 else: e_strip = True 312 313 if e_type == "str" or e_type == "unicode": 314 if e_strip and e_value: 315 e_value = e_value.strip() 316 else: 317 if e_value: 318 e_value = e_value.strip() 319 320 # get the value 321 e_value = self.getValue(e_type,e_value) 322 self._settingsFromFile[name] = e_value; 323 324 return self._settingsFromFile
325 326 327
328 - def _validateTree(self):
329 """ 330 Iterates the XML tree and prints warning when an invalid tag is found. 331 332 Raises an InvalidFormat exception if there is a format error. 333 """ 334 for c in self._root_element.getchildren(): 335 if c.tag != "Module": 336 raise InvalidFormat("Invalid tag in " + self._file + ". Expected Module, got: " + c.tag) 337 elif c.get("name", "") == "": 338 raise InvalidFormat("Invalid tag in " + self._file + ". Module name is empty.") 339 else: 340 for e in c.getchildren(): 341 if e.tag != "Setting": 342 raise InvalidFormat("Invalid tag in " + self._file + " in module: " + c.tag + ". Expected Setting, got: " + e.tag) 343 elif c.get("name", "") == "": 344 raise InvalidFormat("Invalid tag in " + self._file + " in module: " + c.tag + ". Setting name is empty" + e.tag)
345
346 - def _getModuleTree(self, module):
347 """ 348 Returns a module element from the XML tree. If no module with the specified 349 name exists, a new element will be created. 350 351 @param module: The module to get from the settings tree 352 @type module: C{string} 353 """ 354 if not isinstance(module, str) and not isinstance(module, unicode): 355 raise AttributeError("Settings:_getModuleTree: Invalid type for module argument.") 356 357 for c in self._root_element.getchildren(): 358 if c.tag == "Module" and c.get("name", "") == module: 359 return c 360 361 # Create module 362 return ET.SubElement(self._root_element, "Module", {"name":module})
363
364 - def _indent(self, elem, level=0):
365 """ 366 Adds whitespace, so the resulting XML-file is properly indented. 367 Shamelessly stolen from http://effbot.org/zone/element-lib.htm 368 """ 369 i = os.linesep + level*" " 370 if len(elem): 371 if not elem.text or not elem.text.strip(): 372 elem.text = i + " " 373 if not elem.tail or not elem.tail.strip(): 374 elem.tail = i 375 for elem in elem: 376 self._indent(elem, level+1) 377 if not elem.tail or not elem.tail.strip(): 378 elem.tail = i 379 else: 380 if level and (not elem.tail or not elem.tail.strip()): 381 elem.tail = i
382 383 # FIXME: 384 # These serialization functions are not reliable at all 385 # This will only serialize the first level of a dict or list 386 # It will not check the types nor the content for conflicts. 387 # Perhaps we should add a small serialization library?
388 - def _serializeList(self, list):
389 """ Serializes a list, so it can be stored in a text file """ 390 return " ; ".join(list)
391
392 - def _deserializeList(self, string):
393 """ Deserializes a list back into a list object """ 394 return string.split(" ; ")
395
396 - def _serializeDict(self, dict):
397 """ Serializes a list, so it can be stored in a text file """ 398 serial = "" 399 for key in dict: 400 value = dict[key] 401 if serial != "": serial += " ; " 402 serial += str(key)+" : "+str(value) 403 404 return serial
405
406 - def _deserializeDict(self, serial):
407 """ Deserializes a list back into a dict object """ 408 dict = {} 409 items = serial.split(" ; ") 410 for i in items: 411 kv_pair = i.split(" : ") 412 dict[kv_pair[0]] = kv_pair[1] 413 return dict
414