|
@@ -0,0 +1,123 @@ |
|
|
|
|
|
# -*- coding: utf-8 -*- |
|
|
|
|
|
""" |
|
|
|
|
|
Sphinx plugin. |
|
|
|
|
|
|
|
|
|
|
|
This plugin lets you easily include sphinx-generated documentation as part |
|
|
|
|
|
of your Hyde site. |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
from __future__ import absolute_import |
|
|
|
|
|
|
|
|
|
|
|
import json |
|
|
|
|
|
import tempfile |
|
|
|
|
|
|
|
|
|
|
|
from hyde.plugin import Plugin |
|
|
|
|
|
from hyde.fs import File, Folder |
|
|
|
|
|
from hyde.model import Expando |
|
|
|
|
|
|
|
|
|
|
|
import sphinx |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SphinxPlugin(Plugin): |
|
|
|
|
|
"""The plugin class for rendering sphinx-generated documentation.""" |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, site): |
|
|
|
|
|
self.sphinx_build_dir = None |
|
|
|
|
|
super(SphinxPlugin, self).__init__(site) |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
def plugin_name(self): |
|
|
|
|
|
return "sphinx" |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
def settings(self): |
|
|
|
|
|
settings = Expando({}) |
|
|
|
|
|
settings.conf_path = "." |
|
|
|
|
|
settings.block_map = Expando({}) |
|
|
|
|
|
settings.block_map.body = "body" |
|
|
|
|
|
try: |
|
|
|
|
|
user_settings = getattr(self.site.config, self.plugin_name) |
|
|
|
|
|
except AttributeError: |
|
|
|
|
|
pass |
|
|
|
|
|
else: |
|
|
|
|
|
for name in dir(user_settings): |
|
|
|
|
|
if not name.startswith("_"): |
|
|
|
|
|
setattr(settings,name,getattr(user_settings,name)) |
|
|
|
|
|
return settings |
|
|
|
|
|
|
|
|
|
|
|
def begin_site(self): |
|
|
|
|
|
# Find and adjust all the resource that will be handled by sphinx. |
|
|
|
|
|
# We need to: |
|
|
|
|
|
# * change the deploy name from .rst to .html |
|
|
|
|
|
# * make sure they don't get rendered inside a default block |
|
|
|
|
|
for resource in self.site.content.walk_resources(): |
|
|
|
|
|
if resource.source_file.kind == "rst": |
|
|
|
|
|
new_name = resource.source_file.name_without_extension + ".html" |
|
|
|
|
|
target_folder = File(resource.relative_deploy_path).parent |
|
|
|
|
|
resource.relative_deploy_path = target_folder.child(new_name) |
|
|
|
|
|
resource.meta.default_block = None |
|
|
|
|
|
|
|
|
|
|
|
def begin_text_resource(self,resource,text): |
|
|
|
|
|
"""If this is a sphinx input file, replace it with the generated docs. |
|
|
|
|
|
|
|
|
|
|
|
This method will replace the text of the file with the sphinx-generated |
|
|
|
|
|
documentation, lazily running sphinx if it has not yet been called. |
|
|
|
|
|
""" |
|
|
|
|
|
if resource.source_file.kind != "rst": |
|
|
|
|
|
return text |
|
|
|
|
|
if self.sphinx_build_dir is None: |
|
|
|
|
|
self._run_sphinx() |
|
|
|
|
|
output = [] |
|
|
|
|
|
settings = self.settings |
|
|
|
|
|
for (nm,content) in self._get_sphinx_output(resource).iteritems(): |
|
|
|
|
|
try: |
|
|
|
|
|
block = getattr(settings.block_map,nm) |
|
|
|
|
|
except AttributeError: |
|
|
|
|
|
pass |
|
|
|
|
|
else: |
|
|
|
|
|
output.append("{%% block %s %%}" % (block,)) |
|
|
|
|
|
output.append(content) |
|
|
|
|
|
output.append("{% endblock %}") |
|
|
|
|
|
return "\n".join(output) |
|
|
|
|
|
|
|
|
|
|
|
def site_complete(self): |
|
|
|
|
|
if self.sphinx_build_dir is not None: |
|
|
|
|
|
self.sphinx_build_dir.delete() |
|
|
|
|
|
|
|
|
|
|
|
def _run_sphinx(self): |
|
|
|
|
|
"""Run sphinx to generate the necessary output files. |
|
|
|
|
|
|
|
|
|
|
|
This method creates a temporary directory, copies all sphinx-related |
|
|
|
|
|
files into it, and then runs the sphinx build process to produce |
|
|
|
|
|
the documentation fragments. |
|
|
|
|
|
""" |
|
|
|
|
|
self.sphinx_build_dir = Folder(tempfile.mkdtemp()) |
|
|
|
|
|
# Copy all sphinx files into a temporary build directory. |
|
|
|
|
|
self.sphinx_input_dir = self.sphinx_build_dir.child_folder("input") |
|
|
|
|
|
self.sphinx_input_dir.make() |
|
|
|
|
|
for resource in self.site.content.walk_resources(): |
|
|
|
|
|
if resource.source_file.kind == "rst": |
|
|
|
|
|
relpath = resource.relative_path |
|
|
|
|
|
build_file = File(self.sphinx_input_dir.child(relpath)) |
|
|
|
|
|
build_file.parent.make() |
|
|
|
|
|
resource.source_file.copy_to(build_file) |
|
|
|
|
|
# Run sphinx to generate output in the output directory. |
|
|
|
|
|
self.sphinx_output_dir = self.sphinx_build_dir.child_folder("output") |
|
|
|
|
|
self.sphinx_output_dir.make() |
|
|
|
|
|
conf_path = self.site.sitepath.child_folder(self.settings.conf_path) |
|
|
|
|
|
sphinx_args = ["sphinx-build"] |
|
|
|
|
|
sphinx_args.extend([ |
|
|
|
|
|
"-b", "json", |
|
|
|
|
|
"-c", conf_path.path, |
|
|
|
|
|
self.sphinx_input_dir.path, |
|
|
|
|
|
self.sphinx_output_dir.path |
|
|
|
|
|
]) |
|
|
|
|
|
if sphinx.main(sphinx_args) != 0: |
|
|
|
|
|
raise RuntimeError("sphinx build failed") |
|
|
|
|
|
|
|
|
|
|
|
def _get_sphinx_output(self,resource): |
|
|
|
|
|
relpath = File(resource.relative_path) |
|
|
|
|
|
relpath = relpath.parent.child(relpath.name_without_extension+".fjson") |
|
|
|
|
|
with open(self.sphinx_output_dir.child(relpath),"rb") as f: |
|
|
|
|
|
return json.load(f) |
|
|
|
|
|
|