diff -r d51af006fa6b -r 64d8f52bc8c8 xmlclass/xmlclass.py --- 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 -