Keeps documentation defined in the XML Schema

authorOlivier CAYROL (Logilab) <Olivier.Cayrol@logilab.fr>
changeset88ffd0b84d45
branchdefault
phasedraft
hiddenyes
parent revision#cfd59d5e0433 Fix packaging, stop using symlink
child revision<not specified>
files modified by this revision
pyxst/xsd/root.py
pyxst/xsd/utils.py
# HG changeset patch
# User Olivier CAYROL (Logilab) <Olivier.Cayrol@logilab.fr>
# Date 1462207426 -7200
# Mon May 02 18:43:46 2016 +0200
# Node ID 88ffd0b84d45d53949c1cfc967ce51c8e9c4d1d6
# Parent cfd59d5e0433a212bca29f13c49dfd24e623284c
Keeps documentation defined in the XML Schema

diff --git a/pyxst/xsd/root.py b/pyxst/xsd/root.py
@@ -33,10 +33,11 @@
1  from .types import (
2      XsdComplexType, XsdSimpleType, XsdSimpleListType, XsdSimpleUnionType,
3      XsdType, CONSTRAINT_MAPPING, RESTRICTION, EXTENSION)
4  from .groups import (XsdChoice, XsdAll, XsdSequence)
5  from traceback import format_exc
6 +from lxml import etree
7 
8 
9  XSD_BUILT_IN_TYPES = set([
10          u"anyType", u"anySimpleType", u"boolean", u"base64Binary", u"hexBinary",
11          u"float", u"decimal", u"integer", u"nonPositiveInteger",
@@ -688,10 +689,11 @@
12          :returns:
13              ``node`` after it has been filled.
14          :rtype:
15              :class:`~pyxst.xml_struct.xsd.elements.XsdElement`
16          """
17 +        self.collect_documentation(node, xml_obj)
18          type_nodes = self.get_type_nodes_from_xml(xml_obj, u"type",
19                                                    uid_prefix=node.uid)
20          if len(type_nodes) == 0:
21              # Tries to get the type from the substitution group
22              subs_name = xml_obj.get("substitutionGroup")
@@ -726,10 +728,11 @@
23          :returns:
24              ``node`` after it has been filled.
25          :rtype:
26              :class:`~pyxst.xml_struct.xsd.attributes.XsdAttributeGroup`
27          """
28 +        self.collect_documentation(node, xml_obj)
29          self.add_attributes_to_node(node, xml_obj)
30          return node
31 
32      def fill_attribute_node(self, node, xml_obj):
33          """
@@ -751,10 +754,11 @@
34          :returns:
35              ``node`` after it has been filled.
36          :rtype:
37              :class:`~pyxst.xml_struct.xsd.attributes.XsdAttribute`
38          """
39 +        self.collect_documentation(node, xml_obj)
40          type_nodes = self.get_type_nodes_from_xml(xml_obj, u"type",
41                                                    uid_prefix=node.uid)
42          if len(type_nodes) == 0:
43              raise XsdError("%s should contain either a reference or an inner "
44                             "definition of type in %s XML Schema"
@@ -780,13 +784,15 @@
45          :returns:
46              ``node`` after it has been filled.
47          :rtype:
48              :class:`~pyxst.xml_struct.xsd.types.XsdSimpleType`
49          """
50 +        self.collect_documentation(node, xml_obj)
51          if xml_obj.find(u"{%s}restriction" % XSD_NS) is not None:
52              # Restriction of a base simple type
53              rst_obj = xml_obj.find(u"{%s}restriction" % XSD_NS)
54 +            self.collect_documentation(node, rst_obj)
55              type_nodes = self.get_type_nodes_from_xml(
56                  rst_obj, u"base", uid_prefix=node.uid+u"/restriction")
57              if len(type_nodes) == 0:
58                  raise XsdError("Restriction in %s should contain either a "
59                                 "reference or an inner definition of type in "
@@ -843,10 +849,11 @@
60          :returns:
61              ``node`` after it has been filled.
62          :rtype:
63              :class:`~pyxst.xml_struct.xsd.types.XsdComplexType`
64          """
65 +        self.collect_documentation(node, xml_obj)
66          if xml_obj.find(u"{%s}simpleContent" % XSD_NS) is not None:
67              self.fill_node_from_simple_content(node,
68                                    xml_obj.find(u"{%s}simpleContent" % XSD_NS))
69          elif xml_obj.find(u"{%s}complexContent" % XSD_NS) is not None:
70              if xml_obj.get(u"mixed", u"false") == u"true":
@@ -894,21 +901,24 @@
71          :returns:
72              ``node`` after it has been filled.
73          :rtype:
74              :class:`~pyxst.xml_struct.xsd.types.XsdComplexType`
75          """
76 +        self.collect_documentation(node, xml_obj)
77          if xml_obj.find(u"{%s}restriction" % XSD_NS) is not None:
78              der_obj = xml_obj.find(u"{%s}restriction" % XSD_NS)
79              der_meth = RESTRICTION
80 +            self.collect_documentation(node, der_obj)
81              for child_obj in der_obj:
82                  ns, child_name = split_qname(child_obj.tag)
83                  if ns != XSD_NS or child_name not in CONSTRAINT_MAPPING:
84                      continue
85                  node.add_constraint(child_name, child_obj.get(u"value"))
86          elif xml_obj.find(u"{%s}extension" % XSD_NS) is not None:
87              der_obj = xml_obj.find(u"{%s}extension" % XSD_NS)
88              der_meth = EXTENSION
89 +            self.collect_documentation(node, der_obj)
90          else:
91              raise XsdError("Simple Content in %s should contain either a "
92                             "<restriction> or an <extension> child in the "
93                             "%s XML Schema" % (node, self._xsd_file))
94          type_nodes = self.get_type_nodes_from_xml(
@@ -941,18 +951,21 @@
95          :returns:
96              ``node`` after it has been filled.
97          :rtype:
98              :class:`~pyxst.xml_struct.xsd.types.XsdComplexType`
99          """
100 +        self.collect_documentation(node, xml_obj)
101          if xml_obj.get(u"mixed", u"false") == u"true":
102              node.content_type = MIXED
103          if xml_obj.find(u"{%s}restriction" % XSD_NS) is not None:
104              der_obj = xml_obj.find(u"{%s}restriction" % XSD_NS)
105              der_meth = RESTRICTION
106 +            self.collect_documentation(node, der_obj)
107          elif xml_obj.find(u"{%s}extension" % XSD_NS) is not None:
108              der_obj = xml_obj.find(u"{%s}extension" % XSD_NS)
109              der_meth = EXTENSION
110 +            self.collect_documentation(node, der_obj)
111          else:
112              raise XsdError("Complex Content in %s should contain either a "
113                             "<restriction> or an <extension> child in the "
114                             "%s XML Schema" % (node, self._xsd_file))
115          type_nodes = self.get_type_nodes_from_xml(
@@ -1001,10 +1014,11 @@
116          :returns:
117              ``node`` after it has been filled.
118          :rtype:
119              :class:`~pyxst.xml_struct.xsd.groups.XsdGroup`
120          """
121 +        self.collect_documentation(node, xml_obj)
122          if xml_obj.tag == "{%s}group" % XSD_NS:
123              # Finds the all, choice or sequence inside the group and uses it
124              for child_obj in xml_obj:
125                  ns, child_sort = split_qname(child_obj.tag)
126                  if ns == XSD_NS and child_sort in [u"choice", u"all",
@@ -1032,5 +1046,16 @@
127                  continue
128              min_occur, max_occur = self.get_min_max_occur_from_xml(child_obj,
129                                                                     item_node)
130              node.add_item(item_node, min_occur, max_occur)
131          return node
132 +
133 +    def collect_documentation(self, node, xml_obj):
134 +        """
135 +        Adds documentation for the annotation XML elements into the node.
136 +        """
137 +        for xml_doc in xml_obj.xpath(u"xsd:annotation/xsd:documentation",
138 +                                     namespaces={u"xsd": XSD_NS}):
139 +            text = etree.tounicode(xml_doc)
140 +            start = text.find(u">") + 1
141 +            end = text.rfind(u"</")
142 +            node.desc.append(text[start:end])
diff --git a/pyxst/xsd/utils.py b/pyxst/xsd/utils.py
@@ -192,10 +192,16 @@
143 
144         Unique identifier of the node in its category.
145 
146         Type: :class:`unicode`
147 
148 +    .. attribute:: desc
149 +
150 +       List of the documentation data given inside the schema for this node.
151 +
152 +       Type: :class:`list` of :class:`unicode`
153 +
154      .. automethod:: __init__
155      """
156      category = ""
157 
158      def __init__(self, name, uid_prefix=u""):
@@ -207,10 +213,11 @@
159          :type name:
160              :class:`unicode`
161          """
162          self.name = name
163          self.uid_prefix = uid_prefix
164 +        self.desc = []
165          if self.__class__ is XsdNode:
166              raise NotImplementedError("Can't instanciate abstract class")
167 
168      @property
169      def uid(self):