diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py new file mode 100644 index 0000000000..3cf00a38e1 --- /dev/null +++ b/docs/_ext/djangodocs.py @@ -0,0 +1,229 @@ +""" +Sphinx plugins for Django documentation. +""" +import os +import re + +from docutils import nodes, transforms +try: + import json +except ImportError: + try: + import simplejson as json + except ImportError: + try: + from django.utils import simplejson as json + except ImportError: + json = None + +from sphinx import addnodes, roles, __version__ as sphinx_ver +from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.writers.html import SmartyPantsHTMLTranslator +from sphinx.util.console import bold +from sphinx.util.compat import Directive + +# RE for option descriptions without a '--' prefix +simple_option_desc_re = re.compile( + r'([-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)') + +def setup(app): + app.add_crossref_type( + directivename = "setting", + rolename = "setting", + indextemplate = "pair: %s; setting", + ) + app.add_crossref_type( + directivename = "templatetag", + rolename = "ttag", + indextemplate = "pair: %s; template tag" + ) + app.add_crossref_type( + directivename = "templatefilter", + rolename = "tfilter", + indextemplate = "pair: %s; template filter" + ) + app.add_crossref_type( + directivename = "fieldlookup", + rolename = "lookup", + indextemplate = "pair: %s; field lookup type", + ) + app.add_description_unit( + directivename = "django-admin", + rolename = "djadmin", + indextemplate = "pair: %s; django-admin command", + parse_node = parse_django_admin_node, + ) + app.add_description_unit( + directivename = "django-admin-option", + rolename = "djadminopt", + indextemplate = "pair: %s; django-admin command-line option", + parse_node = parse_django_adminopt_node, + ) + app.add_config_value('django_next_version', '0.0', True) + app.add_directive('versionadded', VersionDirective) + app.add_directive('versionchanged', VersionDirective) + app.add_builder(DjangoStandaloneHTMLBuilder) + + +class VersionDirective(Directive): + has_content = True + required_arguments = 1 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {} + + def run(self): + env = self.state.document.settings.env + arg0 = self.arguments[0] + is_nextversion = env.config.django_next_version == arg0 + ret = [] + node = addnodes.versionmodified() + ret.append(node) + if not is_nextversion: + if len(self.arguments) == 1: + linktext = 'Please see the release notes ' % (arg0) + xrefs = roles.XRefRole()('doc', linktext, linktext, self.lineno, self.state) + node.extend(xrefs[0]) + node['version'] = arg0 + else: + node['version'] = "Development version" + node['type'] = self.name + if len(self.arguments) == 2: + inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1) + node.extend(inodes) + if self.content: + self.state.nested_parse(self.content, self.content_offset, node) + ret = ret + messages + env.note_versionchange(node['type'], node['version'], node, self.lineno) + return ret + + +class DjangoHTMLTranslator(SmartyPantsHTMLTranslator): + """ + Django-specific reST to HTML tweaks. + """ + + # Don't use border=1, which docutils does by default. + def visit_table(self, node): + self._table_row_index = 0 # Needed by Sphinx + self.body.append(self.starttag(node, 'table', CLASS='docutils')) + + # ? Really? + def visit_desc_parameterlist(self, node): + self.body.append('(') + self.first_param = 1 + self.param_separator = node.child_text_separator + + def depart_desc_parameterlist(self, node): + self.body.append(')') + + if sphinx_ver < '1.0.8': + # + # Don't apply smartypants to literal blocks + # + def visit_literal_block(self, node): + self.no_smarty += 1 + SmartyPantsHTMLTranslator.visit_literal_block(self, node) + + def depart_literal_block(self, node): + SmartyPantsHTMLTranslator.depart_literal_block(self, node) + self.no_smarty -= 1 + + # + # Turn the "new in version" stuff (versionadded/versionchanged) into a + # better callout -- the Sphinx default is just a little span, + # which is a bit less obvious that I'd like. + # + # FIXME: these messages are all hardcoded in English. We need to change + # that to accomodate other language docs, but I can't work out how to make + # that work. + # + version_text = { + 'deprecated': 'Deprecated in Django %s', + 'versionchanged': 'Changed in Django %s', + 'versionadded': 'New in Django %s', + } + + def visit_versionmodified(self, node): + self.body.append( + self.starttag(node, 'div', CLASS=node['type']) + ) + title = "%s%s" % ( + self.version_text[node['type']] % node['version'], + len(node) and ":" or "." + ) + self.body.append('%s ' % title) + + def depart_versionmodified(self, node): + self.body.append("\n") + + # Give each section a unique ID -- nice for custom CSS hooks + def visit_section(self, node): + old_ids = node.get('ids', []) + node['ids'] = ['s-' + i for i in old_ids] + node['ids'].extend(old_ids) + SmartyPantsHTMLTranslator.visit_section(self, node) + node['ids'] = old_ids + +def parse_django_admin_node(env, sig, signode): + command = sig.split(' ')[0] + env._django_curr_admin_command = command + title = "django-admin.py %s" % sig + signode += addnodes.desc_name(title, title) + return sig + +def parse_django_adminopt_node(env, sig, signode): + """A copy of sphinx.directives.CmdoptionDesc.parse_signature()""" + from sphinx.domains.std import option_desc_re + count = 0 + firstname = '' + for m in option_desc_re.finditer(sig): + optname, args = m.groups() + if count: + signode += addnodes.desc_addname(', ', ', ') + signode += addnodes.desc_name(optname, optname) + signode += addnodes.desc_addname(args, args) + if not count: + firstname = optname + count += 1 + if not count: + for m in simple_option_desc_re.finditer(sig): + optname, args = m.groups() + if count: + signode += addnodes.desc_addname(', ', ', ') + signode += addnodes.desc_name(optname, optname) + signode += addnodes.desc_addname(args, args) + if not count: + firstname = optname + count += 1 + if not firstname: + raise ValueError + return firstname + + +class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder): + """ + Subclass to add some extra things we need. + """ + + name = 'djangohtml' + + def finish(self): + super(DjangoStandaloneHTMLBuilder, self).finish() + if json is None: + self.warn("cannot create templatebuiltins.js due to missing simplejson dependency") + return + self.info(bold("writing templatebuiltins.js...")) + xrefs = self.env.domaindata["std"]["objects"] + templatebuiltins = { + "ttags": [n for ((t, n), (l, a)) in xrefs.items() + if t == "templatetag" and l == "ref/templates/builtins"], + "tfilters": [n for ((t, n), (l, a)) in xrefs.items() + if t == "templatefilter" and l == "ref/templates/builtins"], + } + outfilename = os.path.join(self.outdir, "templatebuiltins.js") + f = open(outfilename, 'wb') + f.write('var django_template_builtins = ') + json.dump(templatebuiltins, f) + f.write(';\n') + f.close(); diff --git a/docs/conf.py b/docs/conf.py index 6c4a38113a..d881a7cad6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,7 @@ import sys, os # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "_ext"))) # -- General configuration ----------------------------------------------------- @@ -25,7 +26,9 @@ import sys, os # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] +#extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] +#extensions = ["djangodocs", "sphinx.ext.intersphinx"] +extensions = ['djangodocs'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -51,7 +54,7 @@ copyright = u'2011, Roberto Rosario' version = '0.12' # The full version, including alpha/beta/rc tags. -release = '0.12' +release = '0.12 beta' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -71,15 +74,15 @@ exclude_patterns = ['_build'] #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +add_module_names = False # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx'