You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.6 KiB
103 lines
3.6 KiB
"Misc. utility functions/classes for admin documentation generator." |
|
|
|
import re |
|
from email.Parser import HeaderParser |
|
from email.Errors import HeaderParseError |
|
from django.utils.safestring import mark_safe |
|
try: |
|
import docutils.core |
|
import docutils.nodes |
|
import docutils.parsers.rst.roles |
|
except ImportError: |
|
docutils_is_available = False |
|
else: |
|
docutils_is_available = True |
|
|
|
def trim_docstring(docstring): |
|
""" |
|
Uniformly trims leading/trailing whitespace from docstrings. |
|
|
|
Based on http://www.python.org/peps/pep-0257.html#handling-docstring-indentation |
|
""" |
|
if not docstring or not docstring.strip(): |
|
return '' |
|
# Convert tabs to spaces and split into lines |
|
lines = docstring.expandtabs().splitlines() |
|
indent = min([len(line) - len(line.lstrip()) for line in lines if line.lstrip()]) |
|
trimmed = [lines[0].lstrip()] + [line[indent:].rstrip() for line in lines[1:]] |
|
return "\n".join(trimmed).strip() |
|
|
|
def parse_docstring(docstring): |
|
""" |
|
Parse out the parts of a docstring. Returns (title, body, metadata). |
|
""" |
|
docstring = trim_docstring(docstring) |
|
parts = re.split(r'\n{2,}', docstring) |
|
title = parts[0] |
|
if len(parts) == 1: |
|
body = '' |
|
metadata = {} |
|
else: |
|
parser = HeaderParser() |
|
try: |
|
metadata = parser.parsestr(parts[-1]) |
|
except HeaderParseError: |
|
metadata = {} |
|
body = "\n\n".join(parts[1:]) |
|
else: |
|
metadata = dict(metadata.items()) |
|
if metadata: |
|
body = "\n\n".join(parts[1:-1]) |
|
else: |
|
body = "\n\n".join(parts[1:]) |
|
return title, body, metadata |
|
|
|
def parse_rst(text, default_reference_context, thing_being_parsed=None, link_base='../..'): |
|
""" |
|
Convert the string from reST to an XHTML fragment. |
|
""" |
|
overrides = { |
|
'doctitle_xform' : True, |
|
'inital_header_level' : 3, |
|
"default_reference_context" : default_reference_context, |
|
"link_base" : link_base, |
|
} |
|
if thing_being_parsed: |
|
thing_being_parsed = "<%s>" % thing_being_parsed |
|
parts = docutils.core.publish_parts(text, source_path=thing_being_parsed, |
|
destination_path=None, writer_name='html', |
|
settings_overrides=overrides) |
|
return mark_safe(parts['fragment']) |
|
|
|
# |
|
# reST roles |
|
# |
|
ROLES = { |
|
'model' : '%s/models/%s/', |
|
'view' : '%s/views/%s/', |
|
'template' : '%s/templates/%s/', |
|
'filter' : '%s/filters/#%s', |
|
'tag' : '%s/tags/#%s', |
|
} |
|
|
|
def create_reference_role(rolename, urlbase): |
|
def _role(name, rawtext, text, lineno, inliner, options=None, content=None): |
|
if options is None: options = {} |
|
if content is None: content = [] |
|
node = docutils.nodes.reference(rawtext, text, refuri=(urlbase % (inliner.document.settings.link_base, text.lower())), **options) |
|
return [node], [] |
|
docutils.parsers.rst.roles.register_canonical_role(rolename, _role) |
|
|
|
def default_reference_role(name, rawtext, text, lineno, inliner, options=None, content=None): |
|
if options is None: options = {} |
|
if content is None: content = [] |
|
context = inliner.document.settings.default_reference_context |
|
node = docutils.nodes.reference(rawtext, text, refuri=(ROLES[context] % (inliner.document.settings.link_base, text.lower())), **options) |
|
return [node], [] |
|
|
|
if docutils_is_available: |
|
docutils.parsers.rst.roles.register_canonical_role('cmsreference', default_reference_role) |
|
docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE = 'cmsreference' |
|
|
|
for name, urlbase in ROLES.items(): |
|
create_reference_role(name, urlbase)
|
|
|