xmlclass/xmlclass.py
changeset 2305 56f1d8aca886
parent 2297 96ca6b056c55
child 2343 33071a451021
equal deleted inserted replaced
2273:a0efe3d9c853 2305:56f1d8aca886
   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 = []
  1171         setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
  1173         setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
  1172         setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
  1174         setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
  1173         class_infos = {
  1175         class_infos = {
  1174             "type": COMPILEDCOMPLEXTYPE,
  1176             "type": COMPILEDCOMPLEXTYPE,
  1175             "name": classname,
  1177             "name": classname,
  1176             "initial": generateClassCreateFunction(class_definition),
  1178             "initial": generateClassCreateFunction(self, class_definition),
  1177         }
  1179         }
  1178 
       
  1179         if self.FileName is not None:
  1180         if self.FileName is not None:
  1180             self.ComputedClasses[self.FileName][classname] = class_definition
  1181             self.ComputedClasses[self.FileName][classname] = class_definition
  1181         else:
  1182         else:
  1182             self.ComputedClasses[classname] = class_definition
  1183             self.ComputedClasses[classname] = class_definition
  1183         self.ComputedClassesInfos[classname] = class_infos
  1184         self.ComputedClassesInfos[classname] = class_infos
  1266         return re.compile(base_structure_pattern + "".join(elements) + "$")
  1267         return re.compile(base_structure_pattern + "".join(elements) + "$")
  1267     else:
  1268     else:
  1268         raise ValueError("XSD structure not yet supported!")
  1269         raise ValueError("XSD structure not yet supported!")
  1269 
  1270 
  1270 
  1271 
  1271 def generateClassCreateFunction(class_definition):
  1272 def generateClassCreateFunction(factory, class_definition):
  1272     """
  1273     """
  1273     Method that generate the method for creating a class instance
  1274     Method that generate the method for creating a class instance
  1274     """
  1275     """
  1275     def classCreatefunction():
  1276     def classCreatefunction():
  1276         return class_definition()
  1277         return factory.Parser.CreateElementFromClass(class_definition)
  1277     return classCreatefunction
  1278     return classCreatefunction
  1278 
  1279 
  1279 
  1280 
  1280 def generateGetattrMethod(factory, class_definition, classinfos):
  1281 def generateGetattrMethod(factory, class_definition, classinfos):
  1281     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"])
  1384                     if not isinstance(value, ListType):
  1385                     if not isinstance(value, ListType):
  1385                         value = [value]
  1386                         value = [value]
  1386 
  1387 
  1387                     for element in reversed(value):
  1388                     for element in reversed(value):
  1388                         if element_infos["elmt_type"]["type"] == SIMPLETYPE:
  1389                         if element_infos["elmt_type"]["type"] == SIMPLETYPE:
  1389                             tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
  1390                             tmp_element = factory.Parser.makeelement(factory.etreeNamespaceFormat % name)
  1390                             tmp_element.text = element_infos["elmt_type"]["generate"](element)
  1391                             tmp_element.text = element_infos["elmt_type"]["generate"](element)
  1391                             element = tmp_element
  1392                             element = tmp_element
  1392                         self.insert(insertion_point, element)
  1393                         self.insert(insertion_point, element)
  1393 
  1394 
  1394         elif "base" in classinfos:
  1395         elif "base" in classinfos:
  1740 class XMLElementClassLookUp(etree.PythonElementClassLookup):
  1741 class XMLElementClassLookUp(etree.PythonElementClassLookup):
  1741 
  1742 
  1742     def __init__(self, classes, *args, **kwargs):
  1743     def __init__(self, classes, *args, **kwargs):
  1743         etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
  1744         etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
  1744         self.LookUpClasses = classes
  1745         self.LookUpClasses = classes
       
  1746         self.ElementTag = None
       
  1747         self.ElementClass = None
  1745 
  1748 
  1746     def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
  1749     def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
  1747         element_class = self.LookUpClasses.get(element_tag, (default, None))
  1750         element_class = self.LookUpClasses.get(element_tag, (default, None))
  1748         if not isinstance(element_class, DictType):
  1751         if not isinstance(element_class, DictType):
  1749             if isinstance(element_class[0], (StringType, UnicodeType)):
  1752             if isinstance(element_class[0], (StringType, UnicodeType)):
  1753         element_with_parent_class = element_class.get(parent_tag, default)
  1756         element_with_parent_class = element_class.get(parent_tag, default)
  1754         if isinstance(element_with_parent_class, (StringType, UnicodeType)):
  1757         if isinstance(element_with_parent_class, (StringType, UnicodeType)):
  1755             return self.GetElementClass(element_with_parent_class, default=default)
  1758             return self.GetElementClass(element_with_parent_class, default=default)
  1756         return element_with_parent_class
  1759         return element_with_parent_class
  1757 
  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 
  1758     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 
  1759         parent = element.getparent()
  1811         parent = element.getparent()
  1760         element_class = self.GetElementClass(
  1812         element_class = self.GetElementClass(
  1761             element.tag, parent.tag if parent is not None else None)
  1813             element.tag, parent.tag if parent is not None else None)
  1762         if isinstance(element_class, ListType):
  1814         if isinstance(element_class, ListType):
  1763             children = "".join([
  1815             children = "".join([
  1771             return element_class[0]
  1823             return element_class[0]
  1772         return element_class
  1824         return element_class
  1773 
  1825 
  1774 
  1826 
  1775 class XMLClassParser(etree.XMLParser):
  1827 class XMLClassParser(etree.XMLParser):
  1776 
  1828     def __init__(self, *args, **kwargs):
  1777     def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs):
       
  1778         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):
  1779         self.DefaultNamespaceFormat = default_namespace_format
  1832         self.DefaultNamespaceFormat = default_namespace_format
  1780         self.NSMAP = namespaces
  1833         self.NSMAP = namespaces
  1781         targetNamespace = etree.QName(default_namespace_format % "d").namespace
  1834         targetNamespace = etree.QName(default_namespace_format % "d").namespace
  1782         if targetNamespace is not None:
  1835         if targetNamespace is not None:
  1783             self.RootNSMAP = {
  1836             self.RootNSMAP = {
  1820             self.DefaultNamespaceFormat % parent_tag
  1873             self.DefaultNamespaceFormat % parent_tag
  1821             if parent_tag is not None else parent_tag,
  1874             if parent_tag is not None else parent_tag,
  1822             None)
  1875             None)
  1823 
  1876 
  1824     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         """
  1825         element_class = self.GetElementClass(element_tag, parent_tag)
  1892         element_class = self.GetElementClass(element_tag, parent_tag)
  1826         if isinstance(element_class, ListType):
  1893         if isinstance(element_class, ListType):
  1827             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):
  1828                 new_element = element_class[class_idx]()
  1895                 element_class = element_class[class_idx]
  1829             else:
  1896             else:
  1830                 raise ValueError("No corresponding class found!")
  1897                 raise ValueError("No corresponding class found!")
  1831         else:
  1898         return self.CreateElementFromClass(element_class, element_tag)
  1832             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()
  1833         DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
  1925         DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
  1834         new_element._init_()
  1926         new_element._init_()
  1835         return new_element
  1927         return new_element
  1836 
  1928 
  1837 
  1929 
  1838 def GenerateParser(factory, xsdstring):
  1930 def GenerateParser(factory, xsdstring):
  1839     """
  1931     """
  1840     This function generate a xml parser from a class factory
  1932     This function generate a xml parser from a class factory
  1841     """
  1933     """
  1842 
  1934 
       
  1935     parser = XMLClassParser(strip_cdata=False, remove_blank_text=True)
       
  1936     factory.Parser = parser
       
  1937 
  1843     ComputedClasses = factory.CreateClasses()
  1938     ComputedClasses = factory.CreateClasses()
  1844 
       
  1845     if factory.FileName is not None:
  1939     if factory.FileName is not None:
  1846         ComputedClasses = ComputedClasses[factory.FileName]
  1940         ComputedClasses = ComputedClasses[factory.FileName]
  1847     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]
  1848 
  1942 
  1849     parser = XMLClassParser(
  1943     parser.initMembers(
  1850         factory.NSMAP,
  1944         factory.NSMAP,
  1851         factory.etreeNamespaceFormat,
  1945         factory.etreeNamespaceFormat,
  1852         BaseClass[0] if len(BaseClass) == 1 else None,
  1946         BaseClass[0] if len(BaseClass) == 1 else None,
  1853         etree.XMLSchema(etree.fromstring(xsdstring)),
  1947         etree.XMLSchema(etree.fromstring(xsdstring)))
  1854         strip_cdata=False, remove_blank_text=True)
  1948 
  1855     class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
  1949     class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
  1856     parser.set_element_class_lookup(class_lookup)
  1950     parser.set_element_class_lookup(class_lookup)
  1857 
  1951 
  1858     return parser
  1952     return parser