summaryrefslogtreecommitdiff
path: root/doc/build/builder/viewsource.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/build/builder/viewsource.py')
-rw-r--r--doc/build/builder/viewsource.py209
1 files changed, 209 insertions, 0 deletions
diff --git a/doc/build/builder/viewsource.py b/doc/build/builder/viewsource.py
new file mode 100644
index 000000000..3f6b8263a
--- /dev/null
+++ b/doc/build/builder/viewsource.py
@@ -0,0 +1,209 @@
+from docutils import nodes
+from sphinx.ext.viewcode import collect_pages
+from sphinx.pycode import ModuleAnalyzer
+import imp
+from sphinx import addnodes
+import re
+from sphinx.util.compat import Directive
+import os
+from docutils.statemachine import StringList
+from sphinx.environment import NoUri
+
+import sys
+
+py2k = sys.version_info < (3, 0)
+if py2k:
+ text_type = unicode
+else:
+ text_type = str
+
+def view_source(name, rawtext, text, lineno, inliner,
+ options={}, content=[]):
+
+ env = inliner.document.settings.env
+
+ node = _view_source_node(env, text, None)
+ return [node], []
+
+def _view_source_node(env, text, state):
+ # pretend we're using viewcode fully,
+ # install the context it looks for
+ if not hasattr(env, '_viewcode_modules'):
+ env._viewcode_modules = {}
+
+ modname = text
+ text = modname.split(".")[-1] + ".py"
+
+ # imitate sphinx .<modname> syntax
+ if modname.startswith("."):
+ # see if the modname needs to be corrected in terms
+ # of current module context
+ base_module = env.temp_data.get('autodoc:module')
+ if base_module is None:
+ base_module = env.temp_data.get('py:module')
+
+ if base_module:
+ modname = base_module + modname
+
+ urito = env.app.builder.get_relative_uri
+
+ # we're showing code examples which may have dependencies
+ # which we really don't want to have required so load the
+ # module by file, not import (though we are importing)
+ # the top level module here...
+ pathname = None
+ for token in modname.split("."):
+ file_, pathname, desc = imp.find_module(token, [pathname] if pathname else None)
+ if file_:
+ file_.close()
+
+ # unlike viewcode which silently traps exceptions,
+ # I want this to totally barf if the file can't be loaded.
+ # a failed build better than a complete build missing
+ # key content
+ analyzer = ModuleAnalyzer.for_file(pathname, modname)
+ # copied from viewcode
+ analyzer.find_tags()
+ if not isinstance(analyzer.code, text_type):
+ code = analyzer.code.decode(analyzer.encoding)
+ else:
+ code = analyzer.code
+
+ if state is not None:
+ docstring = _find_mod_docstring(analyzer)
+ if docstring:
+ # get rid of "foo.py" at the top
+ docstring = re.sub(r"^[a-zA-Z_0-9]+\.py", "", docstring)
+
+ # strip
+ docstring = docstring.strip()
+
+ # yank only first paragraph
+ docstring = docstring.split("\n\n")[0].strip()
+ else:
+ docstring = None
+
+ entry = code, analyzer.tags, {}
+ env._viewcode_modules[modname] = entry
+ pagename = '_modules/' + modname.replace('.', '/')
+
+ try:
+ refuri = urito(env.docname, pagename)
+ except NoUri:
+ # if we're in the latex builder etc., this seems
+ # to be what we get
+ refuri = None
+
+
+ if docstring:
+ # embed the ref with the doc text so that it isn't
+ # a separate paragraph
+ if refuri:
+ docstring = "`%s <%s>`_ - %s" % (text, refuri, docstring)
+ else:
+ docstring = "``%s`` - %s" % (text, docstring)
+ para = nodes.paragraph('', '')
+ state.nested_parse(StringList([docstring]), 0, para)
+ return_node = para
+ else:
+ if refuri:
+ refnode = nodes.reference('', '',
+ nodes.Text(text, text),
+ refuri=urito(env.docname, pagename)
+ )
+ else:
+ refnode = nodes.Text(text, text)
+
+ if state:
+ return_node = nodes.paragraph('', '', refnode)
+ else:
+ return_node = refnode
+
+ return return_node
+
+from sphinx.pycode.pgen2 import token
+
+def _find_mod_docstring(analyzer):
+ """attempt to locate the module-level docstring.
+
+ Note that sphinx autodoc just uses ``__doc__``. But we don't want
+ to import the module, so we need to parse for it.
+
+ """
+ analyzer.tokenize()
+ for type_, parsed_line, start_pos, end_pos, raw_line in analyzer.tokens:
+ if type_ == token.COMMENT:
+ continue
+ elif type_ == token.STRING:
+ return eval(parsed_line)
+ else:
+ return None
+
+def _parse_content(content):
+ d = {}
+ d['text'] = []
+ idx = 0
+ for line in content:
+ idx += 1
+ m = re.match(r' *\:(.+?)\:(?: +(.+))?', line)
+ if m:
+ attrname, value = m.group(1, 2)
+ d[attrname] = value or ''
+ else:
+ break
+ d["text"] = content[idx:]
+ return d
+
+def _comma_list(text):
+ return re.split(r"\s*,\s*", text.strip())
+
+class AutoSourceDirective(Directive):
+ has_content = True
+
+ def run(self):
+ content = _parse_content(self.content)
+
+
+ env = self.state.document.settings.env
+ self.docname = env.docname
+
+ sourcefile = self.state.document.current_source.split(":")[0]
+ dir_ = os.path.dirname(sourcefile)
+ files = [
+ f for f in os.listdir(dir_) if f.endswith(".py")
+ and f != "__init__.py"
+ ]
+
+ if "files" in content:
+ # ordered listing of files to include
+ files = [fname for fname in _comma_list(content["files"])
+ if fname in set(files)]
+
+ node = nodes.paragraph('', '',
+ nodes.Text("Listing of files:", "Listing of files:")
+ )
+
+ bullets = nodes.bullet_list()
+ for fname in files:
+ modname, ext = os.path.splitext(fname)
+ # relative lookup
+ modname = "." + modname
+
+ link = _view_source_node(env, modname, self.state)
+
+ list_node = nodes.list_item('',
+ link
+ )
+ bullets += list_node
+
+ node += bullets
+
+ return [node]
+
+def setup(app):
+ app.add_role('viewsource', view_source)
+
+ app.add_directive('autosource', AutoSourceDirective)
+
+ # from sphinx.ext.viewcode
+ app.connect('html-collect-pages', collect_pages)