summaryrefslogtreecommitdiff
path: root/parts/django/docs/_ext
diff options
context:
space:
mode:
Diffstat (limited to 'parts/django/docs/_ext')
-rw-r--r--parts/django/docs/_ext/applyxrefs.py88
-rw-r--r--parts/django/docs/_ext/djangodocs.py265
-rw-r--r--parts/django/docs/_ext/literals_to_xrefs.py171
3 files changed, 524 insertions, 0 deletions
diff --git a/parts/django/docs/_ext/applyxrefs.py b/parts/django/docs/_ext/applyxrefs.py
new file mode 100644
index 0000000..3809088
--- /dev/null
+++ b/parts/django/docs/_ext/applyxrefs.py
@@ -0,0 +1,88 @@
+"""Adds xref targets to the top of files."""
+
+import sys
+import os
+
+testing = False
+
+DONT_TOUCH = (
+ './index.txt',
+ )
+
+def target_name(fn):
+ if fn.endswith('.txt'):
+ fn = fn[:-4]
+ return '_' + fn.lstrip('./').replace('/', '-')
+
+def process_file(fn, lines):
+ lines.insert(0, '\n')
+ lines.insert(0, '.. %s:\n' % target_name(fn))
+ try:
+ f = open(fn, 'w')
+ except IOError:
+ print("Can't open %s for writing. Not touching it." % fn)
+ return
+ try:
+ f.writelines(lines)
+ except IOError:
+ print("Can't write to %s. Not touching it." % fn)
+ finally:
+ f.close()
+
+def has_target(fn):
+ try:
+ f = open(fn, 'r')
+ except IOError:
+ print("Can't open %s. Not touching it." % fn)
+ return (True, None)
+ readok = True
+ try:
+ lines = f.readlines()
+ except IOError:
+ print("Can't read %s. Not touching it." % fn)
+ readok = False
+ finally:
+ f.close()
+ if not readok:
+ return (True, None)
+
+ #print fn, len(lines)
+ if len(lines) < 1:
+ print("Not touching empty file %s." % fn)
+ return (True, None)
+ if lines[0].startswith('.. _'):
+ return (True, None)
+ return (False, lines)
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ if len(argv) == 1:
+ argv.extend('.')
+
+ files = []
+ for root in argv[1:]:
+ for (dirpath, dirnames, filenames) in os.walk(root):
+ files.extend([(dirpath, f) for f in filenames])
+ files.sort()
+ files = [os.path.join(p, fn) for p, fn in files if fn.endswith('.txt')]
+ #print files
+
+ for fn in files:
+ if fn in DONT_TOUCH:
+ print("Skipping blacklisted file %s." % fn)
+ continue
+
+ target_found, lines = has_target(fn)
+ if not target_found:
+ if testing:
+ print '%s: %s' % (fn, lines[0]),
+ else:
+ print "Adding xref to %s" % fn
+ process_file(fn, lines)
+ else:
+ print "Skipping %s: already has a xref" % fn
+
+if __name__ == '__main__':
+ sys.exit(main()) \ No newline at end of file
diff --git a/parts/django/docs/_ext/djangodocs.py b/parts/django/docs/_ext/djangodocs.py
new file mode 100644
index 0000000..8c4b511
--- /dev/null
+++ b/parts/django/docs/_ext/djangodocs.py
@@ -0,0 +1,265 @@
+"""
+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
+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_transform(SuppressBlockquotes)
+ 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 </releases/%s>' % (arg0)
+ try:
+ xrefs = roles.XRefRole()('doc', linktext, linktext, self.lineno, self.state) # Sphinx >= 1.0
+ except AttributeError:
+ xrefs = roles.xfileref_role('doc', linktext, linktext, self.lineno, self.state) # Sphinx < 1.0
+ 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 SuppressBlockquotes(transforms.Transform):
+ """
+ Remove the default blockquotes that encase indented list, tables, etc.
+ """
+ default_priority = 300
+
+ suppress_blockquote_child_nodes = (
+ nodes.bullet_list,
+ nodes.enumerated_list,
+ nodes.definition_list,
+ nodes.literal_block,
+ nodes.doctest_block,
+ nodes.line_block,
+ nodes.table
+ )
+
+ def apply(self):
+ for node in self.document.traverse(nodes.block_quote):
+ if len(node.children) == 1 and isinstance(node.children[0], self.suppress_blockquote_child_nodes):
+ node.replace_self(node.children[0])
+
+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.body.append(self.starttag(node, 'table', CLASS='docutils'))
+
+ # <big>? Really?
+ def visit_desc_parameterlist(self, node):
+ self.body.append('(')
+ self.first_param = 1
+
+ def depart_desc_parameterlist(self, node):
+ self.body.append(')')
+
+ #
+ # 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('<span class="title">%s</span> ' % title)
+
+ def depart_versionmodified(self, node):
+ self.body.append("</div>\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()"""
+ try:
+ from sphinx.domains.std import option_desc_re # Sphinx >= 1.0
+ except ImportError:
+ from sphinx.directives.desc import option_desc_re # Sphinx < 1.0
+ 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..."))
+ try:
+ # Sphinx < 1.0
+ xrefs = self.env.reftargets.items()
+ templatebuiltins = dict([('ttags', [n for ((t,n),(l,a)) in xrefs
+ if t == 'ttag' and
+ l == 'ref/templates/builtins']),
+ ('tfilters', [n for ((t,n),(l,a)) in xrefs
+ if t == 'tfilter' and
+ l == 'ref/templates/builtins'])])
+ except AttributeError:
+ # Sphinx >= 1.0
+ xrefs = self.env.domaindata["std"]["objects"]
+ templatebuiltins = dict([('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
+ t == '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/parts/django/docs/_ext/literals_to_xrefs.py b/parts/django/docs/_ext/literals_to_xrefs.py
new file mode 100644
index 0000000..569193c
--- /dev/null
+++ b/parts/django/docs/_ext/literals_to_xrefs.py
@@ -0,0 +1,171 @@
+"""
+Runs through a reST file looking for old-style literals, and helps replace them
+with new-style references.
+"""
+
+import re
+import sys
+import shelve
+
+refre = re.compile(r'``([^`\s]+?)``')
+
+ROLES = (
+ 'attr',
+ 'class',
+ "djadmin",
+ 'data',
+ 'exc',
+ 'file',
+ 'func',
+ 'lookup',
+ 'meth',
+ 'mod' ,
+ "djadminopt",
+ "ref",
+ "setting",
+ "term",
+ "tfilter",
+ "ttag",
+
+ # special
+ "skip"
+)
+
+ALWAYS_SKIP = [
+ "NULL",
+ "True",
+ "False",
+]
+
+def fixliterals(fname):
+ data = open(fname).read()
+
+ last = 0
+ new = []
+ storage = shelve.open("/tmp/literals_to_xref.shelve")
+ lastvalues = storage.get("lastvalues", {})
+
+ for m in refre.finditer(data):
+
+ new.append(data[last:m.start()])
+ last = m.end()
+
+ line_start = data.rfind("\n", 0, m.start())
+ line_end = data.find("\n", m.end())
+ prev_start = data.rfind("\n", 0, line_start)
+ next_end = data.find("\n", line_end + 1)
+
+ # Skip always-skip stuff
+ if m.group(1) in ALWAYS_SKIP:
+ new.append(m.group(0))
+ continue
+
+ # skip when the next line is a title
+ next_line = data[m.end():next_end].strip()
+ if next_line[0] in "!-/:-@[-`{-~" and all(c == next_line[0] for c in next_line):
+ new.append(m.group(0))
+ continue
+
+ sys.stdout.write("\n"+"-"*80+"\n")
+ sys.stdout.write(data[prev_start+1:m.start()])
+ sys.stdout.write(colorize(m.group(0), fg="red"))
+ sys.stdout.write(data[m.end():next_end])
+ sys.stdout.write("\n\n")
+
+ replace_type = None
+ while replace_type is None:
+ replace_type = raw_input(
+ colorize("Replace role: ", fg="yellow")
+ ).strip().lower()
+ if replace_type and replace_type not in ROLES:
+ replace_type = None
+
+ if replace_type == "":
+ new.append(m.group(0))
+ continue
+
+ if replace_type == "skip":
+ new.append(m.group(0))
+ ALWAYS_SKIP.append(m.group(1))
+ continue
+
+ default = lastvalues.get(m.group(1), m.group(1))
+ if default.endswith("()") and replace_type in ("class", "func", "meth"):
+ default = default[:-2]
+ replace_value = raw_input(
+ colorize("Text <target> [", fg="yellow") + default + colorize("]: ", fg="yellow")
+ ).strip()
+ if not replace_value:
+ replace_value = default
+ new.append(":%s:`%s`" % (replace_type, replace_value))
+ lastvalues[m.group(1)] = replace_value
+
+ new.append(data[last:])
+ open(fname, "w").write("".join(new))
+
+ storage["lastvalues"] = lastvalues
+ storage.close()
+
+#
+# The following is taken from django.utils.termcolors and is copied here to
+# avoid the dependancy.
+#
+
+
+def colorize(text='', opts=(), **kwargs):
+ """
+ Returns your text, enclosed in ANSI graphics codes.
+
+ Depends on the keyword arguments 'fg' and 'bg', and the contents of
+ the opts tuple/list.
+
+ Returns the RESET code if no parameters are given.
+
+ Valid colors:
+ 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
+
+ Valid options:
+ 'bold'
+ 'underscore'
+ 'blink'
+ 'reverse'
+ 'conceal'
+ 'noreset' - string will not be auto-terminated with the RESET code
+
+ Examples:
+ colorize('hello', fg='red', bg='blue', opts=('blink',))
+ colorize()
+ colorize('goodbye', opts=('underscore',))
+ print colorize('first line', fg='red', opts=('noreset',))
+ print 'this should be red too'
+ print colorize('and so should this')
+ print 'this should not be red'
+ """
+ color_names = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
+ foreground = dict([(color_names[x], '3%s' % x) for x in range(8)])
+ background = dict([(color_names[x], '4%s' % x) for x in range(8)])
+
+ RESET = '0'
+ opt_dict = {'bold': '1', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'}
+
+ text = str(text)
+ code_list = []
+ if text == '' and len(opts) == 1 and opts[0] == 'reset':
+ return '\x1b[%sm' % RESET
+ for k, v in kwargs.iteritems():
+ if k == 'fg':
+ code_list.append(foreground[v])
+ elif k == 'bg':
+ code_list.append(background[v])
+ for o in opts:
+ if o in opt_dict:
+ code_list.append(opt_dict[o])
+ if 'noreset' not in opts:
+ text = text + '\x1b[%sm' % RESET
+ return ('\x1b[%sm' % ';'.join(code_list)) + text
+
+if __name__ == '__main__':
+ try:
+ fixliterals(sys.argv[1])
+ except (KeyboardInterrupt, SystemExit):
+ print \ No newline at end of file