Source code for openpypeline.core.util.XML

import xml.dom.minidom
import __main__
import logging

logger = logging.getLogger("openPypeline.xml")

[docs] class xmlserialize(object): pass
[docs] class xmlunpicklingException(Exception): pass
[docs] class xmlfile(object): """ Class to handle saving / loading an xmlserialize object to file """ def __init__(self, filename=None): self.filename = filename
[docs] def load(self): ''' Load xml from self.filename, return object ''' node = xml.dom.minidom.parse(self.filename) return self.unpickle(node.documentElement)
[docs] def save(self, obj): ''' Save node to self.filename ''' node = self.pickle(root=obj, fabric=xml.dom.minidom.Document()) with open(self.filename, 'w', encoding='utf-8') as f: node.writexml(f)
# helper functions
[docs] def getMod(self, cls): ''' cls would be in the form like module1.module2.cls Loops through __main__ to see how module1.module2 is imported Returns the class(obj) using the existing import method Returns None if the module or class isn't found ''' import __main__ import sys if '.' not in cls: return None # separate the module from the class [module1.module2].[cls] logger.debug(f"Class: {cls}") mod = cls[0:cls.rindex('.')] className = cls[cls.rindex('.')+1:] # Check sys.modules first for a safer and more direct lookup if mod in sys.modules: class_obj = getattr(sys.modules[mod], className, None) if class_obj: return class_obj() for d in dir(__main__): try: module_obj = getattr(__main__, d) mod_name = getattr(module_obj, '__name__', None) if mod_name and f"{mod_name}.{className}" == cls: class_obj = getattr(module_obj, className) return class_obj() except Exception: pass # Ignore if no attribute __name__ return None
[docs] def getType(self, obj): """ Generates string representation of class of obj discarding decoration """ return str(obj.__class__).split("'")[1]
_easyToPickle = [ "int", "float", "str", "unicode", "long" ] def _isCallable(self, obj): return hasattr(obj, "__call__") # # pickling # def _pickleDictItems(self, root, node, fabric): for key, value in root.items(): tempnode = fabric.createElement("item") tempnode.appendChild(self.pickle(key, fabric, "key")) tempnode.appendChild(self.pickle(value, fabric, "value")) node.appendChild(tempnode) def _pickleListItems(self, root, node, fabric): for idx, obj in enumerate(root): tempnode = self.pickle(obj, fabric, "item") tempnode.attributes["index"] = str(idx) node.appendChild(tempnode) _pickleTupleItems = _pickleListItems
[docs] def pickle(self, root, fabric, elementName="root"): node = fabric.createElement(elementName) typeStr = self.getType(root) node.attributes["type"]=typeStr if isinstance(root, xmlserialize): node = self._pickleObjectWithAttributes(node, root, fabric, elementName) elif typeStr in self._easyToPickle: node.appendChild(fabric.createTextNode(str(root))) elif isinstance(root, dict): self._pickleDictItems(root, node, fabric) elif isinstance(root, list): self._pickleListItems(root, node, fabric) elif isinstance(root, tuple): self._pickleTupleItems(root, node, fabric) else: # fallback handler node.appendChild(fabric.createTextNode(repr(root))) return node
def _pickleObjectWithAttributes(self, node, root, fabric, elementName): ''' Pickle object members, prioritizing custom encoding if present. ''' if hasattr(root, "__pickle_to_xml__"): attributesToPickle = root.__pickle_to_xml__ else: # avoid members which are python internal attributesToPickle = [ name for name in dir(root) if not name.startswith("__") ] for name in attributesToPickle: obj = getattr(root, name) # do not pickle member functions if self._isCallable(obj): continue # is there some special encoding method ?? if hasattr(root, f"_xml_encode_{name}"): value = getattr(root, f"_xml_encode_{name}")() element = fabric.createElement(name) element.attributes["type"]='custom' element.appendChild(fabric.createTextNode(value)) node.appendChild(element) else: node.appendChild(self.pickle(obj, fabric, name)) return node # # unpickling # # helper functions def _getElementChilds(self, node, doLower = 1): """ returns list of (tagname, element) for all element childs of node """ dolow = doLower and (lambda x:x.lower()) or (lambda x:x) return [ (dolow(no.tagName), no) for no in node.childNodes if no.nodeType != no.TEXT_NODE ] def _getText(self, nodelist): """ returns collected and stripped text of textnodes among nodes in nodelist """ rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data return rc.strip()
[docs] def unpickle(self, node, obj=None): ''' main unpickle function ''' typeName= node.attributes["type"].value if typeName in self._easyToPickle: initValue = self._getText(node.childNodes) types = {'int': int, 'float': float, 'str': str, 'unicode': str, 'long': int} return types[typeName](initValue) elif typeName=="tuple": return self._unpickleTuple(node) elif typeName=="list": return self._unpickleList(node) elif typeName=="dict": return self._unpickleDict(node) elif typeName=="custom": fun = f"_xml_decode_{node.tagName}" if not obj or not hasattr(obj, fun): return '' return getattr(obj, fun)(self._getText(node.childNodes)) else: try: import __main__ obj = getattr(__main__, typeName)() except Exception: obj = self.getMod(typeName) for name, element in self._getElementChilds(node): obj.__dict__[name] = self.unpickle(element, obj) return obj
def _unpickleList(self, node): li = [] # collect entries, you can not assume that the # members of the list appear in the right order ! for name, element in self._getElementChilds(node): if name != "item": raise xmlunpicklingException() idx = int(element.attributes["index"].value) obj = self.unpickle(element) li.append((idx, obj)) # rebuild list with right order li.sort(key=lambda x: x[0]) return [ item[1] for item in li ] def _unpickleTuple(self, node): return tuple(self._unpickleList(node)) def _unpickleDict(self, node): dd = dict() for name, element in self._getElementChilds(node): if name != "item": raise xmlunpicklingException() childList = self._getElementChilds(element) if len(childList) != 2: raise xmlunpicklingException() for name, element in childList: if name=="key": key = self.unpickle(element) elif name=="value": value = self.unpickle(element) dd[key]=value return dd