1 "Misc. utility functions/classes for admin documentation generator."
4 from email
.Parser
import HeaderParser
5 from email
.Errors
import HeaderParseError
6 from django
.utils
.safestring
import mark_safe
10 import docutils
.parsers
.rst
.roles
12 docutils_is_available
= False
14 docutils_is_available
= True
16 def trim_docstring(docstring
):
18 Uniformly trims leading/trailing whitespace from docstrings.
20 Based on http://www.python.org/peps/pep-0257.html#handling-docstring-indentation
22 if not docstring
or not docstring
.strip():
24 # Convert tabs to spaces and split into lines
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()
30 def parse_docstring(docstring
):
32 Parse out the parts of a docstring. Returns (title, body, metadata).
34 docstring
= trim_docstring(docstring
)
35 parts
= re
.split(r
'\n{2,}', docstring
)
41 parser
= HeaderParser()
43 metadata
= parser
.parsestr(parts
[-1])
44 except HeaderParseError
:
46 body
= "\n\n".join(parts
[1:])
48 metadata
= dict(metadata
.items())
50 body
= "\n\n".join(parts
[1:-1])
52 body
= "\n\n".join(parts
[1:])
53 return title
, body
, metadata
55 def parse_rst(text
, default_reference_context
, thing_being_parsed
=None, link_base
='../..'):
57 Convert the string from reST to an XHTML fragment.
60 'doctitle_xform' : True,
61 'inital_header_level' : 3,
62 "default_reference_context" : default_reference_context
,
63 "link_base" : link_base
,
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'])
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',
83 def create_reference_role(rolename
, urlbase
):
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
)
89 docutils
.parsers
.rst
.roles
.register_canonical_role(rolename
, _role
)
91 def default_reference_role(name
, rawtext
, text
, lineno
, inliner
, options
=None, content
=None):
92 if options
is None: options
= {}
93 if content
is None: content
= []
94 context
= inliner
.document
.settings
.default_reference_context
95 node
= docutils
.nodes
.reference(rawtext
, text
, refuri
=(ROLES
[context
] % (inliner
.document
.settings
.link_base
, text
.lower())), **options
)
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'
102 for name
, urlbase
in ROLES
.items():
103 create_reference_role(name
, urlbase
)