PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/docs/_ext/djangodocs.py

https://code.google.com/p/mango-py/
Python | 248 lines | 199 code | 23 blank | 26 comment | 17 complexity | 22b26aab5d1cf0bc4c18c26d9a872966 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. """
  2. Sphinx plugins for Django documentation.
  3. """
  4. import os
  5. import re
  6. from docutils import nodes, transforms
  7. try:
  8. import json
  9. except ImportError:
  10. try:
  11. import simplejson as json
  12. except ImportError:
  13. try:
  14. from django.utils import simplejson as json
  15. except ImportError:
  16. json = None
  17. from sphinx import addnodes, roles
  18. from sphinx.builders.html import StandaloneHTMLBuilder
  19. from sphinx.writers.html import SmartyPantsHTMLTranslator
  20. from sphinx.util.console import bold
  21. from sphinx.util.compat import Directive
  22. # RE for option descriptions without a '--' prefix
  23. simple_option_desc_re = re.compile(
  24. r'([-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
  25. def setup(app):
  26. app.add_crossref_type(
  27. directivename = "setting",
  28. rolename = "setting",
  29. indextemplate = "pair: %s; setting",
  30. )
  31. app.add_crossref_type(
  32. directivename = "templatetag",
  33. rolename = "ttag",
  34. indextemplate = "pair: %s; template tag"
  35. )
  36. app.add_crossref_type(
  37. directivename = "templatefilter",
  38. rolename = "tfilter",
  39. indextemplate = "pair: %s; template filter"
  40. )
  41. app.add_crossref_type(
  42. directivename = "fieldlookup",
  43. rolename = "lookup",
  44. indextemplate = "pair: %s; field lookup type",
  45. )
  46. app.add_description_unit(
  47. directivename = "django-admin",
  48. rolename = "djadmin",
  49. indextemplate = "pair: %s; django-admin command",
  50. parse_node = parse_django_admin_node,
  51. )
  52. app.add_description_unit(
  53. directivename = "django-admin-option",
  54. rolename = "djadminopt",
  55. indextemplate = "pair: %s; django-admin command-line option",
  56. parse_node = parse_django_adminopt_node,
  57. )
  58. app.add_config_value('django_next_version', '0.0', True)
  59. app.add_directive('versionadded', VersionDirective)
  60. app.add_directive('versionchanged', VersionDirective)
  61. app.add_transform(SuppressBlockquotes)
  62. app.add_builder(DjangoStandaloneHTMLBuilder)
  63. class VersionDirective(Directive):
  64. has_content = True
  65. required_arguments = 1
  66. optional_arguments = 1
  67. final_argument_whitespace = True
  68. option_spec = {}
  69. def run(self):
  70. env = self.state.document.settings.env
  71. arg0 = self.arguments[0]
  72. is_nextversion = env.config.django_next_version == arg0
  73. ret = []
  74. node = addnodes.versionmodified()
  75. ret.append(node)
  76. if not is_nextversion:
  77. if len(self.arguments) == 1:
  78. linktext = 'Please, see the release notes </releases/%s>' % (arg0)
  79. xrefs = roles.XRefRole()('doc', linktext, linktext, self.lineno, self.state)
  80. node.extend(xrefs[0])
  81. node['version'] = arg0
  82. else:
  83. node['version'] = "Development version"
  84. node['type'] = self.name
  85. if len(self.arguments) == 2:
  86. inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1)
  87. node.extend(inodes)
  88. if self.content:
  89. self.state.nested_parse(self.content, self.content_offset, node)
  90. ret = ret + messages
  91. env.note_versionchange(node['type'], node['version'], node, self.lineno)
  92. return ret
  93. class SuppressBlockquotes(transforms.Transform):
  94. """
  95. Remove the default blockquotes that encase indented list, tables, etc.
  96. """
  97. default_priority = 300
  98. suppress_blockquote_child_nodes = (
  99. nodes.bullet_list,
  100. nodes.enumerated_list,
  101. nodes.definition_list,
  102. nodes.literal_block,
  103. nodes.doctest_block,
  104. nodes.line_block,
  105. nodes.table
  106. )
  107. def apply(self):
  108. for node in self.document.traverse(nodes.block_quote):
  109. if len(node.children) == 1 and isinstance(node.children[0], self.suppress_blockquote_child_nodes):
  110. node.replace_self(node.children[0])
  111. class DjangoHTMLTranslator(SmartyPantsHTMLTranslator):
  112. """
  113. Django-specific reST to HTML tweaks.
  114. """
  115. # Don't use border=1, which docutils does by default.
  116. def visit_table(self, node):
  117. self.body.append(self.starttag(node, 'table', CLASS='docutils'))
  118. # <big>? Really?
  119. def visit_desc_parameterlist(self, node):
  120. self.body.append('(')
  121. self.first_param = 1
  122. def depart_desc_parameterlist(self, node):
  123. self.body.append(')')
  124. #
  125. # Don't apply smartypants to literal blocks
  126. #
  127. def visit_literal_block(self, node):
  128. self.no_smarty += 1
  129. SmartyPantsHTMLTranslator.visit_literal_block(self, node)
  130. def depart_literal_block(self, node):
  131. SmartyPantsHTMLTranslator.depart_literal_block(self, node)
  132. self.no_smarty -= 1
  133. #
  134. # Turn the "new in version" stuff (versionadded/versionchanged) into a
  135. # better callout -- the Sphinx default is just a little span,
  136. # which is a bit less obvious that I'd like.
  137. #
  138. # FIXME: these messages are all hardcoded in English. We need to change
  139. # that to accomodate other language docs, but I can't work out how to make
  140. # that work.
  141. #
  142. version_text = {
  143. 'deprecated': 'Deprecated in Django %s',
  144. 'versionchanged': 'Changed in Django %s',
  145. 'versionadded': 'New in Django %s',
  146. }
  147. def visit_versionmodified(self, node):
  148. self.body.append(
  149. self.starttag(node, 'div', CLASS=node['type'])
  150. )
  151. title = "%s%s" % (
  152. self.version_text[node['type']] % node['version'],
  153. len(node) and ":" or "."
  154. )
  155. self.body.append('<span class="title">%s</span> ' % title)
  156. def depart_versionmodified(self, node):
  157. self.body.append("</div>\n")
  158. # Give each section a unique ID -- nice for custom CSS hooks
  159. def visit_section(self, node):
  160. old_ids = node.get('ids', [])
  161. node['ids'] = ['s-' + i for i in old_ids]
  162. node['ids'].extend(old_ids)
  163. SmartyPantsHTMLTranslator.visit_section(self, node)
  164. node['ids'] = old_ids
  165. def parse_django_admin_node(env, sig, signode):
  166. command = sig.split(' ')[0]
  167. env._django_curr_admin_command = command
  168. title = "django-admin.py %s" % sig
  169. signode += addnodes.desc_name(title, title)
  170. return sig
  171. def parse_django_adminopt_node(env, sig, signode):
  172. """A copy of sphinx.directives.CmdoptionDesc.parse_signature()"""
  173. from sphinx.domains.std import option_desc_re
  174. count = 0
  175. firstname = ''
  176. for m in option_desc_re.finditer(sig):
  177. optname, args = m.groups()
  178. if count:
  179. signode += addnodes.desc_addname(', ', ', ')
  180. signode += addnodes.desc_name(optname, optname)
  181. signode += addnodes.desc_addname(args, args)
  182. if not count:
  183. firstname = optname
  184. count += 1
  185. if not count:
  186. for m in simple_option_desc_re.finditer(sig):
  187. optname, args = m.groups()
  188. if count:
  189. signode += addnodes.desc_addname(', ', ', ')
  190. signode += addnodes.desc_name(optname, optname)
  191. signode += addnodes.desc_addname(args, args)
  192. if not count:
  193. firstname = optname
  194. count += 1
  195. if not firstname:
  196. raise ValueError
  197. return firstname
  198. class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder):
  199. """
  200. Subclass to add some extra things we need.
  201. """
  202. name = 'djangohtml'
  203. def finish(self):
  204. super(DjangoStandaloneHTMLBuilder, self).finish()
  205. if json is None:
  206. self.warn("cannot create templatebuiltins.js due to missing simplejson dependency")
  207. return
  208. self.info(bold("writing templatebuiltins.js..."))
  209. xrefs = self.env.domaindata["std"]["objects"]
  210. templatebuiltins = {
  211. "ttags": [n for ((t, n), (l, a)) in xrefs.items()
  212. if t == "templatetag" and l == "ref/templates/builtins"],
  213. "tfilters": [n for ((t, n), (l, a)) in xrefs.items()
  214. if t == "templatefilter" and l == "ref/templates/builtins"],
  215. }
  216. outfilename = os.path.join(self.outdir, "templatebuiltins.js")
  217. f = open(outfilename, 'wb')
  218. f.write('var django_template_builtins = ')
  219. json.dump(templatebuiltins, f)
  220. f.write(';\n')
  221. f.close();