| @@ -3,7 +3,7 @@ | |||||
| Sphinx plugin. | Sphinx plugin. | ||||
| This plugin lets you easily include sphinx-generated documentation as part | This plugin lets you easily include sphinx-generated documentation as part | ||||
| of your Hyde site. | |||||
| of your Hyde site. It is simultaneously a Hyde plugin and a Sphinx plugin. | |||||
| """ | """ | ||||
| # We need absolute import so that we can import the main "sphinx" | # We need absolute import so that we can import the main "sphinx" | ||||
| @@ -18,6 +18,7 @@ import tempfile | |||||
| from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
| from hyde.fs import File, Folder | from hyde.fs import File, Folder | ||||
| from hyde.model import Expando | from hyde.model import Expando | ||||
| from hyde.ext.plugins.meta import MetaPlugin as _MetaPlugin | |||||
| import sphinx | import sphinx | ||||
| from sphinx.builders.html import JSONHTMLBuilder | from sphinx.builders.html import JSONHTMLBuilder | ||||
| @@ -85,6 +86,12 @@ class SphinxPlugin(Plugin): | |||||
| return self._sphinx_config | return self._sphinx_config | ||||
| def begin_site(self): | def begin_site(self): | ||||
| """Event hook for when site processing begins. | |||||
| This hook checks that the site is correctly configured for building | |||||
| with sphinx, and adjusts any sphinx-controlled resources so that | |||||
| hyde will process them correctly. | |||||
| """ | |||||
| settings = self.settings | settings = self.settings | ||||
| if settings.sanity_check: | if settings.sanity_check: | ||||
| self._sanity_check() | self._sanity_check() | ||||
| @@ -102,10 +109,14 @@ class SphinxPlugin(Plugin): | |||||
| resource.meta.default_block = None | resource.meta.default_block = None | ||||
| def begin_text_resource(self,resource,text): | def begin_text_resource(self,resource,text): | ||||
| """If this is a sphinx input file, replace it with the generated docs. | |||||
| """Event hook for processing an individual resource. | |||||
| If the input resource is a sphinx input file, this method will replace | |||||
| replace the text of the file with the sphinx-generated documentation. | |||||
| This method will replace the text of the file with the sphinx-generated | |||||
| documentation, lazily running sphinx if it has not yet been called. | |||||
| Sphinx itself is run lazily the first time this method is called. | |||||
| This means that if no sphinx-related resources need updating, then | |||||
| we entirely avoid running sphinx. | |||||
| """ | """ | ||||
| suffix = self.sphinx_config.get("source_suffix",".rst") | suffix = self.sphinx_config.get("source_suffix",".rst") | ||||
| if not resource.source_file.path.endswith(suffix): | if not resource.source_file.path.endswith(suffix): | ||||
| @@ -132,6 +143,10 @@ class SphinxPlugin(Plugin): | |||||
| return "\n".join(output) | return "\n".join(output) | ||||
| def site_complete(self): | def site_complete(self): | ||||
| """Event hook for when site processing ends. | |||||
| This simply cleans up any temorary build file. | |||||
| """ | |||||
| if self.sphinx_build_dir is not None: | if self.sphinx_build_dir is not None: | ||||
| self.sphinx_build_dir.delete() | self.sphinx_build_dir.delete() | ||||
| @@ -173,6 +188,19 @@ class SphinxPlugin(Plugin): | |||||
| logger.error("sphinx conf.py file.") | logger.error("sphinx conf.py file.") | ||||
| logger.info("(set sphinx.sanity_check=false to disable this check)") | logger.info("(set sphinx.sanity_check=false to disable this check)") | ||||
| raise RuntimeError("sphinx is not configured correctly") | raise RuntimeError("sphinx is not configured correctly") | ||||
| # Check that I am *before* the other plugins, | |||||
| # with the possible exception of MetaPlugin | |||||
| for plugin in self.site.plugins: | |||||
| if plugin is self: | |||||
| break | |||||
| if not isinstance(plugin,_MetaPlugin): | |||||
| logger.error("The sphinx plugin is installed after the") | |||||
| logger.error("plugin %r.",plugin.__class__.__name__) | |||||
| logger.error("It's quite likely that this will break things.") | |||||
| logger.error("Please move the sphinx plugin to the top") | |||||
| logger.error("of the plugins list.") | |||||
| logger.info("(sphinx.sanity_check=false to disable this check)") | |||||
| raise RuntimeError("sphinx is not configured correctly") | |||||
| def _run_sphinx(self): | def _run_sphinx(self): | ||||
| """Run sphinx to generate the necessary output files. | """Run sphinx to generate the necessary output files. | ||||
| @@ -194,6 +222,13 @@ class SphinxPlugin(Plugin): | |||||
| raise RuntimeError("sphinx build failed") | raise RuntimeError("sphinx build failed") | ||||
| def _get_sphinx_output(self,resource): | def _get_sphinx_output(self,resource): | ||||
| """Get the sphinx output for a given resource. | |||||
| This returns a dict mapping block names to HTML text fragments. | |||||
| The most important fragment is "body" which contains the main text | |||||
| of the document. The other fragments are for things like navigation, | |||||
| related pages and so-on. | |||||
| """ | |||||
| relpath = File(resource.relative_path) | relpath = File(resource.relative_path) | ||||
| relpath = relpath.parent.child(relpath.name_without_extension+".fjson") | relpath = relpath.parent.child(relpath.name_without_extension+".fjson") | ||||
| with open(self.sphinx_build_dir.child(relpath),"rb") as f: | with open(self.sphinx_build_dir.child(relpath),"rb") as f: | ||||
| @@ -202,12 +237,26 @@ class SphinxPlugin(Plugin): | |||||
| class HydeJSONHTMLBuilder(JSONHTMLBuilder): | class HydeJSONHTMLBuilder(JSONHTMLBuilder): | ||||
| """A slightly-customised JSONHTMLBuilder, for use by Hyde. | |||||
| This is a Sphinx builder that serilises the generated HTML fragments into | |||||
| a JSON docuent, so they can be later retrieved and dealt with at will. | |||||
| The only customistion we do over the standard JSONHTMLBuilder is to | |||||
| reference documents with a .html suffix, so that internal link will | |||||
| work correctly once things have been processed by Hyde. | |||||
| """ | |||||
| name = "hyde_json" | name = "hyde_json" | ||||
| def get_target_uri(self, docname, typ=None): | def get_target_uri(self, docname, typ=None): | ||||
| return docname + ".html" | return docname + ".html" | ||||
| def setup(app): | def setup(app): | ||||
| """Sphinx plugin setup function. | |||||
| This function allows the module to act as a Sphinx plugin as well as a | |||||
| Hyde plugin. It simply registers the HydeJSONHTMLBuilder class. | |||||
| """ | |||||
| app.add_builder(HydeJSONHTMLBuilder) | app.add_builder(HydeJSONHTMLBuilder) | ||||