Fixes an issue where the organization home page would throw a 505 when no projects...
[Melange.git] / app / django / contrib / admindocs / utils.py
blob4a45a622b28331ac3d3b860d69b8e8923608ec3f
1 "Misc. utility functions/classes for admin documentation generator."
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
16 def trim_docstring(docstring):
17 """
18 Uniformly trims leading/trailing whitespace from docstrings.
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 # 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):
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
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,
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'])
73 # reST roles
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',
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)
88 return [node], []
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)
96 return [node], []
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)