1 "Misc. utility functions/classes for admin documentation generator."
2
3 import re
4 from email.Parser import HeaderParser
5 from email.Errors import HeaderParseError
6 from django.utils.safestring import mark_safe
7 try:
8 import docutils.core
9 import docutils.nodes
10 import docutils.parsers.rst.roles
11 except ImportError:
12 docutils_is_available = False
13 else:
14 docutils_is_available = True
15
17 """
18 Uniformly trims leading/trailing whitespace from docstrings.
19
20 Based on http://www.python.org/peps/pep-0257.html#handling-docstring-indentation
21 """
22 if not docstring or not docstring.strip():
23 return ''
24
25 lines = docstring.expandtabs().splitlines()
26 indent = min([len(line) - len(line.lstrip()) for line in lines if line.lstrip()])
27 trimmed = [lines[0].lstrip()] + [line[indent:].rstrip() for line in lines[1:]]
28 return "\n".join(trimmed).strip()
29
31 """
32 Parse out the parts of a docstring. Returns (title, body, metadata).
33 """
34 docstring = trim_docstring(docstring)
35 parts = re.split(r'\n{2,}', docstring)
36 title = parts[0]
37 if len(parts) == 1:
38 body = ''
39 metadata = {}
40 else:
41 parser = HeaderParser()
42 try:
43 metadata = parser.parsestr(parts[-1])
44 except HeaderParseError:
45 metadata = {}
46 body = "\n\n".join(parts[1:])
47 else:
48 metadata = dict(metadata.items())
49 if metadata:
50 body = "\n\n".join(parts[1:-1])
51 else:
52 body = "\n\n".join(parts[1:])
53 return title, body, metadata
54
55 -def parse_rst(text, default_reference_context, thing_being_parsed=None, link_base='../..'):
56 """
57 Convert the string from reST to an XHTML fragment.
58 """
59 overrides = {
60 'doctitle_xform' : True,
61 'inital_header_level' : 3,
62 "default_reference_context" : default_reference_context,
63 "link_base" : link_base,
64 }
65 if thing_being_parsed:
66 thing_being_parsed = "<%s>" % thing_being_parsed
67 parts = docutils.core.publish_parts(text, source_path=thing_being_parsed,
68 destination_path=None, writer_name='html',
69 settings_overrides=overrides)
70 return mark_safe(parts['fragment'])
71
72
73
74
75 ROLES = {
76 'model' : '%s/models/%s/',
77 'view' : '%s/views/%s/',
78 'template' : '%s/templates/%s/',
79 'filter' : '%s/filters/#%s',
80 'tag' : '%s/tags/#%s',
81 }
82
84 def _role(name, rawtext, text, lineno, inliner, options=None, content=None):
85 if options is None: options = {}
86 if content is None: content = []
87 node = docutils.nodes.reference(rawtext, text, refuri=(urlbase % (inliner.document.settings.link_base, text.lower())), **options)
88 return [node], []
89 docutils.parsers.rst.roles.register_canonical_role(rolename, _role)
90
97
98 if docutils_is_available:
99 docutils.parsers.rst.roles.register_canonical_role('cmsreference', default_reference_role)
100 docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE = 'cmsreference'
101
102 for name, urlbase in ROLES.items():
103 create_reference_role(name, urlbase)
104