| @@ -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) | |||