xmlclass/xmlclass.py
changeset 1730 64d8f52bc8c8
parent 1571 486f94a8032c
child 1732 94ffe74e6895
--- a/xmlclass/xmlclass.py	Fri Aug 11 15:18:19 2017 +0300
+++ b/xmlclass/xmlclass.py	Mon Aug 14 19:13:01 2017 +0300
@@ -93,7 +93,7 @@
     def dst(self, dt):
         return ZERO
 
-[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, 
+[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE,
  ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT,
 ] = range(13)
 
@@ -140,7 +140,7 @@
 
 def GetNormalizedString(attr, extract=True):
     """
-    Function that normalizes a string according to XML 1.0. Replace  
+    Function that normalizes a string according to XML 1.0. Replace
     tabulations, line feed and carriage return by white space
     @param attr: tree node containing data to extract or data to normalize
     @param extract: attr is a tree node or not
@@ -155,14 +155,14 @@
 
 def GetToken(attr, extract=True):
     """
-    Function that tokenizes a string according to XML 1.0. Remove any leading  
-    and trailing white space and replace internal sequence of two or more 
+    Function that tokenizes a string according to XML 1.0. Remove any leading
+    and trailing white space and replace internal sequence of two or more
     spaces by only one white space
     @param attr: tree node containing data to extract or data to tokenize
     @param extract: attr is a tree node or not
     @return: data tokenized as string
     """
-    return " ".join([part for part in 
+    return " ".join([part for part in
                      GetNormalizedString(attr, extract).split(" ")
                      if part])
 
@@ -186,7 +186,7 @@
         raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value)
 
 
-def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, 
+def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None,
                               minExclusive=None, maxExclusive=None):
     """
     Function that generates an extraction function for integer defining min and
@@ -517,7 +517,7 @@
         check that all extracted items match the model
         @param attr: tree node containing data to extract or data as a string
         @param extract: attr is a tree node or not
-        @return: data as a list of string if matching 
+        @return: data as a list of string if matching
         """
         if extract:
             value = GetAttributeValue(attr)
@@ -535,18 +535,18 @@
     return GetModelNameList
 
 def GenerateAnyInfos(infos):
-    
+
     def GetTextElement(tree):
         if infos["namespace"][0] == "##any":
             return tree.xpath("p")[0]
         return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0]
-    
+
     def ExtractAny(tree):
         return GetTextElement(tree).text
-    
+
     def GenerateAny(tree, value):
         GetTextElement(tree).text = etree.CDATA(value)
-        
+
     def InitialAny():
         if infos["namespace"][0] == "##any":
             element_name = "p"
@@ -555,9 +555,9 @@
         p = etree.Element(element_name)
         p.text = etree.CDATA("")
         return p
-        
+
     return {
-        "type": COMPLEXTYPE, 
+        "type": COMPLEXTYPE,
         "extract": ExtractAny,
         "generate": GenerateAny,
         "initial": InitialAny,
@@ -574,16 +574,16 @@
             return True
         else:
             return None
-    
+
     def GenerateTag(value, name=None, indent=0):
         if name is not None and not (infos["minOccurs"] == 0 and value is None):
             ind1, ind2 = getIndent(indent, name)
             return ind1 + "<%s/>\n" % name
         else:
             return ""
-    
+
     return {
-        "type": TAG, 
+        "type": TAG,
         "extract": ExtractTag,
         "generate": GenerateTag,
         "initial": lambda: None,
@@ -595,7 +595,7 @@
         namespace, name = DecomposeQualifiedName(infos)
         return factory.GetQualifiedNameInfos(name, namespace)
     return infos
-    
+
 def GetElementInitialValue(factory, infos):
     infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
     if infos["minOccurs"] == 1:
@@ -667,7 +667,7 @@
     prefix = ("%s:" % factory.TargetNamespace
               if factory.TargetNamespace is not None else "")
     choices_xpath = "|".join(map(lambda x: prefix + x, choices_dict.keys()))
-    
+
     def GetContentInitial():
         content_name, infos = choices[0]
         if content_name == "sequence":
@@ -678,7 +678,7 @@
         else:
             content_value = GetElementInitialValue(factory, infos)
         return content_value
-        
+
     return {
         "type": COMPLEXTYPE,
         "choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP),
@@ -693,13 +693,13 @@
 def DecomposeQualifiedName(name):
     result = QName_model.match(name)
     if not result:
-        raise ValueError("\"%s\" isn't a valid QName value!" % name) 
+        raise ValueError("\"%s\" isn't a valid QName value!" % name)
     parts = result.groups()[0].split(':')
     if len(parts) == 1:
         return None, parts[0]
     return parts
-    
-def GenerateElement(element_name, attributes, elements_model, 
+
+def GenerateElement(element_name, attributes, elements_model,
                     accept_text=False):
     def ExtractElement(factory, node):
         attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
@@ -746,20 +746,20 @@
         else:
             self.BaseFolder = self.FileName = None
         self.Debug = debug
-        
+
         # Dictionary for stocking Classes and Types definitions created from
         # the XML tree
         self.XMLClassDefinitions = {}
-        
+
         self.DefinedNamespaces = {}
         self.NSMAP = {}
         self.Namespaces = {}
         self.SchemaNamespace = None
         self.TargetNamespace = None
         self.etreeNamespaceFormat = "%s"
-        
+
         self.CurrentCompilations = []
-        
+
         # Dictionaries for stocking Classes and Types generated
         self.ComputeAfter = []
         if self.FileName is not None:
@@ -920,15 +920,15 @@
 
     def ParseSchema(self):
         pass
-    
+
     def AddEquivalentClass(self, name, base):
         if name != base:
             equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {})
             equivalences[self.etreeNamespaceFormat % name] = True
-    
+
     def AddDistinctionBetweenParentsInLookupClass(
                                     self, lookup_classes, parent, typeinfos):
-        parent = (self.etreeNamespaceFormat % parent 
+        parent = (self.etreeNamespaceFormat % parent
                   if parent is not None else None)
         parent_class = lookup_classes.get(parent)
         if parent_class is not None:
@@ -939,7 +939,7 @@
                 lookup_classes[parent] = [typeinfos, parent_class]
         else:
             lookup_classes[parent] = typeinfos
-    
+
     def AddToLookupClass(self, name, parent, typeinfos):
         lookup_name = self.etreeNamespaceFormat % name
         if isinstance(typeinfos, (StringType, UnicodeType)):
@@ -958,7 +958,7 @@
             self.AddDistinctionBetweenParentsInLookupClass(
                 lookup_classes, parent, typeinfos)
             self.ComputedClassesLookUp[lookup_name] = lookup_classes
-    
+
     def ExtractTypeInfos(self, name, parent, typeinfos):
         if isinstance(typeinfos, (StringType, UnicodeType)):
             namespace, type_name = DecomposeQualifiedName(typeinfos)
@@ -986,12 +986,12 @@
             return self.CreateClass(name, parent, typeinfos)
         elif typeinfos["type"] == SIMPLETYPE:
             return typeinfos
-    
+
     def GetEquivalentParents(self, parent):
         return reduce(lambda x, y: x + y,
             [[p] + self.GetEquivalentParents(p)
              for p in self.EquivalentClassesParent.get(parent, {}).keys()], [])
-    
+
     """
     Methods that generates the classes
     """
@@ -1028,7 +1028,7 @@
                             if result is not None and \
                                not isinstance(result, (UnicodeType, StringType)):
                                 self.Namespaces[self.TargetNamespace][result["name"]] = result
-        
+
         for name, parents in self.ComputedClassesLookUp.iteritems():
             if isinstance(parents, DictType):
                 computed_classes = parents.items()
@@ -1042,7 +1042,7 @@
                         parents = dict(computed_classes)
                         self.ComputedClassesLookUp[name] = parents
                     parents[equivalent_parent] = computed_class
-        
+
         return self.ComputedClasses
 
     def CreateClass(self, name, parent, classinfos, baseclass = False):
@@ -1050,11 +1050,11 @@
             classname = "%s_%s" % (parent, name)
         else:
             classname = name
-        
+
         # Checks that classe haven't been generated yet
         if self.AlreadyComputed.get(classname, False):
             return self.ComputedClassesInfos.get(classname, None)
-        
+
         # If base classes haven't been generated
         bases = []
         base_infos = classinfos.get("base", None)
@@ -1088,12 +1088,12 @@
         bases.append(DefaultElementClass)
         bases = tuple(bases)
         classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
-        
+
         self.AlreadyComputed[classname] = True
-        
+
         for attribute in classinfos["attributes"]:
             infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
-            if infos is not None:                    
+            if infos is not None:
                 if infos["type"] != SIMPLETYPE:
                     raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"])
                 attrname = attribute["name"]
@@ -1105,7 +1105,7 @@
             else:
                 raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"])
             attribute["attr_type"] = infos
-        
+
         for element in classinfos["elements"]:
             if element["type"] == CHOICE:
                 elmtname = element["name"]
@@ -1141,13 +1141,13 @@
                     classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
             classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
             classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
-            
+
         classmembers["_init_"] = generateInitMethod(self, classinfos)
         classmembers["StructurePattern"] = GetStructurePattern(classinfos)
         classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
         classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
         classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
-        
+
         class_definition = classobj(str(name), bases, classmembers)
         setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
         setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
@@ -1155,16 +1155,16 @@
                        "name": classname,
                        "initial": generateClassCreateFunction(class_definition),
         }
-        
+
         if self.FileName is not None:
             self.ComputedClasses[self.FileName][classname] = class_definition
         else:
             self.ComputedClasses[classname] = class_definition
         self.ComputedClassesInfos[classname] = class_infos
-        
+
         self.AddToLookupClass(name, parent, class_definition)
         self.AddToLookupClass(classname, None, class_definition)
-            
+
         return class_infos
 
     """
@@ -1183,7 +1183,7 @@
         else:
             for classname, xmlclass in items:
                 print "%s: %s" % (classname, str(xmlclass))
-        
+
     def PrintClassNames(self):
         classnames = self.XMLClassDefinitions.keys()
         classnames.sort()
@@ -1191,7 +1191,7 @@
             print classname
 
 """
-Method that generate the method for generating the xml tree structure model by 
+Method that generate the method for generating the xml tree structure model by
 following the attributes list defined
 """
 def ComputeMultiplicity(name, infos):
@@ -1213,7 +1213,7 @@
         if infos["maxOccurs"] == "unbounded":
             return "(?:%s){%d,}" % (name, infos["minOccurs"], name)
         else:
-            return "(?:%s){%d,%d}" % (name, infos["minOccurs"], 
+            return "(?:%s){%d,%d}" % (name, infos["minOccurs"],
                                        infos["maxOccurs"])
 
 def GetStructurePattern(classinfos):
@@ -1256,7 +1256,7 @@
     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
     optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
     elements = dict([(element["name"], element) for element in classinfos["elements"]])
-    
+
     def getattrMethod(self, name):
         if attributes.has_key(name):
             attribute_infos = attributes[name]
@@ -1269,7 +1269,7 @@
             elif attribute_infos.has_key("default"):
                 return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False)
             return None
-        
+
         elif elements.has_key(name):
             element_infos = elements[name]
             element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
@@ -1279,7 +1279,7 @@
                     return content
                 elif len(content) > 0:
                     return content[0]
-                return None 
+                return None
             elif element_infos["type"] == ANY:
                 return element_infos["elmt_type"]["extract"](self)
             elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
@@ -1290,7 +1290,7 @@
                     values = self.findall(element_name)
                     if element_infos["elmt_type"]["type"] == SIMPLETYPE:
                         return map(lambda value:
-                            element_infos["elmt_type"]["extract"](value.text, extract=False), 
+                            element_infos["elmt_type"]["extract"](value.text, extract=False),
                             values)
                     return values
                 else:
@@ -1298,19 +1298,19 @@
                     if element_infos["elmt_type"]["type"] == SIMPLETYPE:
                         return element_infos["elmt_type"]["extract"](value.text, extract=False)
                     return value
-            
+
         elif classinfos.has_key("base"):
             return classinfos["base"].__getattr__(self, name)
-        
+
         return DefaultElementClass.__getattribute__(self, name)
-    
+
     return getattrMethod
 
 def generateSetattrMethod(factory, class_definition, classinfos):
     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
     optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
     elements = OrderedDict([(element["name"], element) for element in classinfos["elements"]])
-    
+
     def setattrMethod(self, name, value):
         if attributes.has_key(name):
             attribute_infos = attributes[name]
@@ -1323,26 +1323,26 @@
             elif attribute_infos.has_key("fixed"):
                 return
             return self.set(name, attribute_infos["attr_type"]["generate"](value))
-        
+
         elif elements.has_key(name):
             element_infos = elements[name]
             element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
             if element_infos["type"] == ANY:
                 element_infos["elmt_type"]["generate"](self, value)
-            
+
             elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
                 self.text = element_infos["elmt_type"]["generate"](value)
-            
+
             else:
                 prefix = ("%s:" % factory.TargetNamespace
                           if factory.TargetNamespace is not None else "")
                 element_xpath = (prefix + name
                                  if name != "content"
                                  else elements["content"]["elmt_type"]["choices_xpath"].path)
-                
+
                 for element in self.xpath(element_xpath, namespaces=factory.NSMAP):
                     self.remove(element)
-                
+
                 if value is not None:
                     element_idx = elements.keys().index(name)
                     if element_idx > 0:
@@ -1351,27 +1351,27 @@
                                       if x != "content"
                                       else elements["content"]["elmt_type"]["choices_xpath"].path,
                             elements.keys()[:element_idx]))
-                        
+
                         insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP))
                     else:
                         insertion_point = 0
-                    
+
                     if not isinstance(value, ListType):
                         value = [value]
-                    
+
                     for element in reversed(value):
                         if element_infos["elmt_type"]["type"] == SIMPLETYPE:
                             tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
                             tmp_element.text = element_infos["elmt_type"]["generate"](element)
                             element = tmp_element
                         self.insert(insertion_point, element)
-        
+
         elif classinfos.has_key("base"):
             return classinfos["base"].__setattr__(self, name, value)
-        
+
         else:
             raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
-        
+
     return setattrMethod
 
 def gettypeinfos(name, facets):
@@ -1398,7 +1398,7 @@
             attr_list.extend(classinfos["base"].getElementAttributes(self))
         for attr in classinfos["attributes"]:
             if attr["use"] != "prohibited":
-                attr_params = {"name" : attr["name"], "use" : attr["use"], 
+                attr_params = {"name" : attr["name"], "use" : attr["use"],
                     "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
                     "value" : getattr(self, attr["name"], "")}
                 attr_list.append(attr_params)
@@ -1408,7 +1408,7 @@
 def generateGetElementInfos(factory, classinfos):
     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
     elements = dict([(element["name"], element) for element in classinfos["elements"]])
-    
+
     def getElementInfos(self, name, path=None, derived=False):
         attr_type = "element"
         value = None
@@ -1419,14 +1419,14 @@
             if attributes.has_key(parts[0]):
                 if len(parts) != 1:
                     raise ValueError("Wrong path!")
-                attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], 
+                attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"],
                                          attributes[parts[0]]["attr_type"]["facets"])
                 value = getattr(self, parts[0], "")
             elif elements.has_key(parts[0]):
                 if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
                     if len(parts) != 1:
                         raise ValueError("Wrong path!")
-                    attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], 
+                    attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"],
                                              elements[parts[0]]["elmt_type"]["facets"])
                     value = getattr(self, parts[0], "")
                 elif parts[0] == "content":
@@ -1463,8 +1463,8 @@
                         if self.content is not None:
                             children.extend(self.content.getElementInfos(value)["children"])
                 elif element["elmt_type"]["type"] == SIMPLETYPE:
-                    children.append({"name": element_name, "require": element["minOccurs"] != 0, 
-                        "type": gettypeinfos(element["elmt_type"]["basename"], 
+                    children.append({"name": element_name, "require": element["minOccurs"] != 0,
+                        "type": gettypeinfos(element["elmt_type"]["basename"],
                                              element["elmt_type"]["facets"]),
                         "value": getattr(self, element_name, None)})
                 else:
@@ -1478,7 +1478,7 @@
 def generateSetElementValue(factory, classinfos):
     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
     elements = dict([(element["name"], element) for element in classinfos["elements"]])
-    
+
     def setElementValue(self, path, value):
         if path is not None:
             parts = path.split(".", 1)
@@ -1489,7 +1489,7 @@
                     setattr(self, parts[0], value)
                 elif attributes[parts[0]]["use"] == "optional" and value == "":
                     if attributes[parts[0]].has_key("default"):
-                        setattr(self, parts[0], 
+                        setattr(self, parts[0],
                             attributes[parts[0]]["attr_type"]["extract"](
                                 attributes[parts[0]]["default"], False))
                     else:
@@ -1680,39 +1680,39 @@
 NAMESPACE_PATTERN = re.compile("xmlns(?:\:[^\=]*)?=\"[^\"]*\" ")
 
 class DefaultElementClass(etree.ElementBase):
-    
+
     StructurePattern = re.compile("$")
-    
+
     def _init_(self):
         pass
-    
+
     def getLocalTag(self):
         return etree.QName(self.tag).localname
-        
+
     def tostring(self):
         return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True, encoding='utf-8')).decode('utf-8')
 
 class XMLElementClassLookUp(etree.PythonElementClassLookup):
-    
+
     def __init__(self, classes, *args, **kwargs):
         etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
         self.LookUpClasses = classes
-    
+
     def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
         element_class = self.LookUpClasses.get(element_tag, (default, None))
         if not isinstance(element_class, DictType):
             if isinstance(element_class[0], (StringType, UnicodeType)):
                 return self.GetElementClass(element_class[0], default=default)
             return element_class[0]
-        
+
         element_with_parent_class = element_class.get(parent_tag, default)
         if isinstance(element_with_parent_class, (StringType, UnicodeType)):
             return self.GetElementClass(element_with_parent_class, default=default)
         return element_with_parent_class
-        
+
     def lookup(self, document, element):
         parent = element.getparent()
-        element_class = self.GetElementClass(element.tag, 
+        element_class = self.GetElementClass(element.tag,
             parent.tag if parent is not None else None)
         if isinstance(element_class, ListType):
             children = "".join([
@@ -1741,24 +1741,24 @@
             self.RootNSMAP = namespaces
         self.BaseClass = base_class
         self.XSDSchema = xsd_schema
-    
+
     def set_element_class_lookup(self, class_lookup):
         etree.XMLParser.set_element_class_lookup(self, class_lookup)
         self.ClassLookup = class_lookup
-    
+
     def LoadXMLString(self, xml_string):
         tree = etree.fromstring(xml_string, self)
         if not self.XSDSchema.validate(tree):
             error = self.XSDSchema.error_log.last_error
             return tree, (error.line, error.message)
-        return tree, None 
-    
+        return tree, None
+
     def Dumps(self, xml_obj):
         return etree.tostring(xml_obj, encoding='utf-8')
-    
+
     def Loads(self, xml_string):
         return etree.fromstring(xml_string, self)
-    
+
     def CreateRoot(self):
         if self.BaseClass is not None:
             root = self.makeelement(
@@ -1767,14 +1767,14 @@
             root._init_()
             return root
         return None
-    
+
     def GetElementClass(self, element_tag, parent_tag=None):
         return self.ClassLookup.GetElementClass(
-            self.DefaultNamespaceFormat % element_tag, 
-            self.DefaultNamespaceFormat % parent_tag 
-            if parent_tag is not None else parent_tag, 
+            self.DefaultNamespaceFormat % element_tag,
+            self.DefaultNamespaceFormat % parent_tag
+            if parent_tag is not None else parent_tag,
             None)
-    
+
     def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
         element_class = self.GetElementClass(element_tag, parent_tag)
         if isinstance(element_class, ListType):
@@ -1787,14 +1787,14 @@
         DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
         new_element._init_()
         return new_element
-    
+
 def GenerateParser(factory, xsdstring):
     ComputedClasses = factory.CreateClasses()
-    
+
     if factory.FileName is not None:
         ComputedClasses = ComputedClasses[factory.FileName]
     BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
-       
+
     parser = XMLClassParser(
         factory.NSMAP,
         factory.etreeNamespaceFormat,
@@ -1803,6 +1803,5 @@
         strip_cdata = False, remove_blank_text=True)
     class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
     parser.set_element_class_lookup(class_lookup)
-    
+
     return parser
-