#!/usr/bin/env python #Copyright 2004 Sebastian Hagen # This file is part of eucharis. # eucharis is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 # as published by the Free Software Foundation # eucharis 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 eucharis; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # This file is now strictly of historical interest. We mean it. Don't read it # unless you need a vivid example of how not to do things. Don't run it at all. # In fact: class ExcessivelyBrainDamagedCodeError(StandardError): pass raise ExcessivelyBrainDamagedCodeError("I'm not running this crap.") # We highly disrecommend taking this out. import os import types import re import logging import socket_management import basic_io import __main__ import xml.parsers.expat import xml.sax.saxutils logger = logging.getLogger('data_persistence') type_mapping = {} for element in ('types.ModuleType', 'object', 'types.InstanceType', 'dict', 'list', 'tuple', 'str', (unicode, 'str'), 'int', 'long', 'float', 'complex', 'bool', 'types.NoneType'): if (type(element) == str): type_mapping[eval(element)] = element elif (type(element) == tuple): type_mapping[element[0]] = element[1] if not ('variable_data_dict') in globals(): variable_data_dict = {} class xml_input: def __init__(self, filename, basename): self.logger = logger self.filename = filename self.basename = basename self.parser_expat = xml.parsers.expat.ParserCreate() self.parser_expat.StartElementHandler = self.start_element self.parser_expat.EndElementHandler = self.finish_element self.parser_expat.CharacterDataHandler = self.handle_data_character self.references = [] self.reference_types = [] self.reference_names = [] self.type_attributes = { 'mutable':(dict, list, tuple, types.ModuleType, types.InstanceType) } self.type_mapping = type_mapping self.base_element = True self.data_buffer = '' self.logger.log(20, 'Opening xml input file ' + filename + '.') self.input_file = file(filename, 'r') self.parser_expat.ParseFile(self.input_file) self.logger.log(20, 'Closing xml input file ' + filename + '.') self.input_file.close() def add_element(self, element_name, element_type): if (element_type == types.ModuleType): if not (element_name in globals()): #Ugly but working. exec 'import ' + element_name globals()[element_name] = eval(element_name) self.reference_types.append(types.ModuleType) self.reference_names.append(element_name) self.references.append(globals()[element_name]) elif (len(self.references) < 1): self.logger.log(40, 'Got element ' + element_name + ' with type ' + element_type + ', but there are no parent elements. Going into ignorance mode for anything below this point.') self.reference_names.append(element_name) self.reference_types.append(None) self.references.append(None) else: parent_element_type = self.reference_types[-1] parent_element = self.references[-1] ignorance = False if (parent_element_type == None): #This is not the same as NoneType. Its presence signifies ignorance #mode, that is something has gone wrong in a parent element and the #child elements are to be discarded. ignorance = True elif (element_type in self.type_mapping): if (parent_element_type == dict): if (element_name in parent_element): self.references.append(parent_element[element_name]) else: self.references.append(eval(self.type_mapping[element_type] + '()')) elif (parent_element_type in (object, types.InstanceType, types.ModuleType)): if ((element_name in dir(parent_element)) and (element_type in (object, types.InstanceType, types.ModuleType, dict))): #Modules, Objects, Instances and dictionaries get referenced if they already exist. self.references.append(eval('parent_element.' + element_name)) else: #For everything else, we create a new empty object of the same type. #The complete discarding of old lists and tuples is intentional. self.references.append(eval(self.type_mapping[element_type] + '()')) else: self.references.append(eval(self.type_mapping[element_type] + '()')) elif (element_type == types.NoneType): #No built in type/function for this type (yet) afaik. self.references.append(None) else: #We don't know this type. Complain and go into ignorance mode. self.logger.log(40, 'Got element ' + element_name + ' with unknown type ' + element_type + '. Going into ignorance mode for anything below this point.') ignorance = True if (ignorance): self.reference_names.append(element_name) self.references.append(None) self.reference_types.append(None) else: self.reference_names.append(element_name) self.reference_types.append(element_type) def finish_element(self, name): if (len(self.reference_types) > 0): element_type = self.reference_types[-1] if not (element_type in self.type_attributes['mutable']): self.references[-1] = self.restore_types(data_type=element_type, data=self.data_buffer) self.data_buffer = '' if (len(self.references) > 1): element = self.references.pop(-1) element_type = self.reference_types.pop(-1) element_name = self.reference_names.pop(-1) parent_element = self.references[-1] parent_element_type = self.reference_types[-1] if (parent_element_type == list): self.references[-1].append(element) elif (parent_element_type == dict): self.references[-1][element_name] = element elif (parent_element_type == tuple): self.references[-1] = self.references[-1] + (element,) elif (parent_element_type in (types.ModuleType, types.InstanceType, object)): exec 'parent_element.' + element_name + '=' + 'element' else: self.references[-1] = element def start_element(self, name, attributes): self.data_buffer = '' if ((name == self.basename) or (self.base_element)): self.base_element = False else: try: element_type = eval(name) except (NameError, SyntaxError, AttributeError): self.logger.log(40, 'Invalid variable type ' + name + ' for element with attributes ' + str(attributes) + '. Going into ignorance mode for anything below it.') else: if ('name' in attributes): if ('nametype' in attributes): element_name = self.restore_types(data_type=attributes['nametype'], data=attributes['name']) else: element_name = attributes['name'] self.logger.log(40, 'No nametype specified for element with type ' + str(element_type) + ' and attributes ' + str(attributes) + '.') else: self.logger.log(40, 'No name specified for element with type ' + str(element_type) + ' and attributes ' + str(attributes) + '.') element_name = None self.add_element(element_name=element_name, element_type=element_type) return self.reference_types.append(None) self.references.append(None) self.reference_names.append(None) def restore_types(self, data_type, data): if not (type(data_type) == str): if (data_type in self.type_mapping): data_type = self.type_mapping[data_type] if (data_type in self.type_attributes['mutable']): raise TypeError('Called to create a new object for a mutable type.') if (data_type in ('str', 'int', 'long', 'float', 'complex', 'bool')): try: data = eval(data_type + "('''" + data.replace('\\', '\\\\').replace("'''", "\\\'\\\'\\\'") + "''')") except ValueError: self.logger.log(40, 'Invalid value ' + data + ' for variable type ' + data_type + '. Discarding type and using original string.') elif (data_type in ('dict', 'list', 'tuple')): data = eval(self.type_mapping[data_type] + '()') return data def handle_data_character(self, data): self.data_buffer = self.data_buffer + data class xml_output: def __init__(self, variable_data, filename, basename): self.logger = logger self.filename = filename self.basename = basename if (type(self.basename) != str): self.basename = repr(self.basename) self.type_mapping = type_mapping self.input_process(variable_data) self.file = file(filename + '_', 'w') self.perform_output(self.basename) self.file.flush() self.file.close() os.rename(filename + '_', filename) def input_process(self, variable_data): """Parse variable-name tuple-list passed as variable_data.""" if (type(variable_data) == dict): self.variable_dictionary = variable_data elif (type(variable_data) in (list, tuple)): self.variable_dictionary = {} while (len(variable_data) > 0): current_element = variable_data.pop(-1) base = self.variable_dictionary variable_existing = True variable_string = '' parent_sub_element_type = None sub_element_type = None if ((len(current_element) > 0) and (not (current_element[0] in globals()))): module_name = current_element[0] try: #Extremely ugly, but it should work. exec 'import ' + module_name globals()[module_name] = eval(module_name) except StandardError: self.logger.log(40, 'Failed to import element ' + module_name + ' presumed to be module. Ignoring it.', exc_info=True) continue for sub_element in current_element: if (parent_sub_element_type == dict): variable_string = variable_string + '[' + repr(sub_element) + ']' else: variable_string = variable_string + sub_element try: sub_element_data = eval(variable_string) sub_element_type = type(sub_element_data) except (NameError, AttributeError): variable_existing = False break if (sub_element_type in (types.ModuleType, types.InstanceType, object)): variable_string = variable_string + '.' elif (sub_element_type != dict): break parent_sub_element = sub_element parent_sub_element_type = sub_element_type if not (variable_existing): self.logger.log(30, "Couldn't find any variable matching sequence " + str(current_element) + ' using string ' + variable_string + ', ignoring.') continue if (sub_element_type in (types.ModuleType, types.InstanceType, object)): raise TypeError("Can't save variable of type " + str(sub_element_type) + " directly.") base = self.variable_dictionary dictionary_string = 'self.variable_dictionary' if not (sub_element_type in self.type_mapping): self.logger.log(30, "Variable matching sequence " + str(current_element) + ' using string ' + variable_string + ' has unsupported type ' + str(sub_element_type) + ', ignoring.') continue for sub_element in current_element: if not (sub_element in base): base[sub_element] = {} base = base[sub_element] dictionary_string = dictionary_string + '[' + repr(sub_element) + ']' exec dictionary_string + ' = ' + repr(eval(variable_string)) else: raise TypeError('Invalid type ' + str(type(variable_data)) + ' for variable_data input (expected dictionary, list or tuple)') def perform_output(self, basename): """Write xml-file.""" self.file.write ('\n<%s>\n' % (basename,)) for element_name in self.variable_dictionary: self.write_data(self.variable_dictionary[element_name], element_name, type(eval(element_name)), element_name, indenting=1) self.file.write('\n\n' % (basename,)) def write_data(self, data, name, variable_type, varstring, indenting): """Write the tags and any values for a variable. Recurse as necessary.""" indent_offset = 3 indent_length = indenting*indent_offset self.file.write('%*s<' % (indent_length, '')) self.file.write(xml.sax.saxutils.escape(self.type_mapping[variable_type])) name_type = type(name) if (name_type in self.type_mapping): name_type = self.type_mapping[name_type] else: self.logger.log(40, "Can't preserve type %s for element name %s" % (str(name_type), str(name))) name_type = str(None) self.file.write(' name="%s" nametype="%s"' % (name, name_type)) self.file.write('>') if (type(data) in (tuple, list, dict)): element_index = -1 for element_name in data: element_index = element_index + 1 try: eval(repr(element_name)) except SyntaxError: self.logger.log(40, 'Variable of type ' + repr(type(element_name)) + " can't reversibly be converted to a string.") continue except NameError: pass if (variable_type in (types.ModuleType, object, types.InstanceType)): new_varstring = varstring + '.' + element_name elif (variable_type == dict): new_varstring = varstring + '[' + repr(element_name) + ']' elif (variable_type in (tuple, list)): new_varstring = varstring + '[' + repr(element_index) + ']' element = element_name element_name = element_index else: self.logger.log(40, 'Invalid type of variable represented as dictionary entry: ' + str(variable_type) + ' (expected dict, types.ModuleType, object or types.InstanceType).') return self.file.write('\n') if (variable_type in (tuple, list)): self.write_data(element, element_name, type(eval(new_varstring)), new_varstring, indenting+1) else: self.write_data(data[element_name], element_name, type(eval(new_varstring)), new_varstring, indenting+1) self.file.write('\n%*s' % (indent_length, '')) elif (type(data) in (types.ModuleType, object, types.InstanceType)): self.logger.log(40, 'Variable of type ' + repr(type(element_name)) + " can't reversibly be converted to a string.") pass else: value = data value_type = type(value) if (value_type in (types.ModuleType, object, types.InstanceType, dict, list, tuple)): self.write_data(value, name, value_type, varstring, indenting+1) else: if not (value_type in (str, unicode)): value = repr(value) self.file.write(xml.sax.saxutils.escape(value)) self.file.write('' % (xml.sax.saxutils.escape(self.type_mapping[variable_type]),)) def register_variable(variable_tuple, filename): if not (filename in variable_data_dict): variable_data_dict[filename] = [] if not (variable_tuple in variable_data_dict[filename]): variable_data_dict[filename].append(variable_tuple) def unregister_variable(variable_tuple, filename): if not (filename in variable_data_dict): raise ValueError('No records for filename ' + str(filename) + '.') elif not (variable_tuple in variable_data_dict[filename]): raise ValueError('The specified record ' + str(variable_tuple) + ' does not exist for filename ' + str(filename) + '.') else: variable_data_dict[filename].remove(variable_tuple) if (variable_data_dict[filename] == []): del(variable_data_dict[filename]) def save_variables(): basename = 'data_persistence_output' for filename in variable_data_dict: xml_output_instance = xml_output(variable_data_dict[filename][:], filename, basename) def register_save_timer(delay=600): socket_management.timers_add(delay, callback_handler=save_variables, persistence=True)