Browse Source

add some sanity-checks to the sphinx plugin

main
Ryan Kelly 13 years ago
parent
commit
d905d57381
1 changed files with 104 additions and 15 deletions
  1. +104
    -15
      hyde/ext/plugins/sphinx.py

+ 104
- 15
hyde/ext/plugins/sphinx.py View File

@@ -6,8 +6,12 @@ This plugin lets you easily include sphinx-generated documentation as part
of your Hyde site.
"""

# We need absolute import so that we can import the main "sphinx"
# module even though this module is also called "sphinx". Ugh.
from __future__ import absolute_import

import os
import sys
import json
import tempfile

@@ -19,24 +23,35 @@ import sphinx
from sphinx.builders.html import JSONHTMLBuilder
from sphinx.util.osutil import SEP

from hyde.util import getLoggerWithNullHandler
logger = getLoggerWithNullHandler('hyde.ext.plugins.sphinx')



class SphinxPlugin(Plugin):
"""The plugin class for rendering sphinx-generated documentation."""

def __init__(self, site):
self.sphinx_build_dir = None
self._sphinx_config = None
super(SphinxPlugin, self).__init__(site)

@property
def plugin_name(self):
"""The name of the plugin, obivously."""
return "sphinx"

@property
def settings(self):
"""Settings for this plugin.

This property combines default settings with those specified in the
site config to produce the final settings for this plugin.
"""
settings = Expando({})
settings.sanity_check = True
settings.conf_path = "."
settings.block_map = Expando({})
settings.block_map.body = "body"
settings.block_map = {}
try:
user_settings = getattr(self.site.config, self.plugin_name)
except AttributeError:
@@ -47,17 +62,44 @@ class SphinxPlugin(Plugin):
setattr(settings,name,getattr(user_settings,name))
return settings

@property
def sphinx_config(self):
"""Configuration options for sphinx.

This is a lazily-generated property giving the options from the
sphinx configuration file. It's generated by actualy executing
the config file, so don't do anything silly in there.
"""
if self._sphinx_config is None:
conf_path = self.settings.conf_path
conf_path = self.site.sitepath.child_folder(conf_path)
# Sphinx always execs the config file in its parent dir.
conf_file = conf_path.child("conf.py")
self._sphinx_config = {"__file__":conf_file}
curdir = os.getcwd()
os.chdir(conf_path.path)
try:
execfile(conf_file,self._sphinx_config)
finally:
os.chdir(curdir)
return self._sphinx_config

def begin_site(self):
settings = self.settings
if settings.sanity_check:
self._sanity_check()
# 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
# * if a block_map is given, switch off default_block
suffix = self.sphinx_config.get("source_suffix",".rst")
for resource in self.site.content.walk_resources():
if resource.source_file.kind == "rst":
if resource.source_file.path.endswith(suffix):
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
if settings.block_map:
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.
@@ -65,33 +107,80 @@ class SphinxPlugin(Plugin):
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":
suffix = self.sphinx_config.get("source_suffix",".rst")
if not resource.source_file.path.endswith(suffix):
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 %}")
sphinx_output = self._get_sphinx_output(resource)
# If they're set up a block_map, use the specific blocks.
# Otherwise, output just the body for use by default_block.
if not settings.block_map:
output.append(sphinx_output["body"])
else:
for (nm,content) in sphinx_output.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 _sanity_check(self):
"""Check the current site for sanity.

This method checks that the site is propertly set up for building
things with sphinx, e.g. it has a config file, a master document,
the hyde sphinx extension is enabled, and so-on.
"""
# Check that the sphinx config file actually exists.
try:
sphinx_config = self.sphinx_config
except EnvironmentError:
logger.error("Could not read the sphinx config file.")
conf_path = self.settings.conf_path
conf_path = self.site.sitepath.child_folder(conf_path)
conf_file = conf_path.child("conf.py")
logger.error("Please ensure %s is a valid sphinx config",conf_file)
logger.error("or set sphinx.conf_path to the directory")
logger.error("containing your sphinx conf.py")
raise
# Check that the hyde_json extension is loaded
extensions = sphinx_config.get("extensions",[])
if "hyde.ext.plugins.sphinx" not in extensions:
logger.error("The hyde_json sphinx extension is not configured.")
logger.error("Please add 'hyde.ext.plugins.sphinx' to the list")
logger.error("of extensions in your sphinx conf.py file.")
logger.info("(set sphinx.sanity_check=false to disable this check)")
raise RuntimeError("sphinx is not configured correctly")
# Check that the master doc exists in the source tree.
master_doc = sphinx_config.get("master_doc","index")
master_doc += sphinx_config.get("source_suffix",".rst")
master_doc = os.path.join(self.site.content.path,master_doc)
if not os.path.exists(master_doc):
logger.error("The sphinx master document doesn't exist.")
logger.error("Please create the file %s",master_doc)
logger.error("or change the 'master_doc' setting in your")
logger.error("sphinx conf.py file.")
logger.info("(set sphinx.sanity_check=false to disable this check)")
raise RuntimeError("sphinx is not configured correctly")

def _run_sphinx(self):
"""Run sphinx to generate the necessary output files.

This method creates a temporary directory for sphinx's output, then
run sphinx against the Hyde input directory.
"""
logger.info("running sphinx")
self.sphinx_build_dir = Folder(tempfile.mkdtemp())
conf_path = self.site.sitepath.child_folder(self.settings.conf_path)
sphinx_args = ["sphinx-build"]


Loading…
Cancel
Save