Django

Code

root/djangoproject.com/django_website/apps/docs/builder.py

Revision 4795, 6.0 kB (checked in by jacob, 1 year ago)

Fixed a few broken model API examples.

Line 
1 """
2 Code to do the ReST --> HTML generation for the docs.
3 """
4
5 import re
6 import compiler
7 import smartypants
8 from docutils import nodes
9 from docutils.core import publish_parts
10 from docutils.writers import html4css1
11
12 def build_document(text):
13     """
14     Build a doc file into a dict of HTML bits.
15     """
16     return publish_parts(text, writer=DjangoHTMLWriter(), settings_overrides={'initial_header_level': 2})
17
18 docstring_re = re.compile(r"([\"']{3})(.*?)(\1)", re.DOTALL|re.MULTILINE)
19 def build_model_document(text):
20     """
21     Build a test example into a dict of HTML bits.
22     """
23     # We need to parse the model file without actually executing it.
24     tree = compiler.parse(text)
25    
26     # Get the title and blurb from the module's docstring
27     title, blurb = tree.doc.strip().split('\n', 1)
28     parts = publish_parts(blurb, writer=DjangoHTMLWriter(), settings_overrides={'initial_header_level': 2})
29     parts["title"] = title
30    
31     # Walk the tree and parse out the bits we care about.
32     visitor = compiler.walk(tree, ModelSourceVistor())
33     parts["api_usage"] = visitor.doctest
34     parts["models"] = visitor.models
35    
36     # Parse out the model source.
37     try:
38         model_source = text[:text.index("__test__")]       
39     except ValueError:
40         model_source = text
41     parts["model_source"] = model_source.replace(tree.doc, "").replace('""""""\n', '\n').strip()
42    
43     return parts
44
45 class ModelSourceVistor:
46     """AST visitor for a model module."""
47    
48     def __init__(self):
49         self.doctest = ""
50         self.models = []
51    
52     def visitAssign(self, node):
53         assname, valtree = node.getChildren()
54         if assname.name == "__test__":
55             self.doctest = valtree.getChildren()[1].value
56            
57     def visitClass(self, node):
58         if node.bases and isinstance(node.bases[0], compiler.ast.Getattr) and node.bases[0].attrname == "Model":
59             self.models.append(node.name)
60
61 class DjangoHTMLWriter(html4css1.Writer):
62     """
63     HTML writer that adds a "toc" key to the set of doc parts.
64     """
65     def __init__(self):
66         html4css1.Writer.__init__(self)
67         self.translator_class = DjangoHTMLTranslator
68
69     def translate(self):
70         # build the document
71         html4css1.Writer.translate(self)
72
73         # build the contents
74         contents = self.build_contents(self.document)
75         contents_doc = self.document.copy()
76         contents_doc.children = contents
77         contents_visitor = self.translator_class(contents_doc)
78         contents_doc.walkabout(contents_visitor)
79         self.parts['toc'] = "<ul class='toc'>%s</ul>" % ''.join(contents_visitor.fragment)
80
81     def build_contents(self, node, level=0):
82         level += 1
83         sections = []
84         i = len(node) - 1
85         while i >= 0 and isinstance(node[i], nodes.section):
86             sections.append(node[i])
87             i -= 1
88         sections.reverse()
89         entries = []
90         autonum = 0
91         depth = 4   # XXX FIXME
92         for section in sections:
93             title = section[0]
94             entrytext = title
95             try:
96                 reference = nodes.reference('', '', refid=section['ids'][0], *entrytext)
97             except IndexError:
98                 continue
99             ref_id = self.document.set_id(reference)
100             entry = nodes.paragraph('', '', reference)
101             item = nodes.list_item('', entry)
102             if level < depth:
103                 subsects = self.build_contents(section, level)
104                 item += subsects
105             entries.append(item)
106         if entries:
107             contents = nodes.bullet_list('', *entries)
108             return contents
109         else:
110             return []
111
112 class DjangoHTMLTranslator(html4css1.HTMLTranslator):
113     """
114     reST -> HTML translator subclass that fixes up the parts of docutils I don't like.
115     """
116    
117     # Prevent name attributes from being generated
118     named_tags = []
119    
120     def __init__(self, document):
121         html4css1.HTMLTranslator.__init__(self, document)
122         self._in_literal = 0
123    
124     # Remove the default border=1 from <table>   
125     def visit_table(self, node):
126         self.body.append(self.starttag(node, 'table', CLASS='docutils'))
127
128     # Prevent <h3> from becoming <h3><a id>
129     #def visit_title(self, node, move_ids=1):
130     #    if isinstance(node.parent, nodes.Admonition):
131     #        self.body.append(self.starttag(node, 'p', '', CLASS='admonition-title'))
132     #        self.context.append("</p>\n")
133     #    else:
134     #        html4css1.HTMLTranslator.visit_title(self, node, move_ids=0)
135    
136     #
137     # Apply smartypants to content when not inside literals
138     #
139     def visit_literal_block(self, node):
140         self._in_literal += 1
141         html4css1.HTMLTranslator.visit_literal_block(self, node)
142
143     def depart_literal_block(self, node):
144         html4css1.HTMLTranslator.depart_literal_block(self, node)
145         self._in_literal -= 1
146      
147     def visit_literal(self, node):
148         self._in_literal += 1
149         try:
150             html4css1.HTMLTranslator.visit_literal(self, node)
151         finally:
152             self._in_literal -= 1
153      
154     def encode(self, text):
155         text = html4css1.HTMLTranslator.encode(self, text)
156         if self._in_literal <= 0:
157             text = smartypants.smartyPants(text, "qde")
158         return text
159    
160     #
161     # Avoid <blockquote>s around merely indented nodes.
162     # Adapted from http://thread.gmane.org/gmane.text.docutils.user/742/focus=804
163     #
164    
165     _suppress_blockquote_child_nodes = (
166         nodes.bullet_list, nodes.enumerated_list, nodes.definition_list,
167         nodes.literal_block, nodes.doctest_block, nodes.line_block, nodes.table
168     )
169     def _bq_is_valid(self, node):
170         return len(node.children) != 1 or not isinstance(node.children[0], self._suppress_blockquote_child_nodes)
171                                        
172     def visit_block_quote(self, node):
173         if self._bq_is_valid(node):
174             html4css1.HTMLTranslator.visit_block_quote(self, node)
175
176     def depart_block_quote(self, node):
177         if self._bq_is_valid(node):
178             html4css1.HTMLTranslator.depart_block_quote(self, node)
179        
180    
Note: See TracBrowser for help on using the browser.