Commits

Kirill Simonov committed 39acc55

Moved Sphinx extensions used by HTSQL documentation into public packages.

Comments (0)

Files changed (12)

 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-sys.path.append(os.path.abspath('extensions'))
-extensions = ['sphinx.ext.autodoc', 'htsqldoc', 'diagram']
+extensions = ['sphinx.ext.autodoc', 'sphinxext_htsqldoc', 'sphinxext_texdiag']
 
 # The default URL of an HTSQL service.
 htsql_server = 'http://demo.htsql.org'
 autodoc_member_order = 'bysource'
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['templates']
+#templates_path = ['templates']
 
 # The suffix of source filenames.
 source_suffix = '.rst'
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['static']
+#html_static_path = ['static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.

doc/extensions/diagram.py

-
-
-from docutils import nodes
-from docutils.parsers.rst import Directive, directives
-from sphinx.util.osutil import ensuredir
-from subprocess import Popen, PIPE
-import os, os.path, tempfile, shutil
-
-
-class DiagramDirective(Directive):
-    required_arguments = 1
-    has_content = False
-    option_spec = {
-        'alt': directives.unchanged,
-        'align': lambda arg: directives.choice(arg, ('left', 'center', 'right')),
-    }
-
-    def run(self):
-        doc = self.state.document
-        env = doc.settings.env
-        docdir = os.path.dirname(env.doc2path(env.docname, base=None))
-        filename = self.arguments[0]
-        if filename.startswith('/'):
-            filename = os.path.normpath(filename[1:])
-        else:
-            filename = os.path.normpath(os.path.join(docdir, filename))
-        env.note_dependency(filename)
-        filename = os.path.join(env.srcdir, filename)
-        try:
-            name, data, width, height = render_diagram(env, filename)
-        except DiagramError, exc:
-            return [doc.reporter.error(str(exc))]
-        node = diagram()
-        node['name'] = name
-        node['data'] = data
-        node['width'] = width
-        node['height'] = height
-        node['alt'] = self.options.get('alt')
-        node['align'] = self.options.get('align')
-        return [node]
-
-
-class DiagramError(Exception):
-    pass
-
-
-class diagram(nodes.General, nodes.Element):
-    pass
-
-
-def render_diagram(env, filename):
-    directory = os.path.dirname(filename)
-    basename = os.path.basename(filename)
-    stem = os.path.splitext(basename)[0]
-    name = stem + '.png'
-    temp = tempfile.mkdtemp()
-    try:
-        texinputs = [directory]
-        for texdir in env.config.diagram_texinputs:
-            texdir = os.path.join(env.srcdir, texdir)
-            texinputs.append(texdir)
-        texinputs.append('')
-        texinputs = ':'.join(texinputs)
-        environ = os.environ.copy()
-        environ['TEXINPUTS'] = texinputs
-        cmdline = [env.config.diagram_pdflatex,
-                   '-halt-on-error',
-                   '-interaction', 'nonstopmode',
-                   '-output-directory', temp,
-                   basename]
-        execute(cmdline, env=environ)
-        cmdline = [env.config.diagram_pdftoppm,
-                   '-r', str(env.config.diagram_resolution),
-                   '-f', '1', '-l', '1',
-                   os.path.join(temp, stem)+'.pdf',
-                   os.path.join(temp, stem)]
-        execute(cmdline)
-        ppmfile = os.path.join(temp, stem)+'-1.ppm'
-        if not os.path.exists(ppmfile):
-            raise DiagramError("file not found: %s" % ppmfile)
-        data = open(ppmfile).read()
-        cmdline = [env.config.diagram_pnmcrop]
-        data = execute(cmdline, data)
-        line = data.splitlines()[1]
-        width, height = [int(chunk) for chunk in line.split()]
-        cmdline = [env.config.diagram_pnmtopng,
-                   '-transparent', 'white',
-                   '-compression', '9']
-        data = execute(cmdline, data)
-    finally:
-        shutil.rmtree(temp)
-    return name, data, width, height
-
-
-def execute(cmdline, input=None, env=None):
-    try:
-        process = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
-    except OSError, exc:
-        raise DiagramError("cannot start executable `%s`: %s"
-                           % (' '.join(cmdline), exc))
-    output, error = process.communicate(input)
-    if process.returncode != 0:
-        if not error:
-            error = output
-        raise DiagramError("`%s` exited with an error:\n%s"
-                           % (' '.join(cmdline), error))
-    return output
-
-
-def visit_diagram(self, node):
-    href = "%s/%s" % (self.builder.imgpath, node['name'])
-    filename = os.path.join(self.builder.outdir, '_images', node['name'])
-    ensuredir(os.path.dirname(filename))
-    open(filename, 'wb').write(node['data'])
-    if (isinstance(node.parent, nodes.TextElement) or
-        (isinstance(node.parent, nodes.reference) and
-         not isinstance(node.parent.parent, nodes.TextElement))):
-        suffix = ''
-    else:
-        suffix = '\n'
-    atts = {}
-    atts['src'] = href
-    atts['width'] = node['width']
-    atts['height'] = node['height']
-    if node['alt']:
-        atts['alt'] = node['alt']
-    atts['class'] = 'diagram'
-    if node['align']:
-        atts['class'] += ' align-%s' % node['align']
-    self.body.append(self.emptytag(node, 'img', suffix, **atts))
-
-
-def depart_diagram(self, node):
-    pass
-
-
-def setup(app):
-    app.add_config_value('diagram_pdflatex', 'pdflatex', 'env')
-    app.add_config_value('diagram_pdftoppm', 'pdftoppm', 'env')
-    app.add_config_value('diagram_pnmcrop', 'pnmcrop', 'env')
-    app.add_config_value('diagram_pnmtopng', 'pnmtopng', 'env')
-    app.add_config_value('diagram_texinputs', [], 'env')
-    app.add_config_value('diagram_resolution', 110, 'env')
-    app.add_directive('diagram', DiagramDirective)
-    app.add_node(diagram,
-                 html=(visit_diagram, depart_diagram))
-
-

doc/extensions/htsqldoc.py

-
-
-from docutils import nodes
-from docutils.parsers.rst import Directive, directives
-
-from urllib2 import quote, urlopen, Request, HTTPError, URLError
-from cgi import escape
-from json import loads
-
-
-class HTSQLServerDirective(Directive):
-    required_arguments = 1
-    has_content = False
-
-    def run(self):
-        env = self.state.document.settings.env
-        env.htsql_server = self.arguments[0]
-        return []
-
-
-class HTSQLDirective(Directive):
-    optional_arguments = 1
-    has_content = True
-    final_argument_whitespace = True
-    option_spec = {
-            'plain': directives.flag,
-            'error': directives.flag,
-            'query': directives.path,
-            'hide': directives.flag,
-            'cut': directives.positive_int,
-    }
-    htsql_safe = "~`!@$^&*()={[}]|:;\"'<,>?/"
-
-    def run(self):
-        doc = self.state.document
-        env = doc.settings.env
-        if self.arguments:
-            if self.content:
-                return [doc.reporter.error("directive cannot have both"
-                                           " content and an argument",
-                                           lineno=self.lineno)]
-            query_content  = " ".join(line.strip()
-                                      for line in self.arguments[0].split("\n"))
-            query = query_content
-        elif self.content:
-            query_content = "\n".join(self.content).strip()
-            query =  " ".join(line.strip() for line in self.content)
-        else:
-            return [doc.reporter.error("directive must have either content"
-                                       " or an argument", lineno=self.lineno)]
-        query_node = htsql_block(query_content, query_content)
-        query_node['language'] = 'htsql'
-        if not hasattr(env, 'htsql_server') or not env.htsql_server:
-            return [doc.reporter.error("config option `htsql_server`"
-                                       " is not set", lineno=self.lineno)]
-        if 'query' not in self.options:
-            query = quote(query.encode('utf-8'), safe=self.htsql_safe)
-        else:
-            query = self.options['query'].encode('utf-8')
-        uri = env.htsql_server+query
-        query_node['uri'] = uri
-        query_node['hide'] = ('hide' in self.options)
-        if not hasattr(env, 'htsql_uris'):
-            env.htsql_uris = {}
-        if uri not in env.htsql_uris:
-            result = load_uri(uri, 'error' in self.options)
-            if not result:
-                return [doc.reporter.error("failed to load: %s" % uri,
-                                           line=self.lineno)]
-            env.htsql_uris[uri] = result
-        content_type, content = env.htsql_uris[uri]
-        if 'plain' in self.options:
-            content_type = 'text/plain'
-        result_node = build_result(self.content_offset, content_type, content,
-                                   self.options.get('cut'))
-        query_container = nodes.container('', query_node,
-                                          classes=['htsql-input'])
-        result_container = nodes.container('', result_node,
-                                           classes=['htsql-output'])
-        if 'hide' in self.options:
-            result_container['classes'].append('htsql-hide')
-        return [query_container, result_container]
-
-
-class VSplitDirective(Directive):
-
-    has_content = True
-
-    def run(self):
-        self.assert_has_content()
-        text = '\n'.join(self.content)
-        node = nodes.container(text, classes=['vsplit'])
-        self.state.nested_parse(self.content, self.content_offset, node)
-        if len(node) != 2:
-            raise self.error("%s directive expects 2 subnodes", self.name)
-        node[0]['classes'].append('vsplit-left')
-        node[1]['classes'].append('vsplit-right')
-        node += nodes.container(classes=['vsplit-clear'])
-        return [node]
-
-
-def purge_htsql_server(app, env, docname):
-    if hasattr(env, 'htsql_server'):
-        del env.htsql_server
-    if env.config.htsql_server:
-        env.htsql_server = env.config.htsql_server
-
-
-class htsql_block(nodes.literal_block):
-    pass
-
-
-def visit_htsql_block(self, node):
-    # Adapted from `visit_literal_block()`
-    if node.rawsource != node.astext():
-        return self.visit_literal_block(self, node)
-    lang = self.highlightlang
-    linenos = node.rawsource.count('\n') >= \
-              self.highlightlinenothreshold - 1
-    if node.has_key('language'):
-        lang = node['language']
-    if node.has_key('linenos'):
-        linenos = node['linenos']
-    def warner(msg):
-        self.builder.warn(msg, (self.builder.current_docname, node.line))
-    self.highlighter.fmter[False].nowrap = True
-    self.highlighter.fmter[True].nowrap = True
-    highlighted = self.highlighter.highlight_block(
-        node.rawsource, lang, linenos, warn=warner)
-    self.highlighter.fmter[False].nowrap = False
-    self.highlighter.fmter[True].nowrap = False
-    if highlighted.startswith('<pre>') and highlighted.endswith('</pre>\n'):
-        # may happen if the language is not detected correctly
-        highlighted = highlighted[5:-7]
-    if node.has_key('uri'):
-        highlighted = '<a href="%s" target="_new" class="htsql-link">%s</a>' \
-                % (escape(node['uri'], True), highlighted)
-    toggle = "[-]"
-    if node.has_key('hide') and node['hide']:
-        toggle = "[+]"
-    highlighted = '<span class="htsql-toggle">%s</span>%s' \
-            % (toggle, highlighted)
-    highlighted = '<pre>%s</pre>' % highlighted
-    highlighted = '<div class="highlight">%s</div>' % highlighted
-    starttag = self.starttag(node, 'div', suffix='',
-                             CLASS='highlight-%s' % lang)
-    self.body.append(starttag + highlighted + '</div>\n')
-    raise nodes.SkipNode
-
-
-def depart_htsql_block(self, node):
-    self.depart_literal_block(node)
-
-
-def load_uri(uri, error=False):
-    try:
-        headers = { 'Accept': 'application/json' }
-        request = Request(uri, headers=headers)
-        response = urlopen(request)
-        content_type = response.info().gettype()
-        content = response.read()
-    except HTTPError, response:
-        if not error:
-            return None
-        content_type = response.headers.gettype()
-        content = response.read()
-    except URLError:
-        return None
-    return (content_type, content)
-
-
-def build_result(line, content_type, content, cut=None):
-    if content_type == 'application/json':
-        data = loads(content)
-        if isinstance(data, dict):
-            data = [[meta['title'] for meta in data['meta']]] + data['data']
-        is_cut = False
-        if cut and len(data) > cut+1:
-            data = data[:cut+1]
-            is_cut = True
-        size = len(data[0])
-        widths = [1]*size
-        for row in data:
-            for idx, value in enumerate(row):
-                widths[idx] = max(widths[idx], len(unicode(value)))
-        table_node = nodes.table()
-        group_node = nodes.tgroup(cols=size)
-        table_node += group_node
-        for width in widths:
-            colspec_node = nodes.colspec(colwidth=width)
-            group_node += colspec_node
-        head_node = nodes.thead()
-        group_node += head_node
-        row_node = nodes.row()
-        head_node += row_node
-        for title in data[0]:
-            entry_node = nodes.entry()
-            row_node += entry_node
-            para_node = nodes.paragraph()
-            entry_node += para_node
-            text_node = nodes.Text(title)
-            para_node += text_node
-        body_node = nodes.tbody()
-        group_node += body_node
-        for row in data[1:]:
-            row_node = nodes.row()
-            body_node += row_node
-            for value in row:
-                entry_node = nodes.entry()
-                row_node += entry_node
-                para_node = nodes.paragraph()
-                entry_node += para_node
-                if value is None:
-                    text_node = nodes.Text(u"\u2014")
-                elif value is True:
-                    text_node = nodes.emphasis()
-                    text_node += nodes.Text(u"true")
-                elif value is False:
-                    text_node = nodes.emphasis()
-                    text_node += nodes.Text(u"false")
-                else:
-                    text_node = nodes.Text(unicode(value))
-                para_node += text_node
-        if is_cut:
-            row_node = nodes.row(classes=['htsql-cut'])
-            body_node += row_node
-            for idx in range(size):
-                entry_node = nodes.entry()
-                row_node += entry_node
-                para_node = nodes.paragraph()
-                entry_node += para_node
-                text_node = nodes.Text(u"\u2026")
-                para_node += text_node
-        result_node = table_node
-    else:
-        content = content.decode('utf-8', 'replace')
-        if cut and content.count('\n') > cut:
-            start = 0
-            while cut:
-                start = content.find('\n', start)+1
-                cut -= 1
-            content = content[:start]+u"\u2026\n"
-        result_node = nodes.literal_block(content, content)
-        result_node['language'] = 'text'
-    return result_node
-
-
-def setup(app):
-    app.add_config_value('htsql_server', None, 'env')
-    app.add_directive('htsql-server', HTSQLServerDirective)
-    app.add_directive('htsql', HTSQLDirective)
-    app.add_directive('vsplit', VSplitDirective)
-    app.connect('env-purge-doc', purge_htsql_server)
-    app.add_node(htsql_block,
-                 html=(visit_htsql_block, depart_htsql_block))
-    app.add_stylesheet('htsqldoc.css')
-    app.add_javascript('htsqldoc.js')
-
-

doc/static/htsqldoc.css

-
-.vsplit-left {
-  width: 45%;
-  float: left;
-  font-size: 95%;
-}
-
-.vsplit-right {
-  width: 50%;
-  float: right;
-  font-size: 95%;
-}
-
-.vsplit-clear {
-  clear: both;
-}
-
-div.htsql-input {
-  font-size: 95%;
-}
-
-div.htsql-output {
-  max-height: 11em;
-  overflow: auto;
-}
-
-div.htsql-output table.docutils tr.htsql-cut td {
-  border-bottom: 0;
-}
-
-div.htsql-hide {
-  display: none;
-}
-
-span.htsql-toggle {
-  float: right;
-  cursor: pointer;
-}
-
-a.htsql-link {
-  color: inherit;
-  background-color: inherit;
-  text-decoration: none;
-}
-
-a.htsql-link:hover {
-  color: inherit;
-  background-color: inherit;
-  text-decoration: none;
-  opacity: 0.7;
-}
-

doc/static/htsqldoc.js

-
-
-$(function () {
-    $('.htsql-toggle').click(function () {
-        var output = this;
-        if ($(this).text() == '[+]') {
-            $(this).parents('.htsql-input').next('.htsql-output').slideDown('fast');
-            $(this).text('[-]');
-        }
-        else {
-            $(this).parents('.htsql-input').next('.htsql-output').slideUp('fast');
-            $(this).text('[+]');
-        }
-    });
-});
-
-

doc/templates/layout.html

-{% extends "!layout.html" %}
 PACKAGES = find_packages('src')
 PACKAGE_DIR = {'': 'src'}
 INCLUDE_PACKAGE_DATA = True
+ZIP_SAFE = False
 ENTRY_POINTS = {
     'console_scripts': ['htsql-ctl = htsql.ctl:main'],
     'htsql.addons': [
       packages=PACKAGES,
       package_dir=PACKAGE_DIR,
       include_package_data=INCLUDE_PACKAGE_DATA,
+      zip_safe=ZIP_SAFE,
       entry_points=ENTRY_POINTS,
       install_requires=INSTALL_REQUIRES,
 )

src/htsql/__init__.py

 
 :copyright: 2006-2011, Prometheus Research, LLC
 :authors: Clark C. Evans <cce@clarkevans.com>,
-          Kirill Simonov <xi@resolvent.net>
+          Kirill Simonov <xi@resolvent.net>;
+          see ``AUTHORS`` file in the source distribution
+          for the full list of contributors
 :license: See ``LICENSE`` file in the source distribution
 
 This package provides HTSQL, a query language for the accidental programmer.

src/sphinxext_htsqldoc/__init__.py

+
+
+from docutils import nodes
+from docutils.parsers.rst import Directive, directives
+from sphinx.util.osutil import copyfile
+
+import os, os.path
+from urllib2 import quote, urlopen, Request, HTTPError, URLError
+from cgi import escape
+from json import loads
+
+
+class HTSQLServerDirective(Directive):
+    required_arguments = 1
+    has_content = False
+
+    def run(self):
+        env = self.state.document.settings.env
+        env.htsql_server = self.arguments[0]
+        return []
+
+
+class HTSQLDirective(Directive):
+    optional_arguments = 1
+    has_content = True
+    final_argument_whitespace = True
+    option_spec = {
+            'plain': directives.flag,
+            'error': directives.flag,
+            'query': directives.path,
+            'hide': directives.flag,
+            'cut': directives.positive_int,
+    }
+    htsql_safe = "~`!@$^&*()={[}]|:;\"'<,>?/"
+
+    def run(self):
+        doc = self.state.document
+        env = doc.settings.env
+        if self.arguments:
+            if self.content:
+                return [doc.reporter.error("directive cannot have both"
+                                           " content and an argument",
+                                           lineno=self.lineno)]
+            query_content  = " ".join(line.strip()
+                                      for line in self.arguments[0].split("\n"))
+            query = query_content
+        elif self.content:
+            query_content = "\n".join(self.content).strip()
+            query =  " ".join(line.strip() for line in self.content)
+        else:
+            return [doc.reporter.error("directive must have either content"
+                                       " or an argument", lineno=self.lineno)]
+        query_node = htsql_block(query_content, query_content)
+        query_node['language'] = 'htsql'
+        if not hasattr(env, 'htsql_server') or not env.htsql_server:
+            return [doc.reporter.error("config option `htsql_server`"
+                                       " is not set", lineno=self.lineno)]
+        if 'query' not in self.options:
+            query = quote(query.encode('utf-8'), safe=self.htsql_safe)
+        else:
+            query = self.options['query'].encode('utf-8')
+        uri = env.htsql_server+query
+        query_node['uri'] = uri
+        query_node['hide'] = ('hide' in self.options)
+        if not hasattr(env, 'htsql_uris'):
+            env.htsql_uris = {}
+        if uri not in env.htsql_uris:
+            result = load_uri(uri, 'error' in self.options)
+            if not result:
+                return [doc.reporter.error("failed to load: %s" % uri,
+                                           line=self.lineno)]
+            env.htsql_uris[uri] = result
+        content_type, content = env.htsql_uris[uri]
+        if 'plain' in self.options:
+            content_type = 'text/plain'
+        result_node = build_result(self.content_offset, content_type, content,
+                                   self.options.get('cut'))
+        query_container = nodes.container('', query_node,
+                                          classes=['htsql-input'])
+        result_container = nodes.container('', result_node,
+                                           classes=['htsql-output'])
+        if 'hide' in self.options:
+            result_container['classes'].append('htsql-hide')
+        return [query_container, result_container]
+
+
+class VSplitDirective(Directive):
+
+    has_content = True
+
+    def run(self):
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+        node = nodes.container(text, classes=['vsplit'])
+        self.state.nested_parse(self.content, self.content_offset, node)
+        if len(node) != 2:
+            raise self.error("%s directive expects 2 subnodes", self.name)
+        node[0]['classes'].append('vsplit-left')
+        node[1]['classes'].append('vsplit-right')
+        node += nodes.container(classes=['vsplit-clear'])
+        return [node]
+
+
+def purge_htsql_server(app, env, docname):
+    if hasattr(env, 'htsql_server'):
+        del env.htsql_server
+    if env.config.htsql_server:
+        env.htsql_server = env.config.htsql_server
+
+
+class htsql_block(nodes.literal_block):
+    pass
+
+
+def visit_htsql_block(self, node):
+    # Adapted from `visit_literal_block()`
+    if node.rawsource != node.astext():
+        return self.visit_literal_block(self, node)
+    lang = self.highlightlang
+    linenos = node.rawsource.count('\n') >= \
+              self.highlightlinenothreshold - 1
+    if node.has_key('language'):
+        lang = node['language']
+    if node.has_key('linenos'):
+        linenos = node['linenos']
+    def warner(msg):
+        self.builder.warn(msg, (self.builder.current_docname, node.line))
+    self.highlighter.fmter[False].nowrap = True
+    self.highlighter.fmter[True].nowrap = True
+    highlighted = self.highlighter.highlight_block(
+        node.rawsource, lang, linenos, warn=warner)
+    self.highlighter.fmter[False].nowrap = False
+    self.highlighter.fmter[True].nowrap = False
+    if highlighted.startswith('<pre>') and highlighted.endswith('</pre>\n'):
+        # may happen if the language is not detected correctly
+        highlighted = highlighted[5:-7]
+    if node.has_key('uri'):
+        highlighted = '<a href="%s" target="_new" class="htsql-link">%s</a>' \
+                % (escape(node['uri'], True), highlighted)
+    toggle = "[-]"
+    if node.has_key('hide') and node['hide']:
+        toggle = "[+]"
+    highlighted = '<span class="htsql-toggle">%s</span>%s' \
+            % (toggle, highlighted)
+    highlighted = '<pre>%s</pre>' % highlighted
+    highlighted = '<div class="highlight">%s</div>' % highlighted
+    starttag = self.starttag(node, 'div', suffix='',
+                             CLASS='highlight-%s' % lang)
+    self.body.append(starttag + highlighted + '</div>\n')
+    raise nodes.SkipNode
+
+
+def depart_htsql_block(self, node):
+    self.depart_literal_block(node)
+
+
+def load_uri(uri, error=False):
+    try:
+        headers = { 'Accept': 'application/json' }
+        request = Request(uri, headers=headers)
+        response = urlopen(request)
+        content_type = response.info().gettype()
+        content = response.read()
+    except HTTPError, response:
+        if not error:
+            return None
+        content_type = response.headers.gettype()
+        content = response.read()
+    except URLError:
+        return None
+    return (content_type, content)
+
+
+def build_result(line, content_type, content, cut=None):
+    if content_type == 'application/json':
+        data = loads(content)
+        if isinstance(data, dict):
+            data = [[meta['title'] for meta in data['meta']]] + data['data']
+        is_cut = False
+        if cut and len(data) > cut+1:
+            data = data[:cut+1]
+            is_cut = True
+        size = len(data[0])
+        widths = [1]*size
+        for row in data:
+            for idx, value in enumerate(row):
+                widths[idx] = max(widths[idx], len(unicode(value)))
+        table_node = nodes.table()
+        group_node = nodes.tgroup(cols=size)
+        table_node += group_node
+        for width in widths:
+            colspec_node = nodes.colspec(colwidth=width)
+            group_node += colspec_node
+        head_node = nodes.thead()
+        group_node += head_node
+        row_node = nodes.row()
+        head_node += row_node
+        for title in data[0]:
+            entry_node = nodes.entry()
+            row_node += entry_node
+            para_node = nodes.paragraph()
+            entry_node += para_node
+            text_node = nodes.Text(title)
+            para_node += text_node
+        body_node = nodes.tbody()
+        group_node += body_node
+        for row in data[1:]:
+            row_node = nodes.row()
+            body_node += row_node
+            for value in row:
+                entry_node = nodes.entry()
+                row_node += entry_node
+                para_node = nodes.paragraph()
+                entry_node += para_node
+                if value is None:
+                    text_node = nodes.Text(u"\u2014")
+                elif value is True:
+                    text_node = nodes.emphasis()
+                    text_node += nodes.Text(u"true")
+                elif value is False:
+                    text_node = nodes.emphasis()
+                    text_node += nodes.Text(u"false")
+                else:
+                    text_node = nodes.Text(unicode(value))
+                para_node += text_node
+        if is_cut:
+            row_node = nodes.row(classes=['htsql-cut'])
+            body_node += row_node
+            for idx in range(size):
+                entry_node = nodes.entry()
+                row_node += entry_node
+                para_node = nodes.paragraph()
+                entry_node += para_node
+                text_node = nodes.Text(u"\u2026")
+                para_node += text_node
+        result_node = table_node
+    else:
+        content = content.decode('utf-8', 'replace')
+        if cut and content.count('\n') > cut:
+            start = 0
+            while cut:
+                start = content.find('\n', start)+1
+                cut -= 1
+            content = content[:start]+u"\u2026\n"
+        result_node = nodes.literal_block(content, content)
+        result_node['language'] = 'text'
+    return result_node
+
+
+def copy_static(app, exception):
+    if app.builder.name != 'html' or exception:
+        return
+    src_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static')
+    dst_dir = os.path.join(app.builder.outdir, '_static')
+    for filename in os.listdir(src_dir):
+        src = os.path.join(src_dir, filename)
+        if not os.path.isfile(src):
+            continue
+        dst = os.path.join(dst_dir, filename)
+        copyfile(src, dst)
+
+
+def setup(app):
+    app.add_config_value('htsql_server', None, 'env')
+    app.add_directive('htsql-server', HTSQLServerDirective)
+    app.add_directive('htsql', HTSQLDirective)
+    app.add_directive('vsplit', VSplitDirective)
+    app.connect('env-purge-doc', purge_htsql_server)
+    app.connect('build-finished', copy_static)
+    app.add_node(htsql_block,
+                 html=(visit_htsql_block, depart_htsql_block))
+    app.add_stylesheet('htsqldoc.css')
+    app.add_javascript('htsqldoc.js')
+
+

src/sphinxext_htsqldoc/static/htsqldoc.css

+
+.vsplit-left {
+  width: 45%;
+  float: left;
+  font-size: 95%;
+}
+
+.vsplit-right {
+  width: 50%;
+  float: right;
+  font-size: 95%;
+}
+
+.vsplit-clear {
+  clear: both;
+}
+
+div.htsql-input {
+  font-size: 95%;
+}
+
+div.htsql-output {
+  max-height: 11em;
+  overflow: auto;
+}
+
+div.htsql-output table.docutils tr.htsql-cut td {
+  border-bottom: 0;
+}
+
+div.htsql-hide {
+  display: none;
+}
+
+span.htsql-toggle {
+  float: right;
+  cursor: pointer;
+}
+
+a.htsql-link {
+  color: inherit;
+  background-color: inherit;
+  text-decoration: none;
+}
+
+a.htsql-link:hover {
+  color: inherit;
+  background-color: inherit;
+  text-decoration: none;
+  opacity: 0.7;
+}
+

src/sphinxext_htsqldoc/static/htsqldoc.js

+
+
+$(function () {
+    $('.htsql-toggle').click(function () {
+        var output = this;
+        if ($(this).text() == '[+]') {
+            $(this).parents('.htsql-input').next('.htsql-output').slideDown('fast');
+            $(this).text('[-]');
+        }
+        else {
+            $(this).parents('.htsql-input').next('.htsql-output').slideUp('fast');
+            $(this).text('[+]');
+        }
+    });
+});
+
+

src/sphinxext_texdiag/__init__.py

+
+
+from docutils import nodes
+from docutils.parsers.rst import Directive, directives
+from sphinx.util.osutil import ensuredir
+from subprocess import Popen, PIPE
+import os, os.path, tempfile, shutil
+
+
+class DiagramDirective(Directive):
+    required_arguments = 1
+    has_content = False
+    option_spec = {
+        'alt': directives.unchanged,
+        'align': lambda arg: directives.choice(arg, ('left', 'center', 'right')),
+    }
+
+    def run(self):
+        doc = self.state.document
+        env = doc.settings.env
+        docdir = os.path.dirname(env.doc2path(env.docname, base=None))
+        filename = self.arguments[0]
+        if filename.startswith('/'):
+            filename = os.path.normpath(filename[1:])
+        else:
+            filename = os.path.normpath(os.path.join(docdir, filename))
+        env.note_dependency(filename)
+        filename = os.path.join(env.srcdir, filename)
+        try:
+            name, data, width, height = render_diagram(env, filename)
+        except DiagramError, exc:
+            return [doc.reporter.error(str(exc))]
+        node = diagram()
+        node['name'] = name
+        node['data'] = data
+        node['width'] = width
+        node['height'] = height
+        node['alt'] = self.options.get('alt')
+        node['align'] = self.options.get('align')
+        return [node]
+
+
+class DiagramError(Exception):
+    pass
+
+
+class diagram(nodes.General, nodes.Element):
+    pass
+
+
+def render_diagram(env, filename):
+    directory = os.path.dirname(filename)
+    basename = os.path.basename(filename)
+    stem = os.path.splitext(basename)[0]
+    name = stem + '.png'
+    temp = tempfile.mkdtemp()
+    try:
+        texinputs = [directory]
+        for texdir in env.config.diagram_texinputs:
+            texdir = os.path.join(env.srcdir, texdir)
+            texinputs.append(texdir)
+        texinputs.append('')
+        texinputs = ':'.join(texinputs)
+        environ = os.environ.copy()
+        environ['TEXINPUTS'] = texinputs
+        cmdline = [env.config.diagram_pdflatex,
+                   '-halt-on-error',
+                   '-interaction', 'nonstopmode',
+                   '-output-directory', temp,
+                   basename]
+        execute(cmdline, env=environ)
+        cmdline = [env.config.diagram_pdftoppm,
+                   '-r', str(env.config.diagram_resolution),
+                   '-f', '1', '-l', '1',
+                   os.path.join(temp, stem)+'.pdf',
+                   os.path.join(temp, stem)]
+        execute(cmdline)
+        ppmfile = os.path.join(temp, stem)+'-1.ppm'
+        if not os.path.exists(ppmfile):
+            raise DiagramError("file not found: %s" % ppmfile)
+        data = open(ppmfile).read()
+        cmdline = [env.config.diagram_pnmcrop]
+        data = execute(cmdline, data)
+        line = data.splitlines()[1]
+        width, height = [int(chunk) for chunk in line.split()]
+        cmdline = [env.config.diagram_pnmtopng,
+                   '-transparent', 'white',
+                   '-compression', '9']
+        data = execute(cmdline, data)
+    finally:
+        shutil.rmtree(temp)
+    return name, data, width, height
+
+
+def execute(cmdline, input=None, env=None):
+    try:
+        process = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
+    except OSError, exc:
+        raise DiagramError("cannot start executable `%s`: %s"
+                           % (' '.join(cmdline), exc))
+    output, error = process.communicate(input)
+    if process.returncode != 0:
+        if not error:
+            error = output
+        raise DiagramError("`%s` exited with an error:\n%s"
+                           % (' '.join(cmdline), error))
+    return output
+
+
+def visit_diagram(self, node):
+    href = "%s/%s" % (self.builder.imgpath, node['name'])
+    filename = os.path.join(self.builder.outdir, '_images', node['name'])
+    ensuredir(os.path.dirname(filename))
+    open(filename, 'wb').write(node['data'])
+    if (isinstance(node.parent, nodes.TextElement) or
+        (isinstance(node.parent, nodes.reference) and
+         not isinstance(node.parent.parent, nodes.TextElement))):
+        suffix = ''
+    else:
+        suffix = '\n'
+    atts = {}
+    atts['src'] = href
+    atts['width'] = node['width']
+    atts['height'] = node['height']
+    if node['alt']:
+        atts['alt'] = node['alt']
+    atts['class'] = 'diagram'
+    if node['align']:
+        atts['class'] += ' align-%s' % node['align']
+    self.body.append(self.emptytag(node, 'img', suffix, **atts))
+
+
+def depart_diagram(self, node):
+    pass
+
+
+def setup(app):
+    app.add_config_value('diagram_pdflatex', 'pdflatex', 'env')
+    app.add_config_value('diagram_pdftoppm', 'pdftoppm', 'env')
+    app.add_config_value('diagram_pnmcrop', 'pnmcrop', 'env')
+    app.add_config_value('diagram_pnmtopng', 'pnmtopng', 'env')
+    app.add_config_value('diagram_texinputs', [], 'env')
+    app.add_config_value('diagram_resolution', 110, 'env')
+    app.add_directive('diagram', DiagramDirective)
+    app.add_node(diagram,
+                 html=(visit_diagram, depart_diagram))
+
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.