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-2019 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  from future import standard_library 
 25  standard_library.install_aliases() 
 26  from builtins import str 
 27  from past.builtins import basestring 
 28  from builtins import object 
 29  import os 
 30  from io import BytesIO, StringIO 
 31           
 32  from fife.extensions.serializers import ET, SerializerError, InvalidFormat, \ 
 33                                                                                  NotFound 
 34   
 35   
 36  EMPTY_XML_FILE="""\ 
 37  <?xml version='1.0' encoding='UTF-8'?> 
 38  <Settings> 
 39   
 40  </Settings> 
 41  """ 
 42   
43 -class SimpleSerializer(object):
44 """ 45 Use this as a base class for custom setting loaders/savers to use with the 46 Setting class. 47 """ 48
49 - def __init__(self, filename=None):
50 pass
51
52 - def get(self, module, name, defaultValue=None):
53 pass
54
55 - def set(self, module, name, value, extra_attrs={}):
56 pass
57
58 - def set(self, module, name, value, extra_attrs={}):
59 pass
60
61 - def load(self, filename=None):
62 """ 63 @note: If the filename specified is empty this function MUST 64 initialize an empty settings file in whatever format you need. 65 """ 66 pass
67
68 - def save(self, filename=None):
69 pass
70
71 - def getModuleNameList(self):
72 """ 73 @note: Returns all the module names that are present in the 74 settings.xml file as a list of strings 75 """ 76 pass
77
78 - def getAllSettings(self,module):
79 """ 80 @note: Returns all the setting names and values under the Module name 81 module as a dictionary structure 82 """ 83 pass
84
85 -class SimpleXMLSerializer(SimpleSerializer):
86 """ 87 This class is a simple interface to get and store data in XML files. 88 89 Usage:: 90 from fife.extensions.serializers.simplexml import SimpleXMLSerializer 91 serializer = SimpleXMLSerializer(filename="somefile.xml") 92 serializer.set("module_name", "variable_name", "value") 93 somevariable = serializer.get("module_name", "variable_name", \ 94 "default_value") 95 """
96 - def __init__(self, filename=None):
97 self._file = filename 98 self._tree = None 99 self._root_element = None 100 101 self._initialized = False
102
103 - def load(self, filename=None):
104 """ 105 Loads the XML file into memory and validates it. 106 107 Raises a SerializerError exception if the file is not specified. 108 109 @param filename: The file to load 110 @type filename: C{str} 111 112 @note: If the file does not exist it will automatically create a blank 113 file for you. 114 """ 115 if filename: 116 self._file = filename 117 118 if not self._file: 119 raise SerializerError("Cannot load file or create file. No " 120 "filename specified!") 121 122 if not os.path.exists(self._file): 123 try: 124 self._tree = ET.parse(BytesIO(EMPTY_XML_FILE)) 125 except TypeError: 126 self._tree = ET.parse(StringIO(EMPTY_XML_FILE)) 127 self._tree.write(self._file, 'UTF-8') 128 else: 129 self._tree = ET.parse(self._file) 130 131 self._root_element = self._tree.getroot() 132 self._validateTree()
133
134 - def save(self, filename=None):
135 """ 136 Saves the XML file. 137 138 @param filename: The file to save 139 @type filename: C{str} 140 141 @note: This Overwrites the file if it exists. 142 """ 143 if not self._initialized: 144 self.load() 145 self._initialized = True 146 147 if filename: 148 savefile = filename 149 else: 150 savefile = self._file 151 152 if not savefile: 153 raise SerializerError("Cannot save file. No filename specified!") 154 155 # Writes the settings to file 156 self._indent(self._root_element) 157 self._tree.write(savefile, 'UTF-8')
158 159
160 - def getValue(self, e_type, e_value):
161 if e_type == 'int': 162 return int(e_value) 163 elif e_type == 'float': 164 return float(e_value) 165 elif e_type == 'bool': 166 e_value = e_value.lower() 167 if e_value == "" or e_value == "false" or e_value == "no" \ 168 or e_value == "0": 169 return False 170 else: 171 return True 172 elif e_type == 'str' or e_type == 'string': 173 return str(e_value) 174 elif e_type == 'unicode': 175 return str(e_value) 176 elif e_type == 'list': 177 return self._deserializeList(e_value) 178 elif e_type == 'dict': 179 return self._deserializeDict(e_value)
180 181
182 - def get(self, module, name, defaultValue=None):
183 """ Gets the value of a specified variable 184 185 @param module: Name of the module to get the variable from 186 @param name: Variable name 187 @param defaultValue: Specifies the default value to return if the 188 variable is not found 189 @type defaultValue: C{str} or C{unicode} or C{int} or C{float} or 190 C{bool} or C{list} or C{dict} 191 """ 192 if not self._initialized: 193 self.load() 194 self._initialized = True 195 196 if not isinstance(name, basestring): 197 raise AttributeError("SimpleXMLSerializer.get(): Invalid type for " 198 "name argument.") 199 200 #get the module tree: for example find tree under module FIFE 201 moduleTree = self._getModuleTree(module) 202 element = None 203 for e in moduleTree: 204 if e.tag == "Setting" and e.get("name", "") == name: 205 element = e 206 break 207 else: 208 return defaultValue 209 210 e_value = element.text 211 e_strip = element.get("strip", "1").strip().lower() 212 e_type = str(element.get("type", "str")).strip() 213 214 if e_value is None: 215 return defaultValue 216 217 # Strip value 218 if e_strip == "" or e_strip == "false" or e_strip == "no" \ 219 or e_strip == "0": 220 e_strip = False 221 else: e_strip = True 222 223 if e_type == "str" or e_type == "unicode": 224 if e_strip: e_value = e_value.strip() 225 else: 226 e_value = e_value.strip() 227 228 # Return value 229 e_value = self.getValue(e_type,e_value) 230 231 return e_value
232
233 - def set(self, module, name, value, extra_attrs={}):
234 """ 235 Sets a variable to specified value. 236 237 @param module: Module where the variable should be set 238 @param name: Name of the variable 239 @param value: Value to assign to the variable 240 @type value: C{str} or C{unicode} or C{int} or C{float} or C{bool} or 241 C{list} or C{dict} 242 @param extra_attrs: Extra attributes to be stored in the XML-file 243 @type extra_attrs: C{dict} 244 """ 245 if not self._initialized: 246 self.load() 247 self._initialized = True 248 249 if not isinstance(name, basestring): 250 raise AttributeError("SimpleXMLSerializer.set(): Invalid type for " 251 "name argument.") 252 253 moduleTree = self._getModuleTree(module) 254 e_type = "str" 255 256 if isinstance(value, bool): # This must be before int 257 e_type = "bool" 258 value = str(value) 259 elif isinstance(value, int): 260 e_type = "int" 261 value = str(value) 262 elif isinstance(value, float): 263 e_type = "float" 264 value = str(value) 265 elif isinstance(value, str): 266 e_type = "unicode" 267 value = str(value) 268 elif isinstance(value, list): 269 e_type = "list" 270 value = self._serializeList(value) 271 elif isinstance(value, dict): 272 e_type = "dict" 273 value = self._serializeDict(value) 274 else: 275 e_type = "str" 276 value = str(value) 277 278 for e in moduleTree: 279 if e.tag != "Setting": continue 280 if e.get("name", "") == name: 281 e.text = value 282 break 283 else: 284 attrs = {"name":name, "type":e_type} 285 for k in extra_attrs: 286 if k not in attrs: 287 attrs[k] = extra_attrs[k] 288 elm = ET.SubElement(moduleTree, "Setting", attrs) 289 elm.text = value
290
291 - def remove(self, module, name):
292 """ 293 Removes a variable 294 295 @param module: Module where the variable should be set 296 @param name: Name of the variable 297 """ 298 if not self._initialized: 299 self.load() 300 self._initialized = True 301 302 if not isinstance(name, basestring): 303 raise AttributeError("SimpleXMLSerializer.set(): Invalid type for " 304 "name argument.") 305 306 moduleTree = self._getModuleTree(module) 307 308 for e in moduleTree: 309 if e.tag != "Setting": continue 310 if e.get("name", "") == name: 311 moduleTree.remove(e)
312
313 - def getModuleNameList(self):
314 """ 315 @return A list of the names of the modules in the XML file as strings. 316 """ 317 318 # Make sure the file has been loaded, if not load it. 319 if not self._initialized: 320 self.load() 321 self._initialized = True 322 323 moduleNames = [] 324 for c in self._root_element: 325 if c.tag == "Module": 326 name = c.get("name","") 327 if not isinstance(name, basestring): 328 raise AttributeError("SimpleXMLSerializer.get(): Invalid " 329 "type for name argument.") 330 331 moduleNames.append(name) 332 return moduleNames
333
334 - def getAllSettings(self, module):
335 settingsFromFile = {} 336 337 # if file has not been loaded, load the file 338 if not self._initialized: 339 self.load() 340 self._initialized = True 341 342 # get the module tree, as we want to get values for module FIFE only 343 moduleTree = self._getModuleTree(module) 344 345 # now from the tree read every value, and put the necessary values 346 # to the list 347 for e in moduleTree: 348 if e.tag == "Setting": 349 name = e.get("name", "") 350 351 # check the name 352 if not isinstance(name, basestring): 353 raise AttributeError("SimpleXMLSerializer.get(): Invalid " 354 "type for name argument.") 355 element = e 356 357 e_value = element.text 358 e_strip = element.get("strip", "1").strip().lower() 359 e_type = str(element.get("type", "str")).strip() 360 361 # Strip value 362 if e_strip == "" or e_strip == "false" \ 363 or e_strip == "no" or e_strip == "0": 364 e_strip = False 365 else: e_strip = True 366 367 if e_type == "str" or e_type == "unicode": 368 if e_strip and e_value: 369 e_value = e_value.strip() 370 else: 371 if e_value: 372 e_value = e_value.strip() 373 374 # get the value 375 e_value = self.getValue(e_type,e_value) 376 settingsFromFile[name] = e_value 377 378 return settingsFromFile
379
380 - def _validateTree(self):
381 """ 382 Iterates the XML tree and prints warning when an invalid tag is found. 383 384 Raises an InvalidFormat exception if there is a format error. 385 """ 386 for c in self._root_element: 387 if c.tag != "Module": 388 raise InvalidFormat("Invalid tag in " + self._file + \ 389 ". Expected Module, got: " + c.tag) 390 elif c.get("name", "") == "": 391 raise InvalidFormat("Invalid tag in " + self._file + \ 392 ". Module name is empty.") 393 else: 394 for e in c: 395 if e.tag != "Setting": 396 raise InvalidFormat("Invalid tag in " + self._file + \ 397 " in module: " + c.tag + \ 398 ". Expected Setting, got: " + \ 399 e.tag) 400 elif c.get("name", "") == "": 401 raise InvalidFormat("Invalid tag in " + self._file + \ 402 " in module: " + c.tag + \ 403 ". Setting name is empty" + e.tag)
404
405 - def _getModuleTree(self, module):
406 """ 407 Returns a module element from the XML tree. If no module with the 408 specified name exists, a new element will be created. 409 410 @param module: The module to get from the settings tree 411 @type module: C{string} 412 """ 413 if not isinstance(module, basestring): 414 raise AttributeError("Settings:_getModuleTree: Invalid type for " 415 "module argument.") 416 417 for c in self._root_element: 418 if c.tag == "Module" and c.get("name", "") == module: 419 return c 420 421 # Create module 422 return ET.SubElement(self._root_element, "Module", {"name":module})
423
424 - def _indent(self, elem, level=0):
425 """ 426 Adds whitespace, so the resulting XML-file is properly indented. 427 Shamelessly stolen from http://effbot.org/zone/element-lib.htm 428 """ 429 i = os.linesep + level*" " 430 if len(elem): 431 if not elem.text or not elem.text.strip(): 432 elem.text = i + " " 433 if not elem.tail or not elem.tail.strip(): 434 elem.tail = i 435 for elem in elem: 436 self._indent(elem, level+1) 437 if not elem.tail or not elem.tail.strip(): 438 elem.tail = i 439 else: 440 if level and (not elem.tail or not elem.tail.strip()): 441 elem.tail = i
442 443 # FIXME: 444 # These serialization functions are not reliable at all 445 # This will only serialize the first level of a dict or list 446 # It will not check the types nor the content for conflicts. 447 # Perhaps we should add a small serialization library?
448 - def _serializeList(self, list):
449 """ Serializes a list, so it can be stored in a text file """ 450 return " ; ".join(list)
451
452 - def _deserializeList(self, string):
453 """ Deserializes a list back into a list object """ 454 if not string: 455 return list() 456 return string.split(" ; ")
457
458 - def _serializeDict(self, dict):
459 """ Serializes a list, so it can be stored in a text file """ 460 serial = "" 461 for key in dict: 462 value = dict[key] 463 if serial != "": serial += " ; " 464 serial += str(key)+" : "+str(value) 465 466 return serial
467
468 - def _deserializeDict(self, serial):
469 """ Deserializes a list back into a dict object """ 470 if not serial: 471 return dict() 472 dict = {} 473 items = serial.split(" ; ") 474 for i in items: 475 kv_pair = i.split(" : ") 476 dict[kv_pair[0]] = kv_pair[1] 477 return dict
478