[svn] add first part of jinja documentation
--HG--
branch : trunk
diff --git a/docs/generate.py b/docs/generate.py
new file mode 100755
index 0000000..0c155c0
--- /dev/null
+++ b/docs/generate.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Generate Jinja Documentation
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Generates a bunch of html files containing the documentation.
+
+ :copyright: 2006-2007 by Armin Ronacher, Georg Brandl.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
+import re
+import inspect
+from datetime import datetime
+from cgi import escape
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+from docutils.core import publish_parts
+from docutils.writers import html4css1
+
+from jinja import Environment
+
+from pygments import highlight
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import HtmlFormatter
+
+def generate_list_of_filters():
+ from jinja.filters import FILTERS
+ result = []
+
+ filters = {}
+ for name, f in FILTERS.iteritems():
+ if not f in filters:
+ filters[f] = ([name], inspect.getdoc(f))
+ else:
+ filters[f][0].append(name)
+ for names, _ in filters.itervalues():
+ names.sort(key=lambda x: -len(x))
+
+ for names, doc in sorted(filters.values(), key=lambda x: x[0][0].lower()):
+ name = names[0]
+ if len(names) > 1:
+ aliases = '\n\n :Aliases: %s\n' % ', '.join(names[1:])
+ else:
+ aliases = ''
+
+ doclines = []
+ for line in doc.splitlines():
+ doclines.append(' ' + line)
+ doc = '\n'.join(doclines)
+ result.append('`%s`\n%s%s' % (name, doc, aliases))
+
+ return '\n'.join(result)
+
+def generate_list_of_tests():
+ from jinja.tests import TESTS
+ result = []
+
+ tests = {}
+ for name, f in TESTS.iteritems():
+ if not f in tests:
+ tests[f] = ([name], inspect.getdoc(f))
+ else:
+ tests[f][0].append(name)
+ for names, _ in tests.itervalues():
+ names.sort(key=lambda x: -len(x))
+
+ for names, doc in sorted(tests.values(), key=lambda x: x[0][0].lower()):
+ name = names[0]
+ if len(names) > 1:
+ aliases = '\n\n :Aliases: %s\n' % ', '.join(names[1:])
+ else:
+ aliases = ''
+
+ doclines = []
+ for line in doc.splitlines():
+ doclines.append(' ' + line)
+ doc = '\n'.join(doclines)
+ result.append('`%s`\n%s%s' % (name, doc, aliases))
+
+ return '\n'.join(result)
+
+def generate_list_of_loaders():
+ from jinja import loaders as loader_module
+
+ result = []
+ loaders = []
+ for item in loader_module.__all__:
+ loaders.append(getattr(loader_module, item))
+ loaders.sort(key=lambda x: x.__name__.lower())
+
+ for loader in loaders:
+ doclines = []
+ for line in inspect.getdoc(loader).splitlines():
+ doclines.append(' ' + line)
+ result.append('`%s`\n%s' % (loader.__name__, '\n'.join(doclines)))
+
+ return '\n\n'.join(result)
+
+e = Environment()
+
+PYGMENTS_FORMATTER = HtmlFormatter(style='pastie', cssclass='syntax')
+
+LIST_OF_FILTERS = generate_list_of_filters()
+LIST_OF_TESTS = generate_list_of_tests()
+LIST_OF_LOADERS = generate_list_of_loaders()
+
+TEMPLATE = e.from_string('''\
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title>{{ title }} — Jinja Documentation</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ <style type="text/css">
+ {{ style|e }}
+ </style>
+</head>
+<body>
+ <div id="content">
+ {% if file_id == 'index' %}
+ <div id="jinjalogo"></div>
+ <h2 class="subheading plain">{{ title }}</h2>
+ {% else %}
+ <h1 class="heading"><span>Jinja</span></h1>
+ <h2 class="subheading">{{ title }}</h2>
+ {% endif %}
+ {% if file_id != 'index' or toc %}
+ <div id="toc">
+ <h2>Navigation</h2>
+ <ul>
+ <li><a href="index.html">back to index</a></li>
+ </ul>
+ {% if toc %}
+ <h2>Contents</h2>
+ <ul class="contents">
+ {% for key, value in toc %}
+ <li><a href="{{ key }}">{{ value }}</a></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </div>
+ {% endif %}
+ <div id="contentwrapper">
+ {{ body }}
+ </div>
+ </div>
+</body>
+<!-- generated on: {{ generation_date }}
+ file id: {{ file_id }} -->
+</html>\
+''')
+
+def pygments_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ try:
+ lexer = get_lexer_by_name(arguments[0])
+ except ValueError:
+ # no lexer found
+ lexer = get_lexer_by_name('text')
+ parsed = highlight(u'\n'.join(content), lexer, PYGMENTS_FORMATTER)
+ return [nodes.raw('', parsed, format="html")]
+pygments_directive.arguments = (1, 0, 1)
+pygments_directive.content = 1
+directives.register_directive('sourcecode', pygments_directive)
+
+
+def create_translator(link_style):
+ class Translator(html4css1.HTMLTranslator):
+ def visit_reference(self, node):
+ refuri = node.get('refuri')
+ if refuri is not None and '/' not in refuri and refuri.endswith('.txt'):
+ node['refuri'] = link_style(refuri[:-4])
+ html4css1.HTMLTranslator.visit_reference(self, node)
+ return Translator
+
+
+class DocumentationWriter(html4css1.Writer):
+
+ def __init__(self, link_style):
+ html4css1.Writer.__init__(self)
+ self.translator_class = create_translator(link_style)
+
+ def translate(self):
+ html4css1.Writer.translate(self)
+ # generate table of contents
+ contents = self.build_contents(self.document)
+ contents_doc = self.document.copy()
+ contents_doc.children = contents
+ contents_visitor = self.translator_class(contents_doc)
+ contents_doc.walkabout(contents_visitor)
+ self.parts['toc'] = self._generated_toc
+
+ def build_contents(self, node, level=0):
+ sections = []
+ i = len(node) - 1
+ while i >= 0 and isinstance(node[i], nodes.section):
+ sections.append(node[i])
+ i -= 1
+ sections.reverse()
+ toc = []
+ for section in sections:
+ try:
+ reference = nodes.reference('', '', refid=section['ids'][0], *section[0])
+ except IndexError:
+ continue
+ ref_id = reference['refid']
+ text = escape(reference.astext().encode('utf-8'))
+ toc.append((ref_id, text))
+
+ self._generated_toc = [('#%s' % href, caption) for href, caption in toc]
+ # no further processing
+ return []
+
+
+def generate_documentation(data, link_style):
+ writer = DocumentationWriter(link_style)
+ data = data.replace('[[list_of_filters]]', LIST_OF_FILTERS)\
+ .replace('[[list_of_tests]]', LIST_OF_TESTS)\
+ .replace('[[list_of_loaders]]', LIST_OF_LOADERS)
+ parts = publish_parts(
+ data,
+ writer=writer,
+ settings_overrides={
+ 'initial_header_level': 3,
+ 'field_name_limit': 50,
+ }
+ )
+ return {
+ 'title': parts['title'].encode('utf-8'),
+ 'body': parts['body'].encode('utf-8'),
+ 'toc': parts['toc']
+ }
+
+
+def handle_file(filename, fp, dst):
+ now = datetime.now()
+ title = os.path.basename(filename)[:-4]
+ content = fp.read()
+ parts = generate_documentation(content, (lambda x: './%s.html' % x))
+ result = file(os.path.join(dst, title + '.html'), 'w')
+ c = dict(parts)
+ c['style'] = PYGMENTS_FORMATTER.get_style_defs('.syntax')
+ c['generation_date'] = now
+ c['file_id'] = title
+ result.write(TEMPLATE.render(c).encode('utf-8'))
+ result.close()
+
+
+def run(dst, sources=()):
+ path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
+ if not sources:
+ sources = [os.path.join(path, fn) for fn in os.listdir(path)]
+ for fn in sources:
+ if not os.path.isfile(fn):
+ continue
+ print 'Processing %s' % fn
+ f = open(fn)
+ try:
+ handle_file(fn, f, dst)
+ finally:
+ f.close()
+
+
+def main(dst='build/', *sources):
+ return run(os.path.realpath(dst), sources)
+
+
+if __name__ == '__main__':
+ main(*sys.argv[1:])