diff -r 72a826dfcfbb -r 02fe382c4511 xmlclass/xmlclass.py --- a/xmlclass/xmlclass.py Wed Jul 31 10:45:07 2013 +0900 +++ b/xmlclass/xmlclass.py Mon Nov 18 12:12:31 2013 +0900 @@ -28,7 +28,9 @@ from types import * from xml.dom import minidom from xml.sax.saxutils import escape, unescape, quoteattr +from lxml import etree from new import classobj +from collections import OrderedDict def CreateNode(name): node = minidom.Node() @@ -533,28 +535,33 @@ 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): - if tree.nodeName in ["#text", "#cdata-section"]: - return unicode(unescape(tree.data)) - else: - return tree - - def GenerateAny(value, name=None, indent=0): - if isinstance(value, (StringType, UnicodeType)): - try: - value = value.decode("utf-8") - except: - pass - return u'\n' % value - else: - return value.toprettyxml(indent=" "*indent, encoding="utf-8") + return GetTextElement(tree).text + + def GenerateAny(tree, value): + GetTextElement(tree).text = etree.CDATA(value) + + def InitialAny(): + if infos["namespace"][0] == "##any": + element_name = "p" + else: + element_name = "{%s}p" % infos["namespace"][0] + p = etree.Element(element_name) + p.text = etree.CDATA("") + return p return { "type": COMPLEXTYPE, "extract": ExtractAny, "generate": GenerateAny, - "initial": lambda: "", - "check": lambda x: isinstance(x, (StringType, UnicodeType, minidom.Node)) + "initial": InitialAny, + "check": lambda x: isinstance(x, (StringType, UnicodeType, etree.ElementBase)) } def GenerateTagInfos(infos): @@ -591,38 +598,23 @@ def GetElementInitialValue(factory, infos): infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) - if infos["minOccurs"] == 0 and infos["maxOccurs"] == 1: - if infos.has_key("default"): - return infos["elmt_type"]["extract"](infos["default"], False) - else: - return None - elif infos["minOccurs"] == 1 and infos["maxOccurs"] == 1: - return infos["elmt_type"]["initial"]() + if infos["minOccurs"] == 1: + element_name = factory.etreeNamespaceFormat % infos["name"] + if infos["elmt_type"]["type"] == SIMPLETYPE: + def initial_value(): + value = etree.Element(element_name) + value.text = (infos["elmt_type"]["generate"](infos["elmt_type"]["initial"]())) + return value + else: + def initial_value(): + value = infos["elmt_type"]["initial"]() + if infos["type"] != ANY: + DefaultElementClass.__setattr__(value, "tag", element_name) + value._init_() + return value + return [initial_value() for i in xrange(infos["minOccurs"])] else: - return [infos["elmt_type"]["initial"]() for i in xrange(infos["minOccurs"])] - -def HandleError(message, raise_exception): - if raise_exception: - raise ValueError(message) - return False - -def CheckElementValue(factory, name, infos, value, raise_exception=True): - infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) - if value is None and raise_exception: - if not (infos["minOccurs"] == 0 and infos["maxOccurs"] == 1): - return HandleError("Attribute '%s' isn't optional." % name, raise_exception) - elif infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: - if not isinstance(value, ListType): - return HandleError("Attribute '%s' must be a list." % name, raise_exception) - if len(value) < infos["minOccurs"] or infos["maxOccurs"] != "unbounded" and len(value) > infos["maxOccurs"]: - return HandleError("List out of bounds for attribute '%s'." % name, raise_exception) - if not reduce(lambda x, y: x and y, map(infos["elmt_type"]["check"], value), True): - return HandleError("Attribute '%s' must be a list of valid elements." % name, raise_exception) - elif infos.has_key("fixed") and value != infos["fixed"]: - return HandleError("Value of attribute '%s' can only be '%s'." % (name, str(infos["fixed"])), raise_exception) - else: - return infos["elmt_type"]["check"](value) - return True + return [] def GetContentInfos(name, choices): for choice_infos in choices: @@ -649,6 +641,7 @@ sequence_element["elmt_type"] = element_infos elif choice["elmt_type"] == "tag": choice["elmt_type"] = GenerateTagInfos(choice) + factory.AddToLookupClass(choice["name"], name, DefaultElementClass) else: choice_infos = factory.ExtractTypeInfos(choice["name"], name, choice["elmt_type"]) if choice_infos is not None: @@ -656,26 +649,6 @@ choices.append((choice["name"], choice)) return choices -def ExtractContentElement(factory, tree, infos, content): - infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) - if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: - if isinstance(content, ListType) and len(content) > 0 and \ - content[-1]["name"] == tree.nodeName: - content_item = content.pop(-1) - content_item["value"].append(infos["elmt_type"]["extract"](tree)) - return content_item - elif not isinstance(content, ListType) and \ - content is not None and \ - content["name"] == tree.nodeName: - return {"name": tree.nodeName, - "value": content["value"] + [infos["elmt_type"]["extract"](tree)]} - else: - return {"name": tree.nodeName, - "value": [infos["elmt_type"]["extract"](tree)]} - else: - return {"name": tree.nodeName, - "value": infos["elmt_type"]["extract"](tree)} - def GenerateContentInfos(factory, name, choices): choices_dict = {} for choice_name, infos in choices: @@ -691,6 +664,9 @@ if choices_dict.has_key(choice_name): raise ValueError("'%s' element defined two times in choice" % choice_name) choices_dict[choice_name] = infos + 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] @@ -698,140 +674,15 @@ content_value = [] for i in xrange(infos["minOccurs"]): for element_infos in infos["elements"]: - value = GetElementInitialValue(factory, element_infos) - if value is not None: - if element_infos["type"] == CHOICE: - content_value.append(value) - else: - content_value.append({"name": element_infos["name"], "value": value}) + content_value.extend(GetElementInitialValue(factory, element_infos)) else: content_value = GetElementInitialValue(factory, infos) - return {"name": content_name, "value": content_value} - - def CheckContent(value): - if value["name"] != "sequence": - infos = choices_dict.get(value["name"], None) - if infos is not None: - return CheckElementValue(factory, value["name"], infos, value["value"], False) - elif len(value["value"]) > 0: - infos = choices_dict.get(value["value"][0]["name"], None) - if infos is None: - for choice_name, infos in choices: - if infos["type"] == "sequence": - for element_infos in infos["elements"]: - if element_infos["type"] == CHOICE: - infos = GetContentInfos(value["value"][0]["name"], element_infos["choices"]) - if infos is not None: - sequence_number = 0 - element_idx = 0 - while element_idx < len(value["value"]): - for element_infos in infos["elements"]: - element_value = None - if element_infos["type"] == CHOICE: - choice_infos = None - if element_idx < len(value["value"]): - for choice in element_infos["choices"]: - if choice["name"] == value["value"][element_idx]["name"]: - choice_infos = choice - element_value = value["value"][element_idx]["value"] - element_idx += 1 - break - if ((choice_infos is not None and - not CheckElementValue(factory, choice_infos["name"], choice_infos, element_value, False)) or - (choice_infos is None and element_infos["minOccurs"] > 0)): - raise ValueError("Invalid sequence value in attribute 'content'") - else: - if element_idx < len(value["value"]) and element_infos["name"] == value["value"][element_idx]["name"]: - element_value = value["value"][element_idx]["value"] - element_idx += 1 - if not CheckElementValue(factory, element_infos["name"], element_infos, element_value, False): - raise ValueError("Invalid sequence value in attribute 'content'") - sequence_number += 1 - if sequence_number < infos["minOccurs"] or infos["maxOccurs"] != "unbounded" and sequence_number > infos["maxOccurs"]: - raise ValueError("Invalid sequence value in attribute 'content'") - return True - else: - for element_name, infos in choices: - if element_name == "sequence": - required = 0 - for element in infos["elements"]: - if element["minOccurs"] > 0: - required += 1 - if required == 0: - return True - return False - - def ExtractContent(tree, content): - infos = choices_dict.get(tree.nodeName, None) - if infos is not None: - if infos["name"] == "sequence": - sequence_dict = dict([(element_infos["name"], element_infos) for element_infos in infos["elements"] if element_infos["type"] != CHOICE]) - element_infos = sequence_dict.get(tree.nodeName) - if content is not None and \ - content["name"] == "sequence" and \ - len(content["value"]) > 0 and \ - choices_dict.get(content["value"][-1]["name"]) == infos: - return {"name": "sequence", - "value": content["value"] + [ExtractContentElement(factory, tree, element_infos, content["value"][-1])]} - else: - return {"name": "sequence", - "value": [ExtractContentElement(factory, tree, element_infos, None)]} - else: - return ExtractContentElement(factory, tree, infos, content) - else: - for choice_name, infos in choices: - if infos["type"] == "sequence": - for element_infos in infos["elements"]: - if element_infos["type"] == CHOICE: - try: - if content is not None and \ - content["name"] == "sequence" and \ - len(content["value"]) > 0: - return {"name": "sequence", - "value": content["value"] + [element_infos["elmt_type"]["extract"](tree, content["value"][-1])]} - else: - return {"name": "sequence", - "value": [element_infos["elmt_type"]["extract"](tree, None)]} - except: - pass - raise ValueError("Invalid element \"%s\" for content!" % tree.nodeName) - - def GenerateContent(value, name=None, indent=0): - text = "" - if value["name"] != "sequence": - infos = choices_dict.get(value["name"], None) - if infos is not None: - infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) - if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: - for item in value["value"]: - text += infos["elmt_type"]["generate"](item, value["name"], indent) - else: - text += infos["elmt_type"]["generate"](value["value"], value["name"], indent) - elif len(value["value"]) > 0: - infos = choices_dict.get(value["value"][0]["name"], None) - if infos is None: - for choice_name, infos in choices: - if infos["type"] == "sequence": - for element_infos in infos["elements"]: - if element_infos["type"] == CHOICE: - infos = GetContentInfos(value["value"][0]["name"], element_infos["choices"]) - if infos is not None: - sequence_dict = dict([(element_infos["name"], element_infos) for element_infos in infos["elements"]]) - for element_value in value["value"]: - element_infos = sequence_dict.get(element_value["name"]) - if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1: - for item in element_value["value"]: - text += element_infos["elmt_type"]["generate"](item, element_value["name"], indent) - else: - text += element_infos["elmt_type"]["generate"](element_value["value"], element_infos["name"], indent) - return text + return content_value return { "type": COMPLEXTYPE, + "choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP), "initial": GetContentInitial, - "check": CheckContent, - "extract": ExtractContent, - "generate": GenerateContent } #------------------------------------------------------------------------------- @@ -901,9 +752,11 @@ self.XMLClassDefinitions = {} self.DefinedNamespaces = {} + self.NSMAP = {} self.Namespaces = {} self.SchemaNamespace = None self.TargetNamespace = None + self.etreeNamespaceFormat = "%s" self.CurrentCompilations = [] @@ -914,6 +767,8 @@ else: self.ComputedClasses = {} self.ComputedClassesInfos = {} + self.ComputedClassesLookUp = {} + self.EquivalentClassesParent = {} self.AlreadyComputed = {} def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False): @@ -1016,7 +871,9 @@ attrs[name] = infos["extract"]["default"](attr) elif namespace == "xmlns": infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace) - self.DefinedNamespaces[infos["extract"](attr)] = name + value = infos["extract"](attr) + self.DefinedNamespaces[value] = name + self.NSMAP[name] = value else: raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName)) for attr in valid_attrs: @@ -1063,20 +920,63 @@ 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 + if parent is not None else None) + parent_class = lookup_classes.get(parent) + if parent_class is not None: + if isinstance(parent_class, ListType): + if typeinfos not in parent_class: + lookup_classes[parent].append(typeinfos) + elif parent_class != typeinfos: + lookup_classes[parent] = [parent_class, typeinfos] + else: + lookup_classes[parent] = typeinfos + + def AddToLookupClass(self, name, parent, typeinfos): + lookup_name = self.etreeNamespaceFormat % name + if isinstance(typeinfos, (StringType, UnicodeType)): + self.AddEquivalentClass(name, typeinfos) + typeinfos = self.etreeNamespaceFormat % typeinfos + lookup_classes = self.ComputedClassesLookUp.get(lookup_name) + if lookup_classes is None: + self.ComputedClassesLookUp[lookup_name] = (typeinfos, parent) + elif isinstance(lookup_classes, DictType): + self.AddDistinctionBetweenParentsInLookupClass( + lookup_classes, parent, typeinfos) + else: + lookup_classes = { + self.etreeNamespaceFormat % lookup_classes[1] + if lookup_classes[1] is not None else None: lookup_classes[0]} + self.AddDistinctionBetweenParentsInLookupClass( + lookup_classes, parent, typeinfos) + self.ComputedClassesLookUp[lookup_name] = lookup_classes + def ExtractTypeInfos(self, name, parent, typeinfos): if isinstance(typeinfos, (StringType, UnicodeType)): - namespace, name = DecomposeQualifiedName(typeinfos) - infos = self.GetQualifiedNameInfos(name, namespace) + namespace, type_name = DecomposeQualifiedName(typeinfos) + infos = self.GetQualifiedNameInfos(type_name, namespace) + if name != "base": + if infos["type"] == SIMPLETYPE: + self.AddToLookupClass(name, parent, DefaultElementClass) + elif namespace == self.TargetNamespace: + self.AddToLookupClass(name, parent, type_name) if infos["type"] == COMPLEXTYPE: - name, parent = self.SplitQualifiedName(name, namespace) - result = self.CreateClass(name, parent, infos) + type_name, parent = self.SplitQualifiedName(type_name, namespace) + result = self.CreateClass(type_name, parent, infos) if result is not None and not isinstance(result, (UnicodeType, StringType)): self.Namespaces[self.TargetNamespace][result["name"]] = result return result elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE: - name, parent = self.SplitQualifiedName(name, namespace) - result = self.CreateClass(name, parent, infos["elmt_type"]) + type_name, parent = self.SplitQualifiedName(type_name, namespace) + result = self.CreateClass(type_name, parent, infos["elmt_type"]) if result is not None and not isinstance(result, (UnicodeType, StringType)): self.Namespaces[self.TargetNamespace][result["name"]] = result return result @@ -1086,7 +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 """ @@ -1123,6 +1028,21 @@ 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() + elif parents[1] is not None: + computed_classes = [(self.etreeNamespaceFormat % parents[1], parents[0])] + else: + computed_classes = [] + for parent, computed_class in computed_classes: + for equivalent_parent in self.GetEquivalentParents(parent): + if not isinstance(parents, DictType): + parents = dict(computed_classes) + self.ComputedClassesLookUp[name] = parents + parents[equivalent_parent] = computed_class + return self.ComputedClasses def CreateClass(self, name, parent, classinfos, baseclass = False): @@ -1141,9 +1061,12 @@ bases = [] base_infos = classinfos.get("base", None) if base_infos is not None: + namespace, base_name = DecomposeQualifiedName(base_infos) + if namespace == self.TargetNamespace: + self.AddEquivalentClass(name, base_name) result = self.ExtractTypeInfos("base", name, base_infos) if result is None: - namespace, base_name = DecomposeQualifiedName(base_infos) + namespace, base_name = DecomposeQualifiedName(base_infos) if self.AlreadyComputed.get(base_name, False): self.ComputeAfter.append((name, parent, classinfos)) if self.TargetNamespace is not None: @@ -1164,7 +1087,7 @@ if classinfos["base"] is None: raise ValueError("No class found for base type") bases.append(classinfos["base"]) - bases.append(object) + bases.append(DefaultElementClass) bases = tuple(bases) classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass} @@ -1177,11 +1100,8 @@ raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"]) attrname = attribute["name"] if attribute["use"] == "optional": - classmembers[attrname] = None classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute) classmembers["delete%s"%attrname] = generateDeleteMethod(attrname) - else: - classmembers[attrname] = infos["initial"]() classmembers["set%s"%attrname] = generateSetMethod(attrname) classmembers["get%s"%attrname] = generateGetMethod(attrname) else: @@ -1200,54 +1120,43 @@ classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"]) infos = GenerateContentInfos(self, name, choices) elif element["type"] == ANY: - elmtname = element["name"] = "text" + elmtname = element["name"] = "anyText" element["minOccurs"] = element["maxOccurs"] = 1 infos = GenerateAnyInfos(element) else: elmtname = element["name"] if element["elmt_type"] == "tag": infos = GenerateTagInfos(element) + self.AddToLookupClass(element["name"], name, DefaultElementClass) else: infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"]) if infos is not None: element["elmt_type"] = infos if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1: - classmembers[elmtname] = [] classmembers["append%s" % elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element) classmembers["insert%s" % elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element) classmembers["remove%s" % elmtname] = generateRemoveMethod(elmtname, element["minOccurs"]) classmembers["count%s" % elmtname] = generateCountMethod(elmtname) else: if element["minOccurs"] == 0: - classmembers[elmtname] = None classmembers["add%s" % elmtname] = generateAddMethod(elmtname, self, element) classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname) - elif not isinstance(element["elmt_type"], (UnicodeType, StringType)): - classmembers[elmtname] = element["elmt_type"]["initial"]() - else: - classmembers[elmtname] = None classmembers["set%s" % elmtname] = generateSetMethod(elmtname) classmembers["get%s" % elmtname] = generateGetMethod(elmtname) - classmembers["__init__"] = generateInitMethod(self, classinfos) - classmembers["getStructure"] = generateStructureMethod(classinfos) - classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos) - classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos) + classmembers["_init_"] = generateInitMethod(self, classinfos) + classmembers["StructurePattern"] = GetStructurePattern(classinfos) classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos) classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos) classmembers["setElementValue"] = generateSetElementValue(self, classinfos) - classmembers["singleLineAttributes"] = True - classmembers["compatibility"] = lambda x, y: None - classmembers["extraAttrs"] = {} class_definition = classobj(str(classname), bases, classmembers) + setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos)) setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos)) class_infos = {"type": COMPILEDCOMPLEXTYPE, "name": classname, - "check": generateClassCheckFunction(class_definition), "initial": generateClassCreateFunction(class_definition), - "extract": generateClassExtractFunction(class_definition), - "generate": class_definition.generateXMLText} + } if self.FileName is not None: self.ComputedClasses[self.FileName][classname] = class_definition @@ -1255,6 +1164,9 @@ 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 """ @@ -1281,69 +1193,6 @@ print classname """ -Method that generate the method for checking a class instance -""" -def generateClassCheckFunction(class_definition): - def classCheckfunction(instance): - return isinstance(instance, class_definition) - return classCheckfunction - -""" -Method that generate the method for creating a class instance -""" -def generateClassCreateFunction(class_definition): - def classCreatefunction(): - return class_definition() - return classCreatefunction - -""" -Method that generate the method for extracting a class instance -""" -def generateClassExtractFunction(class_definition): - def classExtractfunction(node): - instance = class_definition() - instance.loadXMLTree(node) - return instance - return classExtractfunction - -""" -Method that generate the method for loading an xml tree by following the -attributes list defined -""" -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 = dict([(element["name"], element) for element in classinfos["elements"]]) - - def setattrMethod(self, name, value): - if attributes.has_key(name): - attributes[name]["attr_type"] = FindTypeInfos(factory, attributes[name]["attr_type"]) - if value is None: - if optional_attributes.get(name, False): - return object.__setattr__(self, name, None) - else: - raise ValueError("Attribute '%s' isn't optional." % name) - elif attributes[name].has_key("fixed") and value != attributes[name]["fixed"]: - raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"])) - elif attributes[name]["attr_type"]["check"](value): - return object.__setattr__(self, name, value) - else: - raise ValueError("Invalid value for attribute '%s'." % (name)) - elif elements.has_key(name): - if CheckElementValue(factory, name, elements[name], value): - return object.__setattr__(self, name, value) - else: - raise ValueError("Invalid value for attribute '%s'." % (name)) - elif classinfos.has_key("base"): - return classinfos["base"].__setattr__(self, name, value) - elif class_definition.__dict__.has_key(name): - return object.__setattr__(self, name, value) - else: - raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name)) - - return setattrMethod - -""" Method that generate the method for generating the xml tree structure model by following the attributes list defined """ @@ -1369,7 +1218,10 @@ return "(?:%s){%d,%d}" % (name, infos["minOccurs"], infos["maxOccurs"]) -def GetStructure(classinfos): +def GetStructurePattern(classinfos): + base_structure_pattern = ( + classinfos["base"].StructurePattern.pattern[:-1] + if classinfos.has_key("base") else "") elements = [] for element in classinfos["elements"]: if element["type"] == ANY: @@ -1380,7 +1232,7 @@ choices = [] for infos in element["choices"]: if infos["type"] == "sequence": - structure = "(?:%s)" % GetStructure(infos) + structure = "(?:%s)" % GetStructurePattern(infos) else: structure = "%s " % infos["name"] choices.append(ComputeMultiplicity(structure, infos)) @@ -1390,170 +1242,139 @@ else: elements.append(ComputeMultiplicity("%s " % element["name"], element)) if classinfos.get("order", True) or len(elements) == 0: - return "".join(elements) + return re.compile(base_structure_pattern + "".join(elements) + "$") else: raise ValueError("XSD structure not yet supported!") -def generateStructureMethod(classinfos): - def getStructureMethod(self): - structure = GetStructure(classinfos) - if classinfos.has_key("base"): - return classinfos["base"].getStructure(self) + structure - return structure - return getStructureMethod - """ -Method that generate the method for loading an xml tree by following the -attributes list defined +Method that generate the method for creating a class instance """ -def generateLoadXMLTree(factory, classinfos): +def generateClassCreateFunction(class_definition): + def classCreatefunction(): + return class_definition() + return classCreatefunction + +def generateGetattrMethod(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 = dict([(element["name"], element) for element in classinfos["elements"]]) - def loadXMLTreeMethod(self, tree, extras=[], derived=False): - self.extraAttrs = {} - self.compatibility(tree) - if not derived: - children_structure = "" - for node in tree.childNodes: - if not (node.nodeName == "#text" and node.data.strip() == "") and node.nodeName != "#comment": - children_structure += "%s " % node.nodeName - structure_pattern = self.getStructure() - if structure_pattern != "": - structure_model = re.compile("(%s)$" % structure_pattern) - result = structure_model.match(children_structure) - if not result: - raise ValueError("Invalid structure for \"%s\" children!." % tree.nodeName) - required_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "required"]) - if classinfos.has_key("base"): - extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) - classinfos["base"].loadXMLTree(self, tree, extras, True) - for attrname, attr in tree._attrs.iteritems(): - if attributes.has_key(attrname): - attributes[attrname]["attr_type"] = FindTypeInfos(factory, attributes[attrname]["attr_type"]) - object.__setattr__(self, attrname, attributes[attrname]["attr_type"]["extract"](attr)) - elif not classinfos.has_key("base") and not attrname in extras and not self.extraAttrs.has_key(attrname): - self.extraAttrs[attrname] = GetAttributeValue(attr) - required_attributes.pop(attrname, None) - if len(required_attributes) > 0: - raise ValueError("Required attributes %s missing for \"%s\" element!" % (", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName)) - first = {} - for node in tree.childNodes: - name = node.nodeName - if name == "#text" and node.data.strip() == "" or name == "#comment": - continue - elif elements.has_key(name): - elements[name]["elmt_type"] = FindTypeInfos(factory, elements[name]["elmt_type"]) - if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1: - if first.get(name, True): - object.__setattr__(self, name, [elements[name]["elmt_type"]["extract"](node)]) - first[name] = False + def getattrMethod(self, name): + if attributes.has_key(name): + attribute_infos = attributes[name] + attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"]) + value = self.get(name) + if value is not None: + return attribute_infos["attr_type"]["extract"](value, extract=False) + elif attribute_infos.has_key("fixed"): + return attribute_infos["attr_type"]["extract"](attribute_infos["fixed"], extract=False) + 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"]) + if element_infos["type"] == CHOICE: + content = element_infos["elmt_type"]["choices_xpath"](self) + if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1: + return content + elif len(content) > 0: + return content[0] + return None + elif element_infos["type"] == ANY: + return element_infos["elmt_type"]["extract"](self) + elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE: + return element_infos["elmt_type"]["extract"](self.text, extract=False) + else: + element_name = factory.etreeNamespaceFormat % name + if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1: + 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), + values) + return values + else: + value = self.find(element_name) + 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] + attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"]) + if optional_attributes.get(name, False): + default = attribute_infos.get("default", None) + if value is None or value == default: + self.attrib.pop(name, None) + return + 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: + previous_elements_xpath = "|".join(map( + lambda x: prefix + x + 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: - getattr(self, name).append(elements[name]["elmt_type"]["extract"](node)) - else: - object.__setattr__(self, name, elements[name]["elmt_type"]["extract"](node)) - elif elements.has_key("text"): - if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1: - if first.get("text", True): - object.__setattr__(self, "text", [elements["text"]["elmt_type"]["extract"](node)]) - first["text"] = False - else: - getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node)) - else: - object.__setattr__(self, "text", elements["text"]["elmt_type"]["extract"](node)) - elif elements.has_key("content"): - if name in ["#cdata-section", "#text"]: - if elements["content"]["elmt_type"]["type"] == SIMPLETYPE: - object.__setattr__(self, "content", elements["content"]["elmt_type"]["extract"](node.data, False)) - else: - content = getattr(self, "content") - if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1: - if first.get("content", True): - object.__setattr__(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)]) - first["content"] = False - else: - content.append(elements["content"]["elmt_type"]["extract"](node, content)) - else: - object.__setattr__(self, "content", elements["content"]["elmt_type"]["extract"](node, content)) - return loadXMLTreeMethod - - -""" -Method that generates the method for generating an xml text by following the -attributes list defined -""" -def generateGenerateXMLText(factory, classinfos): - def generateXMLTextMethod(self, name, indent=0, extras={}, derived=False): - ind1, ind2 = getIndent(indent, name) - if not derived: - text = ind1 + u'<%s' % name - else: - text = u'' - - first = True - - if not classinfos.has_key("base"): - extras.update(self.extraAttrs) - for attr, value in extras.iteritems(): - if not first and not self.singleLineAttributes: - text += u'\n%s' % (ind2) - text += u' %s=%s' % (attr, quoteattr(value)) - first = False - extras.clear() - for attr in classinfos["attributes"]: - if attr["use"] != "prohibited": - attr["attr_type"] = FindTypeInfos(factory, attr["attr_type"]) - value = getattr(self, attr["name"], None) - if value != None: - computed_value = attr["attr_type"]["generate"](value) - else: - computed_value = None - if attr["use"] != "optional" or (value != None and \ - computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))): - if classinfos.has_key("base"): - extras[attr["name"]] = computed_value - else: - if not first and not self.singleLineAttributes: - text += u'\n%s' % (ind2) - text += ' %s=%s' % (attr["name"], quoteattr(computed_value)) - first = False - if classinfos.has_key("base"): - first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True) - text += new_text - else: - first = True - for element in classinfos["elements"]: - element["elmt_type"] = FindTypeInfos(factory, element["elmt_type"]) - value = getattr(self, element["name"], None) - if element["minOccurs"] == 0 and element["maxOccurs"] == 1: - if value is not None: - if first: - text += u'>\n' - first = False - text += element["elmt_type"]["generate"](value, element["name"], indent + 1) - elif element["minOccurs"] == 1 and element["maxOccurs"] == 1: - if first: - text += u'>\n' - first = False - if element["name"] == "content" and element["elmt_type"]["type"] == SIMPLETYPE: - text += element["elmt_type"]["generate"](value) - else: - text += element["elmt_type"]["generate"](value, element["name"], indent + 1) - else: - if first and len(value) > 0: - text += u'>\n' - first = False - for item in value: - text += element["elmt_type"]["generate"](item, element["name"], indent + 1) - if not derived: - if first: - text += u'/>\n' - else: - text += ind1 + u'\n' % (name) - return text - else: - return first, text - return generateXMLTextMethod + 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): if facets.has_key("enumeration") and facets["enumeration"][0] is not None: @@ -1611,7 +1432,7 @@ elements[parts[0]]["elmt_type"]["facets"]) value = getattr(self, parts[0], "") elif parts[0] == "content": - return self.content["value"].getElementInfos(self.content["name"], path) + return self.content.getElementInfos(self.content.getLocalTag(), path) else: attr = getattr(self, parts[0], None) if attr is None: @@ -1622,7 +1443,7 @@ return attr.getElementInfos(parts[0], parts[1]) elif elements.has_key("content"): if len(parts) > 0: - return self.content["value"].getElementInfos(name, path) + return self.content.getElementInfos(name, path) elif classinfos.has_key("base"): classinfos["base"].getElementInfos(name, path) else: @@ -1640,15 +1461,9 @@ if self.content is None: value = "" else: - value = self.content["name"] - if self.content["value"] is not None: - if self.content["name"] == "sequence": - choices_dict = dict([(choice["name"], choice) for choice in element["choices"]]) - sequence_infos = choices_dict.get("sequence", None) - if sequence_infos is not None: - children.extend([item.getElementInfos(infos["name"]) for item, infos in zip(self.content["value"], sequence_infos["elements"])]) - else: - children.extend(self.content["value"].getElementInfos(self.content["name"])["children"]) + value = self.content.getLocalTag() + 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"], @@ -1705,13 +1520,13 @@ instance.setElementValue(None, value) elif elements.has_key("content"): if len(parts) > 0: - self.content["value"].setElementValue(path, value) + self.content.setElementValue(path, value) elif classinfos.has_key("base"): classinfos["base"].setElementValue(self, path, value) elif elements.has_key("content"): if value == "": if elements["content"]["minOccurs"] == 0: - self.setcontent(None) + self.setcontent([]) else: raise ValueError("\"content\" element is required!") else: @@ -1723,20 +1538,21 @@ """ def generateInitMethod(factory, classinfos): def initMethod(self): - self.extraAttrs = {} if classinfos.has_key("base"): - classinfos["base"].__init__(self) + classinfos["base"]._init_(self) for attribute in classinfos["attributes"]: attribute["attr_type"] = FindTypeInfos(factory, attribute["attr_type"]) if attribute["use"] == "required": - setattr(self, attribute["name"], attribute["attr_type"]["initial"]()) - elif attribute["use"] == "optional": - if attribute.has_key("default"): - setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False)) - else: - setattr(self, attribute["name"], None) + self.set(attribute["name"], attribute["attr_type"]["generate"](attribute["attr_type"]["initial"]())) for element in classinfos["elements"]: - setattr(self, element["name"], GetElementInitialValue(factory, element)) + if element["type"] != CHOICE: + element_name = ( + etree.QName(factory.NSMAP["xhtml"], "p") + if element["type"] == ANY + else factory.etreeNamespaceFormat % element["name"]) + initial = GetElementInitialValue(factory, element) + if initial is not None: + map(self.append, initial) return initMethod def generateSetMethod(attr): @@ -1753,18 +1569,16 @@ def addMethod(self): if infos["type"] == ATTRIBUTE: infos["attr_type"] = FindTypeInfos(factory, infos["attr_type"]) - initial = infos["attr_type"]["initial"] - extract = infos["attr_type"]["extract"] + if not infos.has_key("default"): + setattr(self, attr, infos["attr_type"]["initial"]()) elif infos["type"] == ELEMENT: infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) - initial = infos["elmt_type"]["initial"] - extract = infos["elmt_type"]["extract"] + value = infos["elmt_type"]["initial"]() + DefaultElementClass.__setattr__(value, "tag", factory.etreeNamespaceFormat % attr) + setattr(self, attr, value) + value._init_() else: raise ValueError("Invalid class attribute!") - if infos.has_key("default"): - setattr(self, attr, extract(infos["default"], False)) - else: - setattr(self, attr, initial()) return addMethod def generateDeleteMethod(attr): @@ -1777,10 +1591,10 @@ infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) attr_list = getattr(self, attr) if maxOccurs == "unbounded" or len(attr_list) < maxOccurs: - if infos["elmt_type"]["check"](value): - attr_list.append(value) + if len(attr_list) == 0: + setattr(self, attr, [value]) else: - raise ValueError("\"%s\" value isn't valid!" % attr) + attr_list[-1].addnext(value) else: raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) return appendMethod @@ -1790,10 +1604,12 @@ infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) attr_list = getattr(self, attr) if maxOccurs == "unbounded" or len(attr_list) < maxOccurs: - if infos["elmt_type"]["check"](value): - attr_list.insert(index, value) + if len(attr_list) == 0: + setattr(self, attr, [value]) + elif index == 0: + attr_list[0].addprevious(value) else: - raise ValueError("\"%s\" value isn't valid!" % attr) + attr_list[min(index - 1, len(attr_list) - 1)].addnext(value) else: raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) return insertMethod @@ -1805,24 +1621,26 @@ def generateSetChoiceByTypeMethod(factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) - def setChoiceMethod(self, type): - if not choices.has_key(type): - raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) - choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"]) - new_element = choices[type]["elmt_type"]["initial"]() - self.content = {"name": type, "value": new_element} - return new_element + def setChoiceMethod(self, content_type): + if not choices.has_key(content_type): + raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) + choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) + new_content = choices[content_type]["elmt_type"]["initial"]() + DefaultElementClass.__setattr__(new_content, "tag", factory.etreeNamespaceFormat % content_type) + self.content = new_content + return new_content return setChoiceMethod def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) - def appendChoiceMethod(self, type): - if not choices.has_key(type): - raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) - choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"]) + def appendChoiceMethod(self, content_type): + if not choices.has_key(content_type): + raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) + choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) if maxOccurs == "unbounded" or len(self.content) < maxOccurs: - new_element = choices[type]["elmt_type"]["initial"]() - self.content.append({"name": type, "value": new_element}) + new_element = choices[content_type]["elmt_type"]["initial"]() + DefaultElementClass.__setattr__(new_element, "tag", factory.etreeNamespaceFormat % content_type) + self.appendcontent(new_element) return new_element else: raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) @@ -1830,13 +1648,14 @@ def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) - def insertChoiceMethod(self, index, type): - if not choices.has_key(type): - raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) - choices[type]["elmt_type"] = FindTypeInfos(factory, choices[type]["elmt_type"]) + def insertChoiceMethod(self, index, content_type): + if not choices.has_key(content_type): + raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) + choices[type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) if maxOccurs == "unbounded" or len(self.content) < maxOccurs: - new_element = choices[type]["elmt_type"]["initial"]() - self.content.insert(index, {"name" : type, "value" : new_element}) + new_element = choices[content_type]["elmt_type"]["initial"]() + DefaultElementClass.__setattr__(new_element, "tag", factory.etreeNamespaceFormat % content_type) + self.insertcontent(index, new_element) return new_element else: raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) @@ -1846,7 +1665,7 @@ def removeMethod(self, index): attr_list = getattr(self, attr) if len(attr_list) > minOccurs: - getattr(self, attr).pop(index) + self.remove(attr_list[index]) else: raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr)) return removeMethod @@ -1857,16 +1676,135 @@ return countMethod """ -This function generate the classes from a class factory +This function generate a xml parser from a class factory """ -def GenerateClasses(factory): + +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)) + +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, + parent.tag if parent is not None else None) + if isinstance(element_class, ListType): + children = "".join([ + "%s " % etree.QName(child.tag).localname + for child in element]) + for possible_class in element_class: + if isinstance(possible_class, (StringType, UnicodeType)): + possible_class = self.GetElementClass(possible_class) + if possible_class.StructurePattern.match(children) is not None: + return possible_class + return element_class[0] + return element_class + +class XMLClassParser(etree.XMLParser): + + def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs): + etree.XMLParser.__init__(self, *args, **kwargs) + self.DefaultNamespaceFormat = default_namespace_format + self.NSMAP = namespaces + targetNamespace = etree.QName(default_namespace_format % "d").namespace + if targetNamespace is not None: + self.RootNSMAP = { + name if targetNamespace != uri else None: uri + for name, uri in namespaces.iteritems()} + else: + 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 + + def Dumps(self, xml_obj): + return etree.tostring(xml_obj) + + def Loads(self, xml_string): + return etree.fromstring(xml_string, self) + + def CreateRoot(self): + if self.BaseClass is not None: + root = self.makeelement( + self.DefaultNamespaceFormat % self.BaseClass[0], + nsmap=self.RootNSMAP) + 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, + 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): + if class_idx is not None and class_idx < len(element_class): + new_element = element_class[class_idx]() + else: + raise ValueError, "No corresponding class found!" + else: + new_element = element_class() + 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 and len(ComputedClasses) == 1: - UpdateXMLClassGlobals(ComputedClasses[factory.FileName]) - return ComputedClasses[factory.FileName] - else: - UpdateXMLClassGlobals(ComputedClasses) - return ComputedClasses - -def UpdateXMLClassGlobals(classes): - globals().update(classes) + + 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, + BaseClass[0] if len(BaseClass) == 1 else None, + etree.XMLSchema(etree.fromstring(xsdstring)), + strip_cdata = False, remove_blank_text=True) + class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp) + parser.set_element_class_lookup(class_lookup) + + return parser +