xmlclass/xmlclass.py
changeset 2297 96ca6b056c55
parent 2278 a3ac46366b86
child 2343 33071a451021
equal deleted inserted replaced
2296:a2ab363f9e90 2297:96ca6b056c55
   616     infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
   616     infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
   617     if infos["minOccurs"] == 1:
   617     if infos["minOccurs"] == 1:
   618         element_name = factory.etreeNamespaceFormat % infos["name"]
   618         element_name = factory.etreeNamespaceFormat % infos["name"]
   619         if infos["elmt_type"]["type"] == SIMPLETYPE:
   619         if infos["elmt_type"]["type"] == SIMPLETYPE:
   620             def initial_value():
   620             def initial_value():
   621                 value = etree.Element(element_name)
   621                 value = factory.Parser.makeelement(element_name)
   622                 value.text = (infos["elmt_type"]["generate"](infos["elmt_type"]["initial"]()))
   622                 value.text = (infos["elmt_type"]["generate"](infos["elmt_type"]["initial"]()))
       
   623                 value._init_()
   623                 return value
   624                 return value
   624         else:
   625         else:
   625             def initial_value():
   626             def initial_value():
   626                 value = infos["elmt_type"]["initial"]()
   627                 value = infos["elmt_type"]["initial"]()
   627                 if infos["type"] != ANY:
   628                 if infos["type"] != ANY:
   774         self.NSMAP = {}
   775         self.NSMAP = {}
   775         self.Namespaces = {}
   776         self.Namespaces = {}
   776         self.SchemaNamespace = None
   777         self.SchemaNamespace = None
   777         self.TargetNamespace = None
   778         self.TargetNamespace = None
   778         self.etreeNamespaceFormat = "%s"
   779         self.etreeNamespaceFormat = "%s"
       
   780         self.Parser = None
   779 
   781 
   780         self.CurrentCompilations = []
   782         self.CurrentCompilations = []
   781 
   783 
   782         # Dictionaries for stocking Classes and Types generated
   784         # Dictionaries for stocking Classes and Types generated
   783         self.ComputeAfter = []
   785         self.ComputeAfter = []
  1160                     classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
  1162                     classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
  1161             classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
  1163             classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
  1162             classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
  1164             classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
  1163 
  1165 
  1164         classmembers["_init_"] = generateInitMethod(self, classinfos)
  1166         classmembers["_init_"] = generateInitMethod(self, classinfos)
  1165         classmembers["_tmp_initial_"] = None
       
  1166         classmembers["StructurePattern"] = GetStructurePattern(classinfos)
  1167         classmembers["StructurePattern"] = GetStructurePattern(classinfos)
  1167         classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
  1168         classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
  1168         classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
  1169         classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
  1169         classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
  1170         classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
  1170 
  1171 
  1172         setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
  1173         setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
  1173         setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
  1174         setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
  1174         class_infos = {
  1175         class_infos = {
  1175             "type": COMPILEDCOMPLEXTYPE,
  1176             "type": COMPILEDCOMPLEXTYPE,
  1176             "name": classname,
  1177             "name": classname,
  1177             "initial": generateClassCreateFunction(class_definition),
  1178             "initial": generateClassCreateFunction(self, class_definition),
  1178         }
  1179         }
  1179 
       
  1180         if self.FileName is not None:
  1180         if self.FileName is not None:
  1181             self.ComputedClasses[self.FileName][classname] = class_definition
  1181             self.ComputedClasses[self.FileName][classname] = class_definition
  1182         else:
  1182         else:
  1183             self.ComputedClasses[classname] = class_definition
  1183             self.ComputedClasses[classname] = class_definition
  1184         self.ComputedClassesInfos[classname] = class_infos
  1184         self.ComputedClassesInfos[classname] = class_infos
  1267         return re.compile(base_structure_pattern + "".join(elements) + "$")
  1267         return re.compile(base_structure_pattern + "".join(elements) + "$")
  1268     else:
  1268     else:
  1269         raise ValueError("XSD structure not yet supported!")
  1269         raise ValueError("XSD structure not yet supported!")
  1270 
  1270 
  1271 
  1271 
  1272 def generateClassCreateFunction(class_definition):
  1272 def generateClassCreateFunction(factory, class_definition):
  1273     """
  1273     """
  1274     Method that generate the method for creating a class instance
  1274     Method that generate the method for creating a class instance
  1275     """
  1275     """
  1276     def classCreatefunction():
  1276     def classCreatefunction():
  1277         return class_definition()
  1277         return factory.Parser.CreateElementFromClass(class_definition)
  1278     return classCreatefunction
  1278     return classCreatefunction
  1279 
  1279 
  1280 
  1280 
  1281 def generateGetattrMethod(factory, class_definition, classinfos):
  1281 def generateGetattrMethod(factory, class_definition, classinfos):
  1282     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
  1282     attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
  1385                     if not isinstance(value, ListType):
  1385                     if not isinstance(value, ListType):
  1386                         value = [value]
  1386                         value = [value]
  1387 
  1387 
  1388                     for element in reversed(value):
  1388                     for element in reversed(value):
  1389                         if element_infos["elmt_type"]["type"] == SIMPLETYPE:
  1389                         if element_infos["elmt_type"]["type"] == SIMPLETYPE:
  1390                             tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
  1390                             tmp_element = factory.Parser.makeelement(factory.etreeNamespaceFormat % name)
  1391                             tmp_element.text = element_infos["elmt_type"]["generate"](element)
  1391                             tmp_element.text = element_infos["elmt_type"]["generate"](element)
  1392                             element = tmp_element
  1392                             element = tmp_element
  1393                         self.insert(insertion_point, element)
  1393                         self.insert(insertion_point, element)
  1394 
  1394 
  1395         elif "base" in classinfos:
  1395         elif "base" in classinfos:
  1580                 self.set(attribute["name"], attribute["attr_type"]["generate"](attribute["attr_type"]["initial"]()))
  1580                 self.set(attribute["name"], attribute["attr_type"]["generate"](attribute["attr_type"]["initial"]()))
  1581         for element in classinfos["elements"]:
  1581         for element in classinfos["elements"]:
  1582             if element["type"] != CHOICE:
  1582             if element["type"] != CHOICE:
  1583                 initial = GetElementInitialValue(factory, element)
  1583                 initial = GetElementInitialValue(factory, element)
  1584                 if initial is not None:
  1584                 if initial is not None:
  1585                     # FIXME: this is looks like dirty hack to fix strange problem with initial[0]
       
  1586                     # changing its type after returning from _init_ method to lxml.etree._Element
       
  1587                     # As a result all methods generated by class factory are lost.
       
  1588                     object.__setattr__(self, "_tmp_initial_", initial)
       
  1589                     map(self.append, initial)
  1585                     map(self.append, initial)
  1590     return initMethod
  1586     return initMethod
  1591 
  1587 
  1592 
  1588 
  1593 def generateSetMethod(attr):
  1589 def generateSetMethod(attr):
  1745 class XMLElementClassLookUp(etree.PythonElementClassLookup):
  1741 class XMLElementClassLookUp(etree.PythonElementClassLookup):
  1746 
  1742 
  1747     def __init__(self, classes, *args, **kwargs):
  1743     def __init__(self, classes, *args, **kwargs):
  1748         etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
  1744         etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
  1749         self.LookUpClasses = classes
  1745         self.LookUpClasses = classes
       
  1746         self.ElementTag = None
       
  1747         self.ElementClass = None
  1750 
  1748 
  1751     def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
  1749     def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
  1752         element_class = self.LookUpClasses.get(element_tag, (default, None))
  1750         element_class = self.LookUpClasses.get(element_tag, (default, None))
  1753         if not isinstance(element_class, DictType):
  1751         if not isinstance(element_class, DictType):
  1754             if isinstance(element_class[0], (StringType, UnicodeType)):
  1752             if isinstance(element_class[0], (StringType, UnicodeType)):
  1758         element_with_parent_class = element_class.get(parent_tag, default)
  1756         element_with_parent_class = element_class.get(parent_tag, default)
  1759         if isinstance(element_with_parent_class, (StringType, UnicodeType)):
  1757         if isinstance(element_with_parent_class, (StringType, UnicodeType)):
  1760             return self.GetElementClass(element_with_parent_class, default=default)
  1758             return self.GetElementClass(element_with_parent_class, default=default)
  1761         return element_with_parent_class
  1759         return element_with_parent_class
  1762 
  1760 
       
  1761     def SetLookupResult(self, element, element_class):
       
  1762         """
       
  1763         Set lookup result for the next 'lookup' callback made by lxml backend.
       
  1764         Lookup result is used only if element matches with tag's name submited to 'lookup'.
       
  1765         This is done, because there is no way to submit extra search parameters for
       
  1766         etree.PythonElementClassLookup.lookup() from etree.XMLParser.makeelement()
       
  1767         It's valid only for a signle 'lookup' call.
       
  1768 
       
  1769         :param element:
       
  1770             element's tag name
       
  1771         :param element_class:
       
  1772             element class that should be returned on
       
  1773             match in the next 'lookup' call.
       
  1774         :return:
       
  1775             Nothing
       
  1776         """
       
  1777         self.ElementTag = element
       
  1778         self.ElementClass = element_class
       
  1779 
       
  1780     def ResetLookupResult(self):
       
  1781         """Reset lookup result, so it don't influence next lookups"""
       
  1782         self.ElementTag = None
       
  1783         self.ElementClass = None
       
  1784 
       
  1785     def GetLookupResult(self, element):
       
  1786         """Returns previously set SetLookupResult() lookup result"""
       
  1787         element_class = None
       
  1788         if self.ElementTag is not None and self.ElementTag == element.tag:
       
  1789             element_class = self.ElementClass
       
  1790         self.ResetLookupResult()
       
  1791         return element_class
       
  1792 
  1763     def lookup(self, document, element):
  1793     def lookup(self, document, element):
       
  1794         """
       
  1795         Lookup for element class for given element tag.
       
  1796         If return None from this method, the fallback is called.
       
  1797 
       
  1798         :param document:
       
  1799             opaque document instance that contains the Element
       
  1800         :param element:
       
  1801             lightweight Element proxy implementation that is only valid during the lookup.
       
  1802             Do not try to keep a reference to it.
       
  1803             Once the lookup is done, the proxy will be invalid.
       
  1804         :return:
       
  1805             Returns element class corresponding to given element.
       
  1806         """
       
  1807         element_class = self.GetLookupResult(element)
       
  1808         if element_class is not None:
       
  1809             return element_class
       
  1810 
  1764         parent = element.getparent()
  1811         parent = element.getparent()
  1765         element_class = self.GetElementClass(
  1812         element_class = self.GetElementClass(
  1766             element.tag, parent.tag if parent is not None else None)
  1813             element.tag, parent.tag if parent is not None else None)
  1767         if isinstance(element_class, ListType):
  1814         if isinstance(element_class, ListType):
  1768             children = "".join([
  1815             children = "".join([
  1776             return element_class[0]
  1823             return element_class[0]
  1777         return element_class
  1824         return element_class
  1778 
  1825 
  1779 
  1826 
  1780 class XMLClassParser(etree.XMLParser):
  1827 class XMLClassParser(etree.XMLParser):
  1781 
  1828     def __init__(self, *args, **kwargs):
  1782     def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs):
       
  1783         etree.XMLParser.__init__(self, *args, **kwargs)
  1829         etree.XMLParser.__init__(self, *args, **kwargs)
       
  1830 
       
  1831     def initMembers(self, namespaces, default_namespace_format, base_class, xsd_schema):
  1784         self.DefaultNamespaceFormat = default_namespace_format
  1832         self.DefaultNamespaceFormat = default_namespace_format
  1785         self.NSMAP = namespaces
  1833         self.NSMAP = namespaces
  1786         targetNamespace = etree.QName(default_namespace_format % "d").namespace
  1834         targetNamespace = etree.QName(default_namespace_format % "d").namespace
  1787         if targetNamespace is not None:
  1835         if targetNamespace is not None:
  1788             self.RootNSMAP = {
  1836             self.RootNSMAP = {
  1825             self.DefaultNamespaceFormat % parent_tag
  1873             self.DefaultNamespaceFormat % parent_tag
  1826             if parent_tag is not None else parent_tag,
  1874             if parent_tag is not None else parent_tag,
  1827             None)
  1875             None)
  1828 
  1876 
  1829     def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
  1877     def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
       
  1878         """
       
  1879         Create XML element based on elements and parent's tag names.
       
  1880 
       
  1881         :param element_tag:
       
  1882             element's tag name
       
  1883         :param parent_tag:
       
  1884             optional parent's tag name. Default value is None.
       
  1885         :param class_idx:
       
  1886             optional index of class in list of founded classes
       
  1887             with same element and parent. Default value is None.
       
  1888         :return:
       
  1889             created XML element
       
  1890             (subclass of lxml.etree._Element created by class factory)
       
  1891         """
  1830         element_class = self.GetElementClass(element_tag, parent_tag)
  1892         element_class = self.GetElementClass(element_tag, parent_tag)
  1831         if isinstance(element_class, ListType):
  1893         if isinstance(element_class, ListType):
  1832             if class_idx is not None and class_idx < len(element_class):
  1894             if class_idx is not None and class_idx < len(element_class):
  1833                 new_element = element_class[class_idx]()
  1895                 element_class = element_class[class_idx]
  1834             else:
  1896             else:
  1835                 raise ValueError("No corresponding class found!")
  1897                 raise ValueError("No corresponding class found!")
  1836         else:
  1898         return self.CreateElementFromClass(element_class, element_tag)
  1837             new_element = element_class()
  1899 
       
  1900     def CreateElementFromClass(self, element_class, element_tag=None):
       
  1901         """
       
  1902         Create XML element instance of submitted element's class.
       
  1903         Submitted class should be subclass of lxml.etree._Element.
       
  1904 
       
  1905         element_class shouldn't be used to create XML element
       
  1906         directly using element_class(), because lxml backend
       
  1907         should be aware what class handles what xml element,
       
  1908         otherwise default lxml.etree._Element will be used.
       
  1909 
       
  1910         :param element_class:
       
  1911             element class
       
  1912         :param element_tag:
       
  1913             optional element's tag name.
       
  1914             If omitted it's calculated from element_class instance.
       
  1915         :return:
       
  1916             created XML element
       
  1917             (subclass of lxml.etree._Element created by class factory)
       
  1918         """
       
  1919         if element_tag is None:
       
  1920             element_tag = element_class().tag
       
  1921         etag = self.DefaultNamespaceFormat % element_tag
       
  1922         self.ClassLookup.SetLookupResult(etag, element_class)
       
  1923         new_element = self.makeelement(etag)
       
  1924         self.ClassLookup.ResetLookupResult()
  1838         DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
  1925         DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
  1839         new_element._init_()
  1926         new_element._init_()
  1840         return new_element
  1927         return new_element
  1841 
  1928 
  1842 
  1929 
  1843 def GenerateParser(factory, xsdstring):
  1930 def GenerateParser(factory, xsdstring):
  1844     """
  1931     """
  1845     This function generate a xml parser from a class factory
  1932     This function generate a xml parser from a class factory
  1846     """
  1933     """
  1847 
  1934 
       
  1935     parser = XMLClassParser(strip_cdata=False, remove_blank_text=True)
       
  1936     factory.Parser = parser
       
  1937 
  1848     ComputedClasses = factory.CreateClasses()
  1938     ComputedClasses = factory.CreateClasses()
  1849 
       
  1850     if factory.FileName is not None:
  1939     if factory.FileName is not None:
  1851         ComputedClasses = ComputedClasses[factory.FileName]
  1940         ComputedClasses = ComputedClasses[factory.FileName]
  1852     BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
  1941     BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
  1853 
  1942 
  1854     parser = XMLClassParser(
  1943     parser.initMembers(
  1855         factory.NSMAP,
  1944         factory.NSMAP,
  1856         factory.etreeNamespaceFormat,
  1945         factory.etreeNamespaceFormat,
  1857         BaseClass[0] if len(BaseClass) == 1 else None,
  1946         BaseClass[0] if len(BaseClass) == 1 else None,
  1858         etree.XMLSchema(etree.fromstring(xsdstring)),
  1947         etree.XMLSchema(etree.fromstring(xsdstring)))
  1859         strip_cdata=False, remove_blank_text=True)
  1948 
  1860     class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
  1949     class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
  1861     parser.set_element_class_lookup(class_lookup)
  1950     parser.set_element_class_lookup(class_lookup)
  1862 
  1951 
  1863     return parser
  1952     return parser